NEW WARNING: Google Play Billing

Just pushed out a new version that should fix the price and other information missing from the products.

Can all those who experienced this problem with v2 please test Scott’s new update to verify its working?

Good news/bad news:

The good news is that the app doesn’t crash right away and appears to be loading localized pricing. (I’ll need to get a build out to some of my overseas play testers to be sure.) Purchasing also works as expected.

The bad news is that restore purchases is failing. I get the following error message out of IAP Bagder:

ERROR: iap badger.storeTransactionCallback: unable to find product '' in a product for the google store.

So, according to the documentation, store.restore will call the storeTransaction listener once for each item in the product list. It looks like some part of the product list table that IAP Badger is looking for is still missing- judging by the logcat output, it’s falling down while trying to convert the store product ID to the catalog product name.

@Scott_Harrison Any ideas? I’m elbow-deep in IAP Badger right now, but I don’t really know what’s happening behind the scenes on the plugin side.

Is this is an IAP Badger-specific problem, or are people who have written their own IAP modules also not able to restore purchases?

I am not able to get Restore working in my implementation of the Billing V2 plugin either.

Problem seems to be that the entitlements are seemingly “consumed” automatically even if I am not calling the consume function for these specific SKUs. This means that I am able to buy the entitlements as many times as I want and as expected, there is no response (no PURCHASE or ERROR) when attempting to restore.

I have also again built with the old plugin and it works correctly-- I simply skip the Consume call for entitlements and this ensures that I am unable to purchase them again. On attempting to restore, the PURCHASE state is called on these entitlements as expected.

Also wanted to point out that I’m not getting any REFUND callbacks on the new plugin but I’ll continue to test further.

Consume/Restore is definitely broken. Localised prices are now working.

@famousdoggstudios and others, good work! @Scott_Harrison Ideally, you can fix everything so that it works identically to the Billing (non-v2) version. Otherwise, I’m sure many developers that use 1) IAP Badger andor 2) their own IAP code will find themselves unable to create updates, without headaches, come Google’s deadline in just over 2 months.

I am working on it, I am aware of the deadline and don’t need the extra pressure :slight_smile:

5 Likes

@Scott_Harrison Sorry if my words made you feel any pressure. Your best efforts are always enough for me.

@colinmorgan I fixed the restore issue yesterday and been testing with in app badger restore via the example code here . I am still getting that error message but the restore is working based on the demo they have. I am glancing at the source code and it look like they are expecting a param that is not there.

Will be running more tests tomorrow

2 Likes

Does this also mean that non-consumable items are working correctly now?

EDIT: @Scott_Harrison were your changes made available into the plugin? I cleared cache, built again to test but still no luck with non-consumables and Restore. The items that I am not consuming still become available for repeated purchase and restore appears to do nothing.

@famousdoggstudios I am getting restore events from non-consumables.

I feel like this thread is getting “hijacked” and disorganized with a few different issues. If you are having a “new”(that no one else a submitted on the page) issue please submit it here on the GitHub issues page and will get it looked at as soon as possible.

^P.S including sample code will help me solve issues much quicker and saves me a lot of time :slight_smile:

Have opened an issue there. Formatting is off on Github due to my irregular commenting in code so reproducing the code flow below.

As I have posted previously, my only way of handling non-consumables for Android was to not call the consume function and this till date works flawlessly on the old plugin. I have looked at the documentation for the new plugin as well and there doesn’t seem to be any change in this respect but I’d appreciate if you’d point out anything that seems off.

  1. Purchase
function IAP.purchase(productIdentifier)
	if (checkProhibitedApp())then
		toast.showToast(textResource.prohibitedAppText)
		--fire an event if a prohibited app was found
		analytics.sendTrackingEvent("BlockedProhibitedApp")
		return
	end

	store.purchase(productIdentifier)
end
  1. Transaction listener. Of interest here is the call to a third function called performProductionAction in the PURCHASED state clause
