NEW WARNING: Google Play Billing

@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

@famousdoggstudios check out: Google Play's IAP Auto-Refunds Big Problem - #6 by troylyndon

I had this same problem months ago, until I realized the problem was not including a delay between finish and consume - check out the last entry in this topic post for precise details on how I fixed this. Now, we rarely see refunds.

Coincidentally, I too have a delay in my code before i consume a purchase— i had written my IAP script some years back and never added a comment as to why the delay was added so this might have been the reason

However, i think this pertains to automatic refunds from google and not the scenario where users successfully request a refund to exploit the system. They usually claim refund under “accidental purchase” and get their money back as well as the in-game product/entitlement

Does the new IAP plugin also support Google Play’s new subscription features that were rolled out in May 2022?

https://www.androidpolice.com/google-i-o-2022-brought-the-biggest-changes-ever-to-google-play/

Basically the ability to handle different plans and offers?

Thanks!
Andrew

@Scott_Harrison We have a truly weird issue. We’ve made the required updates to the build.setting. My colleague, a remote developer, was able to create builds that work. However, it doesn’t seem to work for me. When I create the build and test on the test device, I see that the app receives the “initialized” event, but no other events. Also, the actual native purchase menu doesn’t show up either.

He and I have both downloaded the same code base, same Corona version (2022.3677 (2022.8.2)), we even compared the Cached plugin data.tgz that’s downloaded into Solar2DPlugins/Coches/ to be the same. We also tested the same device but with builds from his machine vs mine, and his works, but mine doesn’t.

Is there anything else that we need to make sure this plugin works? eg. SDK version, deleting old plugin from a hidden cache, etc? We are pretty much at a dead end…