Pārlūkot izejas kodu

Merge pull request #478 from timoschwarzer/android-iap-3.2.2

Update Android IAP demo for 3.2.2
Aaron Franke 5 gadi atpakaļ
vecāks
revīzija
9600d93a33

+ 0 - 138
misc/android_iap/iap.gd

@@ -1,138 +0,0 @@
-extends Node
-
-signal purchase_success(item_name)
-signal purchase_fail
-signal purchase_cancel
-signal purchase_owned(item_name)
-
-signal has_purchased(item_name)
-
-signal consume_success(item_name)
-signal consume_fail
-signal consume_not_required
-
-signal sku_details_complete
-signal sku_details_error
-
-var payment
-
-func _ready():
-	if Engine.has_singleton("GodotPayments"):
-		payment = Engine.get_singleton("GodotPayments")
-	else:
-		print("GodotPayment singleton is only available on Android devices.")
-
-	if payment:
-		# Set callback with this script instance.
-		payment.setPurchaseCallbackId(get_instance_id())
-
-# Set consume purchased item automatically after purchase, default value is true.
-func set_auto_consume(auto):
-	if payment:
-		payment.setAutoConsume(auto)
-
-
-# Request user owned item, callback: has_purchased.
-func request_purchased():
-	if payment:
-		payment.requestPurchased()
-
-func has_purchased(_receipt, _signature, sku):
-	if sku == "":
-		print("has_purchased : nothing")
-		emit_signal("has_purchased", null)
-	else:
-		print("has_purchased : ", sku)
-		emit_signal("has_purchased", sku)
-
-
-# purchase item
-# callback : purchase_success, purchase_fail, purchase_cancel, purchase_owned
-func purchase(item_name):
-	if payment:
-		# transaction_id could be any string that used for validation internally in java
-		payment.purchase(item_name, "transaction_id")
-
-
-func purchase_success(_receipt, _signature, sku):
-	print("purchase_success : ", sku)
-	emit_signal("purchase_success", sku)
-
-
-func purchase_fail():
-	print("purchase_fail")
-	emit_signal("purchase_fail")
-
-
-func purchase_cancel():
-	print("purchase_cancel")
-	emit_signal("purchase_cancel")
-
-
-func purchase_owned(sku):
-	print("purchase_owned : ", sku)
-	emit_signal("purchase_owned", sku)
-
-
-# Consume purchased item.
-# Callback: consume_success, consume_fail
-func consume(item_name):
-	if payment:
-		payment.consume(item_name)
-
-
-# Consume all purchased items.
-func consume_all():
-	if payment:
-		payment.consumeUnconsumedPurchases()
-
-
-func consume_success(_receipt, _signature, sku):
-	print("consume_success : ", sku)
-	emit_signal("consume_success", sku)
-
-
-# If consume fails, need to call request_purchased() to get purchase token from Google.
-# Then try to consume again.
-func consume_fail():
-	emit_signal("consume_fail")
-
-
-# No purchased item to consume.
-func consume_not_required():
-	emit_signal("consume_not_required")
-
-
-# Detail info of IAP items:
-# sku_details = {
-#     product_id (String) : {
-#         type (String),
-#         product_id (String),
-#         title (String),
-#         description (String),
-#         price (String),  # this can be used to display price for each country with their own currency
-#         price_currency_code (String),
-#         price_amount (float)
-#     },
-#     ...
-# }
-var sku_details = {}
-
-# Query for details of IAP items.
-# Callback: sku_details_complete
-func sku_details_query(list):
-	if payment:
-		var sku_list = PoolStringArray(list)
-		payment.querySkuDetails(sku_list)
-
-
-func sku_details_complete(result):
-	print("sku_details_complete : ", result)
-	for key in result.keys():
-		sku_details[key] = result[key]
-	emit_signal("sku_details_complete")
-
-
-func sku_details_error(error_message):
-	print("error_sku_details = ", error_message)
-	emit_signal("sku_details_error")

+ 88 - 49
misc/android_iap/iap_demo.gd

@@ -1,78 +1,117 @@
 extends Control
 
