Hello everyone.
I was previously using version 5.2.1 of the Google Play Billing library, but then Google told me that I should use a higher version because Google does not accept this version.
I found a working version, version 7.0.0, but I did not adapt the codes according to myself from the experiments I made and that is why I could not make the purchase correctly.
Store.gd is the scene with my purchase button and the purchase process, GooglePlay.gd is the code for my old version and Example.gd is the code for the link I will use.
How can I adapt these new codes to my own store scene?
I would appreciate your help.
Link: Release V 1.2 for Godot 4.3 · code-with-max/godot-google-play-iapp · GitHub
Store.gd:
extends Node
@onready var ui_control = preload("res://Scripts/UIControl.gd").new()
@onready var store_panel = $Store/StorePanel
@onready var buyButtons = {
"one_offer" : $Store/BuyButton,
}
var activated_purchase_window = {
0 : false,
}
const package_names = {
0 : "one_offer",
}
func purchase_package(product_id: String):
match product_id:
package_names[0]:
Player.coin = ui_control.coin_increase(Player.coin, 2000, "coin")
print("package " + package_names[0] + " is purchased!")
func in_app_purchase(product_id: String):
if OS.get_name() == "Android":
$Store/google_play.buy(product_id, "consumable")
func reset_purchase_state():
for i in range(0):
activated_purchase_window[i] = false
func open_purchase_window():
store_panel.show()
func close_purchase_window():
store_panel.hide()
func get_prices(product_ids, product_prices):
for i in buyButtons.size():
buyButtons[product_ids[i]].text = product_prices[i]
buyButtons[product_ids[i]].disabled = false
func _on_one_offer_button_pressed():
activated_purchase_window[0] = true
open_purchase_window()
func _on_store_no_button_pressed():
close_purchase_window()
reset_purchase_state()
func _on_store_yes_button_pressed():
for i in range(0):
if activated_purchase_window[i] == true:
in_app_purchase(package_names[i])
close_purchase_window()
reset_purchase_state()
func _on_google_play_bought_product(product_id):
purchase_package(product_id)
func _on_google_play_products_ready(product_ids, product_prices):
print("in app products are ready!")
get_prices(product_ids, product_prices)
GooglePlay.gd:
extends Node
#Note 1: This code can only function on Android.
#Note 2: The purchase overlay will only show if you uploaded the app on Google Play, at least internaly.
@export_category("Products")
@export_multiline var product_ids = "product1,product2"
var payment
var recent_product_id
var recent_purchase_type
signal products_ready(product_ids, product_prices)
signal bought_product(product_id: String)
func _ready():
if Engine.has_singleton("GodotGooglePlayBilling"): # Testing if you enabled the plugin
payment = Engine.get_singleton("GodotGooglePlayBilling") # Adds the payment system to the variable
payment.connected.connect(_on_connected) # Fired when sucessfully connected
payment.connect_error.connect(_on_connect_error) # Fired when failed to connect, RETURNS: Error ID (int), Error Message (string)
payment.sku_details_query_completed.connect(_on_product_details_query_completed) # Fired when successfully prepared the products for possible purchases, RETURNS: Products (Dictionary[])
payment.sku_details_query_error.connect(_on_product_details_query_error) # Fired when failed to prepare the products for possible purchases, RETURNS: Error ID (int), Error Message (string), Product Token (string[])
payment.purchases_updated.connect(_on_purchases_updated) # Fired when successfully purchased, RETURNS: Purchases (Dictionary[])
payment.purchase_error.connect(_on_purchase_error) # Fired when failed to purchase, RETURNS: Error ID (int), Error Message (string)
payment.purchase_consumed.connect(_on_purchase_consumed) # Fired when successfully consumed the purchase, RETURNS: Purchase Token (string)
payment.purchase_consumption_error.connect(_on_purchase_consumption_error) # Fired when failed to consume the purchase, RETURNS: Error ID (int), Error Message (string), Purchase Token (string)
payment.purchase_acknowledged.connect(_on_purchase_acknowledged) # Fired when Google successfully acknowledged the purchase, RETURNS: Purchase Token (string)
payment.purchase_acknowledgement_error.connect(_on_purchase_acknowledgement_error) # Fired when Google failed to acknowledge the purchase, RETURNS: Error ID (int), Error Message (string), Purchase Token (string)
payment.query_purchases_response.connect(_on_query_purchases_response)
payment.startConnection() # This tries to connect
func _on_connected():
print("Successfully connected")
payment.querySkuDetails(product_ids.split(","), "inapp") # This prepares the products for possible purchases
payment.queryPurchases("inapp") # This gets all the purchases that you have done (if any)
func _on_query_purchases_response(query_result):
if query_result.status == OK:
for purchase in query_result.purchases:
print(purchase)
func _on_connect_error(_errorid, _errormessage):
print("Failed to connect")
func _on_product_details_query_completed(_products):
print("Successfully prepared the products")
var product_ids = []
var product_prices = []
for _product in _products:
product_ids.append(_product["sku"])
product_prices.append(_product["price"])
emit_signal("products_ready", product_ids, product_prices)
func _on_product_details_query_error(_errorid, _errormessage, _producttoken):
print("Failed to prepare the product")
#BUY FUNCTION
func buy(product_id, purchase_type):
recent_product_id = product_id
recent_purchase_type = purchase_type
payment.purchase(product_id) # This purchases the item
func _on_purchases_updated(purchases):
if recent_purchase_type == "consumable":
if purchases.size() > 0:
# This consumes the purchase by getting the recent purchase's token
payment.consumePurchase(purchases[purchases.size() - 1].purchase_token)
elif recent_purchase_type == "one-time":
for purchase in purchases: # This goes over all purchases
if not purchase.is_acknowledged:
payment.acknowledgePurchase(purchase.purchase_token) # This acknowledges the purchase
emit_signal("bought_product", recent_product_id)
print("Successfully purchased")
func _on_purchase_error(_errorid, _errormessage):
print("Failed to purchase")
#CONSUMABLE
func _on_purchase_consumed(_purchasetoken):
print("Successfully consumed the purchase")
func _on_purchase_consumption_error(_errorid, _errormessage, _purchasetoken):
print("Failed to consume the purchase")
#ONE-TIME
func _on_purchase_acknowledged(_purchasetoken):
print("Successfully acknowledged the purchase")
func _on_purchase_acknowledgement_error(_errorid, _errormessage, _purchasetoken):
print("Failed to acknowledge the purchase")
Example.gd:
# AndroidIAPP is a plugin for the Godot game engine.
# It provides an interface to work with Google Play Billing Library version 7.
# The plugin supports all public functions of the library, passes all error codes, and can work with different subscription plans.
# https://developer.android.com/google/play/billing
#
# You can use this plugin with any node in Godot.
# But, I recommend adding this script as a singleton (autoload).
# This makes it easier to access and use its functions from anywhere in your project.
#
# An example of working with a plugin:
extends Node
# https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState
enum purchaseState {
UNSPECIFIED_STATE = 0,
PURCHASED = 1,
PENDING = 2,
}
# https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode
enum billingResponseCode {
SERVICE_TIMEOUT = -3,
FEATURE_NOT_SUPPORTED = -2,
SERVICE_DISCONNECTED = -1,
OK = 0,
USER_CANCELED = 1,
SERVICE_UNAVAILABLE = 2,
BILLING_UNAVAILABLE = 3,
ITEM_UNAVAILABLE = 4,
DEVELOPER_ERROR = 5,
ERROR = 6,
ITEM_ALREADY_OWNED = 7,
ITEM_NOT_OWNED = 8,
NETWORK_ERROR = 12
}
const ITEM_CONSUMATED: Array = [
"additional_life_v1",
]
const ITEM_ACKNOWLEDGED: Array = [
"red_skin_v1",
"blue_skin_v1",
"yellow_skin_v1",
]
const SUBSCRIPTIONS: Array = [
"remove_ads_sub_01",
"test_iapp_v7",
]
var billing = null
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
await get_tree().create_timer(1).timeout
run_iapp_billing()
func run_iapp_billing():
if Engine.has_singleton("AndroidIAPP"):
# Get the singleton instance of AndroidIAPP
billing = Engine.get_singleton("AndroidIAPP")
print("AndroidIAPP singleton loaded")
# Connection information
# Handle the response from the helloResponse signal
billing.helloResponse.connect(_on_hello_response)
# Handle the startConnection signal
billing.startConnection.connect(_on_start_connection)
# Handle the connected signal
billing.connected.connect(_on_connected)
# Handle the disconnected signal
billing.disconnected.connect(_on_disconnected)
# Querying purchases
# Handle the response from the query_purchases signal
billing.query_purchases.connect(_on_query_purchases)
# Handle the query_purchases_error signal
billing.query_purchases_error.connect(_on_query_purchases_error)
# Querying products details
# Handle the response from the query_product_details signal
billing.query_product_details.connect(query_product_details)
# Handle the query_product_details_error signal
billing.query_product_details_error.connect(_on_query_product_details_error)
# Purchase processing
# Handle the purchase signal
billing.purchase.connect(_on_purchase)
# Handle the purchase_error signal
billing.purchase_error.connect(_on_purchase_error)
# Purchase updating
# Handle the purchase_updated signal
billing.purchase_updated.connect(_on_purchase_updated)
# Handle the purchase_cancelled signal
billing.purchase_cancelled.connect(_on_purchase_cancelled)
# Handle the purchase_update_error signal
billing.purchase_update_error.connect(_on_purchase_update_error)
# Purchase consuming
# Handle the purchase_consumed signal
billing.purchase_consumed.connect(_on_purchase_consumed)
# Handle the purchase_consumed_error signal
billing.purchase_consumed_error.connect(_on_purchase_consumed_error)
# Purchase acknowledging
# Handle the purchase_acknowledged signal
billing.purchase_acknowledged.connect(_on_purchase_acknowledged)
# Handle the purchase_acknowledged_error signal
billing.purchase_acknowledged_error.connect(_on_purchase_acknowledged_error)
# Connection
billing.startConnection()
else:
printerr("AndroidIAPP singleton not found")
func _on_start_connection() -> void:
print("Billing: start connection")
func _on_connected() -> void:
print("Billing successfully connected")
await get_tree().create_timer(0.4).timeout
if billing.isReady():
# billing.sayHello("Hello from Godot Google IAPP plugin :)")
# Show products available to buy
# https://developer.android.com/google/play/billing/integrate#show-products
billing.queryProductDetails(ITEM_ACKNOWLEDGED, "inapp")
billing.queryProductDetails(ITEM_CONSUMATED, "inapp")
billing.queryProductDetails(SUBSCRIPTIONS, "subs")
# Handling purchases made outside your app
# https://developer.android.com/google/play/billing/integrate#ooap
billing.queryPurchases("subs")
billing.queryPurchases("inapp")
func _on_disconnected() -> void:
print("Billing disconnected")
func _on_hello_response(response) -> void:
print("Hello signal response: " + response)
func query_product_details(response) -> void:
for i in response["product_details_list"].size():
var product = response["product_details_list"][i]
print(JSON.stringify(product["product_id"], " "))
#
# Handle avaible for purchase product details here
#
func _on_query_purchases(response) -> void:
print("on_query_Purchases_response: ")
for purchase in response["purchases_list"]:
process_purchase(purchase)
func _on_purchase_updated(response):
for purchase in response["purchases_list"]:
process_purchase(purchase)
# Processing incoming purchase
func process_purchase(purchase):
for product in purchase["products"]:
if (product in ITEM_ACKNOWLEDGED) or (product in SUBSCRIPTIONS):
# Acknowledge the purchase
if not purchase["is_acknowledged"]:
print("Acknowledging: " + purchase["purchase_token"])
billing.acknowledgePurchase(purchase["purchase_token"])
#
# Here, process the use of the product in your game.
#
else:
print("Already acknowledged")
elif product in ITEM_CONSUMATED:
# Consume the purchase
print("Consuming: " + purchase["purchase_token"])
billing.consumePurchase(purchase["purchase_token"])
#
# Here, process the use of the product in your game.
#
else:
print("Product not found: " + str(product))
# Purchase
func do_purchase(id: String, is_personalized: bool = false):
billing.purchase([id], is_personalized)
# Subscriptions
func do_subsciption(subscription_id: String, base_plan_id: String , is_personalized: bool = false):
billing.subscribe([subscription_id], [base_plan_id], is_personalized)
func print_purchases(purchases):
for purchase in purchases:
print(JSON.stringify(purchase, " "))
func _on_purchase(response) -> void:
print("Purchase started:")
print(JSON.stringify(response, " "))
func _on_purchase_cancelled(response) -> void:
print("Purchase_cancelled:")
print(JSON.stringify(response, " "))
func _on_purchase_consumed(response) -> void:
print("Purchase_consumed:")
print(JSON.stringify(response, " "))
func _on_purchase_acknowledged(response) -> void:
print("Purchase_acknowledged:")
print(JSON.stringify(response, " "))
func _on_purchase_update_error(error) -> void:
print(JSON.stringify(error, " "))
func _on_purchase_error(error) -> void:
print(JSON.stringify(error, " "))
func _on_purchase_consumed_error(error) -> void:
print(JSON.stringify(error, " "))
func _on_purchase_acknowledged_error(error) -> void:
print(JSON.stringify(error, " "))
func _on_query_purchases_error(error) -> void:
print(JSON.stringify(error, " "))
func _on_query_product_details_error(error) -> void:
print(JSON.stringify(error, " "))