iOS in app purchases implementation

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By TobiLa

Hey,

I am currently trying to implement in-app-purchases on iOS. But the documentation ( Services for iOS — Godot Engine (3.1) documentation in English ) seems to be lacking some infos. From various threads I gathered some infos and achieved the following simple wip helper class, which is working on Android so far:


extends Node

signal purchase_success(item_name)

enum modes {Android, iOS, Other}

var currentMode 
var payment
var timer

func _ready():
	# check for android
	if Engine.has_singleton("GodotPayments"):
		payment = Engine.get_singleton("GodotPayments")
		currentMode = modes.Android
		print("Using Android IAP")
	elif Engine.has_singleton("InAppStore"):
		payment = Engine.get_singleton("InAppStore")
		currentMode = modes.iOS
		print("Using iOS IAP")
	else:
		currentMode = modes.Other
		print("Using no IAP")

	if currentMode == modes.Android and payment:
		# set callback with this script instance
		payment.setPurchaseCallbackId(get_instance_id())
 
# purchase item
# callback : purchase_success, purchase_fail, purchase_cancel, purchase_owned
func purchase(item_name):
	if payment:
		if currentMode == modes.Android:
			# transaction_id could be any string that used for validation internally in java
			payment.purchase(item_name, "transaction_id")
		elif currentMode == modes.iOS:
			var result = payment.purchase({"product_id":  item_name})
			if result == OK:
				print('Ok, purchasing')
				_listenForSuccess()
				return true
			else:
				print("Purchase Error")

func _listenForSuccess():
	timer = Timer.new()
	timer.autostart = true
	timer.one_shot = false
	timer.wait_time = 1.0
	timer.connect("timeout", self, "check_events")
	add_child(timer)

# put this on a 1 second timer or something
func check_events():
	if currentMode == modes.iOS:
		while payment.get_pending_event_count() > 0:
			var event = payment.pop_pending_event()
			if event.type == "purchase":
				if event.result == "ok":
					emit_signal("purchase_success", event.product_id)
				timer.queue_free()

	
func purchase_success(_receipt, _signature, sku):
	print("purchase_success : ", sku)
	emit_signal("purchase_success", sku)

I also tried to replace this line:

var result = payment.purchase({"product_id":  item_name})

with this:

var result = payment.purchase({"product_id": "com.company.game." + item_name})

The doc isn’t clear about how the product id should look like.

I exported the project to xCode and uploaded it to the appstore from there. Afterwards I added the build to the internat test group and am running the app through TestFlight. When I get the the point where the function is called nothing happens.

I also don’t know how to get some logs from apps running in testflight, so that would be helpful, too.

Any hints would be really appreciated, thanks in advance!

I made my own logging with labels and found out, that the initial purchase call is successful and the event from the queue just stores the information that an error occured, nothing more.

TobiLa | 2019-10-13 13:59

Wait, which version of the product id worked?

Because mine are always said to be invalid.

Richard van der Dys | 2020-07-01 16:02

:bust_in_silhouette: Reply From: Zaramath

I can’t get the purchases to work. I have created a purchase in the App store connect with an ID and followed the commands in the tutorial, but I always get this error:

Condition “!params.has(“product_id”)” is true. Returned: ERR_INVALID_PARAMETER