-onready var alert = get_node("alert")
+const TEST_ITEM_SKU = "my_in_app_purchase_sku"
+
+onready var alert_dialog = $AlertDialog
+onready var label = $Label
+
+var payment = null
+var test_item_purchase_token = null
 
 func _ready():
-	iap.set_auto_consume(false)
-	iap.connect("purchase_success", self, "on_purchase_success")
-	iap.connect("purchase_fail", self, "on_purchase_fail")
-	iap.connect("purchase_cancel", self, "on_purchase_cancel")
-	iap.connect("purchase_owned", self, "on_purchase_owned")
-	iap.connect("has_purchased", self, "on_has_purchased")
-	iap.connect("consume_success", self, "on_consume_success")
-	iap.connect("consume_fail", self, "on_consume_fail")
-	iap.connect("sku_details_complete", self, "on_sku_details_complete")
+	if Engine.has_singleton("GodotPayment"):
+		label.text += "\n\n\nTest item SKU: %s" % TEST_ITEM_SKU
+
+		payment = Engine.get_singleton("GodotPayment")
+		payment.connect("connected", self, "_on_connected") # No params
+		payment.connect("disconnected", self, "_on_disconnected") # No params
+		payment.connect("connect_error", self, "_on_connect_error") # Response ID (int), Debug message (string)
+		payment.connect("purchases_updated", self, "_on_purchases_updated") # Purchases (Dictionary[])
+		payment.connect("purchase_error", self, "_on_purchase_error") # Response ID (int), Debug message (string)
+		payment.connect("sku_details_query_completed", self, "_on_sku_details_query_completed") # SKUs (Dictionary[])
+		payment.connect("sku_details_query_error", self, "_on_sku_details_query_error") # Response ID (int), Debug message (string), Queried SKUs (string[])
+		payment.connect("purchase_acknowledged", self, "_on_purchase_acknowledged") # Purchase token (string)
+		payment.connect("purchase_acknowledgement_error", self, "_on_purchase_acknowledgement_error") # Response ID (int), Debug message (string), Purchase token (string)
+		payment.connect("purchase_consumed", self, "_on_purchase_consumed") # Purchase token (string)
+		payment.connect("purchase_consumption_error", self, "_on_purchase_consumption_error") # Response ID (int), Debug message (string), Purchase token (string)
+		payment.startConnection()
+	else:
+		show_alert("Android IAP support is not enabled. Make sure you have enabled 'Custom Build' and the GodotPayment plugin in your Android export settings! This application will not work.")
 
-	get_node("purchase").connect("pressed", self, "button_purchase")
-	get_node("consume").connect("pressed", self, "button_consume")
-	get_node("request").connect("pressed", self, "button_request")
-	get_node("query").connect("pressed", self, "button_query")
 
+func show_alert(text):
+	alert_dialog.dialog_text = text
+	alert_dialog.popup_centered()
 
-func on_purchase_success(item_name):
-	alert.set_text("Purchase success : " + item_name)
-	alert.popup()
 
+func _on_connected():
+	print("PurchaseManager connected")
 
-func on_purchase_fail():
-	alert.set_text("Purchase fail")
-	alert.popup()
+	# We must acknowledge all puchases.
+	# See https://developer.android.com/google/play/billing/integrate#process for more information
+	var query = payment.queryPurchases("inapp") # Use "subs" for subscriptions
+	var purchase_token = null
+	if query.status == OK:
+		for purchase in query.purchases:
+			if !purchase.is_acknowledged:
+				print("Purchase " + str(purchase.sku) + " has not been acknowledged. Acknowledging...")
+				payment.acknowledgePurchase(purchase.purchase_token)
+	else:
+		print("Purchase query failed: %d" % query.status)
 
 
-func on_purchase_cancel():
-	alert.set_text("Purchase cancel")
-	alert.popup()
+func _on_sku_details_query_completed(sku_details):
+	for available_sku in sku_details:
+		show_alert(to_json(available_sku))
 
 
-func on_purchase_owned(item_name):
-	alert.set_text("Purchase owned: " + item_name)
-	alert.popup()
+func _on_purchases_updated(purchases):
+	print("Purchases updated: %s" % to_json(purchases))
 