function transactionListenerAndroid(event)

	local transaction = event.transaction

   -- Google IAP initialization event
    if ( event.name == "init" ) then
        if not ( transaction.isError ) then
            -- Perform steps to enable IAP, load products, etc.
 			if(store.canLoadProducts)then
 				--populate a local table of just product IDs and call the loadProducts function
 				local ids={}
 				for i=1,#productIdentifiers do
 					ids[#ids+1]=productIdentifiers[i].id
 				end
 				store.loadProducts(ids,productListener)
 			end	
        else  -- Unsuccessful initialization; output error details
            debugStmt.print("IAP:"..transaction.errorType)
            debugStmt.print("IAP:"..transaction.errorString)
        end

    -- Store transaction event
    elseif ( event.name == "storeTransaction" ) then
        if not ( transaction.state == "failed" ) then  -- Successful transaction
 			if ( transaction.state == "purchased") then
 				toast.showToast("IAP: puchased "..transaction.productIdentifier)
	            -- Handle a normal purchase or restored purchase here
	            performProductAction(transaction.productIdentifier)
	            store.finishTransaction( transaction )--this will end the transaction, if not called the transaction will remain incomplete and continue on next app launch
	        elseif(transaction.state=="consumed")then
	        	
	        elseif(transaction.state=="refunded")then
	        	
	        elseif ( transaction.state == "cancelled" ) then
	            -- Handle a cancelled transaction here
	 			debugStmt.print("IAP:"..transaction.state)
	        end
        else  -- Unsuccessful transaction; output error details
			
			-- if transaction fails with error 7 it means user has already owned the product. In case if the product is non-consumeable it is fine. But we need to make the consumable product to be purchaseable again.
			-- to do this we need to consume all the consumable products. This will make them purchaseable again.
			if (transaction.errorType == 7) then
				--consume all consumable products because sometimes they become non-consumable if an error occured and the product was failed to be consumed.
				--We will iterate over all porducts from the product identifier table and consume all the products based on their ID which have a TRUE flage for their consumable status
 				for i=1,#productIdentifiers do
 					if(productIdentifiers[i].isConsumable)then
 						store.consumePurchase(productIdentifiers[i].id)
 					end
 				end
			end

			toast.showToast("IAP: error "..transaction.errorType)
			toast.showToast("IAP: error "..transaction.errorString)

            debugStmt.print("IAP failed type:"..transaction.errorType )
            debugStmt.print("IAP failed string:"..transaction.errorString )
        end
    end
end
  1. In the performProductAction function, I call consume if the product was a consumable and also perform other app-specific tasks to grant the reward/entitlement to user. You’d notice that I am not calling consume on items like noAds etc.
function performProductAction(identifier)
	if(identifier=="com.famousdoggstudios.pg.noads")then
		analytics.sendTrackingEvent("noAdsPurchased")
		toast.showToast(textResource.pleaseRestart)
		preferenceHandler.set("isNoAdsPurchased",true)
	elseif(identifier=="com.famousdoggstudios.pg.currency1" and not isPurchaseRestoreInProgress)then--this item will not be purchasable during the restoration process. This is due to an issue with Google Billing plugin. see top of script where the variable is defined for details. 
		if(targetAppStore=="google")then--only required for android
			-- timer to wait sometime after the purchase call before consuming the product
			timerService.addTimer(100, function() store.consumePurchase(identifier)  
											debugStmt.print("IAP: consumed currency1")
										end, nil, true) -- exempt from service so that timer doesn't get paused.

   		end
		analytics.sendTrackingEvent("currency1Purchased")
		toast.showToast(textResource.purchaseGreeting)
		preferenceHandler.set("currency",preferenceHandler.get("currency")+4000)
	elseif(identifier=="com.famousdoggstudios.pg.currency2" and not isPurchaseRestoreInProgress)then--this item will not be purchasable during the restoration process.
		if(targetAppStore=="google")then--only required for android
			-- timer to wait sometime after the purchase call before consuming the product
			timerService.addTimer(100, function() store.consumePurchase(identifier)  
										debugStmt.print("IAP: consumed currency2")
									end, nil, true) -- exempt from service so that timer doesn't get paused.

		end
		analytics.sendTrackingEvent("currency2Purchased")
		toast.showToast(textResource.purchaseGreeting)
		preferenceHandler.set("currency",preferenceHandler.get("currency")+9000)	
	elseif(identifier=="com.famousdoggstudios.pg.noadshalf")then
		analytics.sendTrackingEvent("noAdsHalfPurchased")
		toast.showToast(textResource.pleaseRestart)
		preferenceHandler.set("isNoAdsPurchased",true)
   	end
end
1 Like

@famousdoggstudios thank you for the code, I don’t see your issue

Thanks for pointing out. I’ll submit again shortly from another account. It seems there’s some error with the account i posted from.

EDIT- opened an issue there from another account

I can confirm that the problem with non-consumables being consumed is now fixed based on my most recent testing. I have so far tested populating the details of items after retrieving them from Google Play and making purchases for consumable and non-consumable items, restore etc… all of which seem to work correctly.

@Scott_Harrison thanks for your efforts on resolving the issue with consuming of purchases.

As regards the multiple calls for consumable items that I had reported earlier, it only seems to currently happen in live builds but in regular signed builds, I haven’t noticed this yet.

1 Like

Good to hear the consume stuff is working again.

@Scott_Harrison Did you manage to find out how to retrieve the subscription trial period stuff with the product info?

Yes should be in products.originalJson

1 Like

Hi @Scott_Harrison After all of your work and this communication with developers who are testing here, is the newly updated Google Billing V2 plugin interchangeable with the previous Google Billing plugin? And if so, is there any specific testing of one or more functions remaining?

I believe it should be interchangeable, I have seen people handle IAP in different ways but like I said previously, people should do their own testing with their different setups to make sure everything works for them.

1 Like

I did a successful build yesterday with the latest version of .v2 using my copy of IAP Badger. The only things I changed were my build.settings file and adding .v2 at the end of my require call in IAP Badger itself.
Localized pricing seemed to be working as expected, purchases worked and restores worked. I only played around with it for 5 minutes or so on one device, but everything seems to be in order.

3 Likes

I dont mean for this to go too far off topic but I was wondering how developers here are handling refunds? I do not seem to get any callbacks in my code if a user has refunded an item was curious if there’s something I’m missing

Quite a few users are exploiting this for a couple of our apps