Advice for beginner implementing IAP

First time implementing IAP for mobile game (Android / Google Play). Not sure where to start and how to organize everything. Searched info about non-consumables so Auto Finish Transaction = 0 and need to use iap.acknowledge() instead of iap.finish()

Things like if product is owned, equipped, price etc. and updating the corresponding gui nodes.

1 Like

Since iap.list() has limit of 20 products per request, I tried something like this to split all the product ids from constant.PRODUCT_ID_LIST to smaller tables to send.

local function retrieve_product_info()
	local index = 1
	local max_items = 20
	local id_tables = {}
	local num_tables = math.ceil(#constant.PRODUCT_ID_LIST / max_items)
	for i = 1, num_tables do
		id_tables[i] = {}
		for j = 1, max_items do
			id_tables[i][j] = constant.PRODUCT_ID_LIST[index]
			index = index + 1
		end
		iap.list(id_tables[i], product_list)
	end
end

Sometimes it works fine, sometimes only partially and sometimes error failed to fetch product.
How can I make this more reliable? Am I requesting too fast? Implement retrying system?

Not sure about the list issue, but hopefully someone else can chime in.

I can share my IAP callback code which handles a non-consumable IAP. I removed some extra stuff and wrote more comments, so I hope it’s clear. Feel free to ask questions! My starting point was the examples in the extension-iap project.

There are certainly more scalable ways of writing this if you have 20+ IAP. Instead of checking for a particular IAP ID, have a table which you can reference to see which IAP are consumable or not, and maybe have custom functions to call for each one. In my code, the commit_iap(id) method is where I apply the purchased items to the game.


local function iap_listener(self, transaction, error)
	if error == nil then
		if transaction.state == iap.TRANS_STATE_PURCHASING then
			print("Purchasing...")
		elseif transaction.state == iap.TRANS_STATE_PURCHASED then
			print("Purchased!")
			-- this is called on game startup for non-consumables.
			-- also called during purchase of consumables
			if transaction.ident == const.IAP_REMOVE_ADS then
				commit_iap(const.IAP_REMOVE_ADS)
				-- As described by the extension-iap docs, IOS needs you to finish, Android needs acknowledge
				-- I check for IOS using the system_name
				local IS_IOS = info.system_name == "iPhone OS"

				if IS_IOS then
					iap.finish(transaction)
				else
					iap.acknowledge(transaction)
				end
				-- Send message to user thanking for purchase,
				-- but only for the actual purchase event
				-- In my game, I can check the save file for a flag
				local already_have_it = settings.ads_disabled()
				if not already_have_it then
					show_messagebox(defglot.get_text("IAP_ADS_THANKS"))
				end
			end
		elseif transaction.state == iap.TRANS_STATE_UNVERIFIED then
			print("Unverified!")
		elseif transaction.state == iap.TRANS_STATE_FAILED then
			-- Send message to user if the purchase failed
			show_messagebox(defglot.get_text("IAP_FAILED"))
		elseif transaction.state == iap.TRANS_STATE_RESTORED then
			print("Restored " .. transaction.ident)
			-- If more than a few, better to have a lua table with restorable IAP IDs
			-- and check if the transaction.ident is in that list
			if (transaction.ident == const.IAP_REMOVE_ADS or transaction.ident == const.IAP_TP) then
				commit_iap(transaction.ident)
			end
		end
	else
		show_messagebox(error.error .. ": " .. error.reason)
		print(error.error .. ": " .. error.reason)
	end
end
4 Likes

How to use iap.restore() ?
Do I call this on app launch to get purchased products?

Not sure on best practice, so hopefully a few more people chime in.

Seems like it might be a good thing to call the very first time your game launches.

In my game, I don’t automatically call iap.restore(), but have a button on the store page that can trigger it. I think this button might be required on iOS, and it’s nice to have. On Android, it kind of happens automatically for non-consumable IAPs, since the transaction is never finished.

1 Like

Correct. I think Apple requires there to be an option to restore purchases.

1 Like

Is it okay to call iap.list() every time the app launches?
I noticed I’m still receiving product info even when offline, so does Google Play cache them?
Just trying to figure out if I should make store cache system so product info is not retrieved too often.

Here it says query limit is 200,000 per day.

Yes, the products are cached somewhere inside Google Play.

I believe this is the quota for the API you use when you interact with the Google Play Developer console (for programatically submitting or reading products, store listings etc).

1 Like

Okay looks like my store cache system is not needed as Google takes care of that :+1:

1 Like

How to handle slow payments?

I believe we have an open issue for slow payments. Please review this issue, do some tests of your own, and then report any problems you encounter.

Does the user have to be logged in for the iap.restore() to function properly?

Probably