+	# See _on_connected
+	for purchase in purchases:
+		if !purchase.is_acknowledged:
+			print("Purchase " + str(purchase.sku) + " has not been acknowledged. Acknowledging...")
+			payment.acknowledgePurchase(purchase.purchase_token)
+
+	if purchases.size() > 0:
+		test_item_purchase_token = purchases[purchases.size() - 1].purchase_token
+
+
+func _on_purchase_acknowledged(purchase_token):
+	print("Purchase acknowledged: %s" % purchase_token)
+
+
+func _on_purchase_consumed(purchase_token):
+	show_alert("Purchase consumed successfully: %s" % purchase_token)
+
+
+func _on_purchase_error(code, message):
+	show_alert("Purchase error %d: %s" % [code, message])
 
-func on_has_purchased(item_name):
-	if item_name == null:
-		alert.set_text("Don't have purchased item")
-	else:
-		alert.set_text("Has purchased: " + item_name)
-	alert.popup()
 
+func _on_purchase_acknowledgement_error(code, message):
+	show_alert("Purchase acknowledgement error %d: %s" % [code, message])
 
-func on_consume_success(item_name):
-	alert.set_text("Consume success: " + item_name)
-	alert.popup()
 
+func _on_purchase_consumption_error(code, message):
+	show_alert("Purchase consumption error %d: %s" % [code, message])
 
-func on_consume_fail():
-	alert.set_text("Try to request purchased first")
-	alert.popup()
 
+func _on_sku_details_query_error(code, message):
+	show_alert("SKU details query error %d: %s" % [code, message])
 
-func on_sku_details_complete():
-	alert.set_text("Got detail info: " + to_json(iap.sku_details["item_test_a"]))
-	alert.popup()
 
+func _on_disconnected():
+	show_alert("GodotPayment disconnected. Will try to reconnect in 10s...")
+	yield(get_tree().create_timer(10), "timeout")
+	payment.startConnection()
 
-func button_purchase():
-	iap.purchase("item_tess")
 
+# GUI
+func _on_QuerySkuDetailsButton_pressed():
+	payment.querySkuDetails([TEST_ITEM_SKU])
 
-func button_consume():
-	iap.consume("item_tess")
 
+func _on_PurchaseButton_pressed():
+	var response = payment.purchase(TEST_ITEM_SKU)
+	if response.status != OK:
+		show_alert("Purchase error %d: %s" % [response.response_code, response.debug_message])
 
-func button_request():
-	iap.request_purchased()
 
+func _on_ConsumeButton_pressed():
+	if test_item_purchase_token == null:
+		show_alert("You need to set 'test_item_purchase_token' first! (either by hand or in code)")
+		return
 
-func button_query():
-	iap.sku_details_query(["item_test_a", "item_test_b"])
+	payment.consumePurchase(test_item_purchase_token)

+ 55 - 60
misc/android_iap/main.tscn

@@ -7,9 +7,9 @@ anchor_left = 0.5
 anchor_top = 0.5
 anchor_right = 0.5
 anchor_bottom = 0.5
-margin_left = -512.0
+margin_left = -512.711
 margin_top = -300.0
-margin_right = 512.0
+margin_right = 511.289
 margin_bottom = 300.0
 size_flags_horizontal = 2
 size_flags_vertical = 2
@@ -18,71 +18,66 @@ __meta__ = {
 "_edit_use_anchors_": false
 }
 
-[node name="purchase" type="Button" parent="."]
-margin_left = 40.0
-margin_top = 40.0
-margin_right = 250.0
-margin_bottom = 120.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
-text = "Purchase in app"
-
-[node name="consume" type="Button" parent="."]
-margin_left = 40.0
-margin_top = 150.0
-margin_right = 250.0
-margin_bottom = 230.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
-text = "Consume in app"
-
-[node name="request" type="Button" parent="."]
-margin_left = 40.0
-margin_top = 260.0
-margin_right = 250.0
-margin_bottom = 340.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
-text = "Request purchased"
-
-[node name="query" type="Button" parent="."]
-margin_left = 40.0
-margin_top = 370.0
-margin_right = 250.0
-margin_bottom = 450.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
-text = "Query in app items"
-
-[node name="alert" type="AcceptDialog" parent="."]
-margin_left = 290.0
-margin_top = 60.0
-margin_right = 700.0
-margin_bottom = 290.0
-size_flags_horizontal = 2
-size_flags_vertical = 2
+[node name="AlertDialog" type="AcceptDialog" parent="."]
+anchor_left = 0.5
+anchor_top = 0.5
+anchor_right = 0.5
+anchor_bottom = 0.5
+margin_left = 64.0
+margin_top = 64.0
+margin_right = -64.0
+margin_bottom = -64.0
+grow_horizontal = 2
+grow_vertical = 2
+rect_min_size = Vector2( 400, 0 )
+size_flags_vertical = 4
+popup_exclusive = true
+dialog_autowrap = true
 
 [node name="Label" type="Label" parent="."]
 margin_left = 300.0
 margin_top = 40.0
-margin_right = 932.0
-margin_bottom = 207.0
+margin_right = 996.0
+margin_bottom = 156.0
 size_flags_horizontal = 2
 size_flags_vertical = 0
-text = "\"iap\" is located in Autoloads. See Project > Project Settings > AutoLoad
+text = "To test in-app purchase on android device,
 
-To enable IAP module
+1. Make sure you have enabled \"Custom Build\" and the GodotPayment plugin in your Android export settings
+2. Export APK and upload it as alpha or beta stage to Google Play Developer Console and publish it.
+    (It's not published to public, but you and other testers can access it.)
+3. There should be an activate in-app item. Copy its SKU into the TEST_ITEM_SKU constant in iap_demo.gd
+4. Changes you make in the Play Console may take some time before taking effect"
 
-1. Project > Project Settings
-2. write [Category : android] / [Property : modules] / [Type : String] and click Add
-3. Click \"Android\" on left panel
-4. double click on right filed of \"modules\"
-5. write \"org/godotengine/godot/GodotPaymentV3\"
+[node name="QuerySkuDetailsButton" type="Button" parent="."]
+margin_left = 40.5697
+margin_top = 39.9347
+margin_right = 221.57
+margin_bottom = 91.9347
+text = "Query SKU details"
+__meta__ = {
+"_edit_use_anchors_": false
+}
 
-To test in-app purchase on android device,
+[node name="PurchaseButton" type="Button" parent="."]
+margin_left = 40.5697
+margin_top = 101.203
+margin_right = 221.57
+margin_bottom = 153.203
+text = "Purchase"
+__meta__ = {
+"_edit_use_anchors_": false
+}
 
-1. Need to add \"com.android.vending.BILLING\" permission at Project > Export > Android > User Permissions
-2. Export APK and upload it as alpha or beta stage to Google Play Developer Console and publish it.
-    (It's not published to public, but you and tester can access it.)
-3. There should be activated in-app item
-4. Any changes on Developer console will take 2~3 hours to take effect"
+[node name="ConsumeButton" type="Button" parent="."]
+margin_left = 40.5697
+margin_top = 162.142
+margin_right = 221.57
+margin_bottom = 214.142
+text = "Consume"
+__meta__ = {
+"_edit_use_anchors_": false
+}
+[connection signal="pressed" from="QuerySkuDetailsButton" to="." method="_on_QuerySkuDetailsButton_pressed"]
+[connection signal="pressed" from="PurchaseButton" to="." method="_on_PurchaseButton_pressed"]
+[connection signal="pressed" from="ConsumeButton" to="." method="_on_ConsumeButton_pressed"]

+ 0 - 4
misc/android_iap/project.godot

@@ -23,10 +23,6 @@ config/name="Android in-app purchases"
 run/main_scene="res://main.tscn"
 config/icon="res://icon.png"
 
-[autoload]
-
-iap="*res://iap.gd"
-
 [debug]
 
 gdscript/warnings/return_value_discarded=false