IAP appears to work.... but then doesn't

Hey Y’all,

I implemented my IAP by following this tut:

https://coronalabs.com/blog/2013/09/03/tutorial-understanding-in-app-purchases/

As is,  it would not work. I had to remove all the store.availableStores.apple and store.availableStores.google references as having these in cause the app to freeze on a black screen and never do anything. OK got around that using this at the start of main.lua:

local store local googleIAP = false local isSim = false if ( system.getInfo( "platformName" ) == "Android" ) then store = require( "plugin.google.iap.v3" ) googleIAP = true elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then store = require( "store" ) else native.showAlert( "Notice", "In-app purchases are not supported in the Corona Simulator.", { "OK" } ) isSim = true end

After that i used the status of googleIAP in the if statements, and it fixed the freeze on device. yay!

New problem arose after I tapped my IAP button in one of my composer scenes. The first time, it appears to work correctly, brings up the dialog box from google play and lets do the purchase, even says it was successful. But the 'purchased part of the callback does nothing:

if tstate == "purchased" then print("Transaction succuessful!") mySettings.isPaid = true utility.saveTable(mySettings, "settings.json") native.showAlert("Thank you!", "Your support is greatly appreciated!", {"Okay"}) store.finishTransaction( transaction )

none of this actually executes, it seems to skip it completely. I am using adb to get the log from my device

so i do see " In transactionCallback purchased " but that is it. The printing of “Transaction Successful!” doesn’t happen, the save file doesn’t get updated and the dialog box does not appear.

So I tried tapping my buy button again, and of course I get " In transactionCallback failed

Transaction failed, type: 7 Unable to buy item (response: 7:Item Already Owned) "

Why is the failed state working (as according to the code from the tutorial) but not the purchased state? Also before I forget, the restored part doesn’t work either.

Just to be thorough, here are all related bits and pieces for those that want to see (truncated unrelated stuff though for brevity):

build.settings

 plugins = { ["plugin.google.iap.v3"] = { publisherId = "com.coronalabs", supportedPlatforms = { android=true } }, }, -- Android permissions androidPermissions = { "android.permission.INTERNET", "com.android.vending.BILLING", "com.android.vending.CHECK\_LICENSE", },

config.lua

 license = { google = { key = "stupidly long hash key gibberish was here", policy = "serverManaged" }, },

main.lua

local loadsave = require("lua.util.loadsave") -- \<-- same as UTILITY.LUA from example local store local googleIAP = false local isSim = false if ( system.getInfo( "platformName" ) == "Android" ) then store = require( "plugin.google.iap.v3" ) googleIAP = true elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then store = require( "store" ) else native.showAlert( "Notice", "In-app purchases are not supported in the Corona Simulator.", { "OK" } ) isSim = true end local unlockApp = {} local restoring unlockApp = loadsave.loadTable("savedammit.json") if unlockApp == nil then unlockApp = {} unlockApp.isPaid = false loadsave.saveTable(unlockApp,"savedammit.json") end function transactionCallback( event ) local transaction = event.transaction local tstate = transaction.state print( "In transactionCallback", tstate ) --local transaction = event.transaction --local tstate = transaction.state -- --Google does not return a "restored" state when you call store.restore() --You're only going to get "purchased" with Google. This is a work around --to the problem. -- --The assumption here is that any real purchase should happen reasonably --quick while restores will have a transaction date sometime in the past. --5 minutes seems sufficient to separate a purchase from a restore. -- -- if ( restoring == true ) and ( tstate == "purchased" ) then local tdate = math.floor( transaction.date /1000 ) local timeStamp = loadsave.makeTimeStamp(transaction.date,"ctime") if timeStamp + 360 \< os.time() then -- if the time stamp is older than 5 minutes, we will assume a restore. print("map this purchase to a restore") tstate = "restored" print("I think tstate is ", tstate) restoring = false end end --]] if ( tstate == "purchased" ) then print("Transaction succuessful!") unlockApp.isPaid = true loadsave.saveTable(unlockApp,"savedammit.json") native.showAlert("Thank you!", "Your support is greatly appreciated!", {"Okay"}) store.finishTransaction( transaction ) elseif ( tstate == "restored" ) then print("Transaction restored (from previous session)") unlockApp.isPaid = true loadsave.saveTable(unlockApp,"savedammit.json") store.finishTransaction( transaction ) elseif ( tstate == "refunded" ) then print("User requested a refund -- locking app back") unlockApp.isPaid = false loadsave.saveTable(unlockApp,"savedammit.json") store.finishTransaction( transaction ) elseif ( tstate == "cancelled" ) then print("User cancelled transaction") store.finishTransaction( transaction ) elseif ( tstate == "failed" ) then print("Transaction failed, type:", transaction.errorType, transaction.errorString) store.finishTransaction( transaction ) else print("unknown event") store.finishTransaction( transaction ) end print("done with store business for now") end function loadProductsListener( event ) print("In loadProductsListener") local products = event.products for i=1, #event.products do print(event.products[i].title) print(event.products[i].description) print(event.products[i].localizedPrice) print(event.products[i].productIdentifier) end for i=1, #event.invalidProducts do print(event.invalidProducts[i]) end end if isSim == false then if googleIAP == false then print("start apple store") timer.performWithDelay(1000, function() store.init( "apple", transactionCallback); end) end if googleIAP == true then print("start google store") timer.performWithDelay( 1000, function() store.init( "google", transactionCallback ); restoring = true; store.restore(); end ) end end --]] -- Start up Composer local composer = require( "composer" ) composer.gotoScene( "lua.base.splash" )

then in the scene where the purchase is attempted:

-- NEAR THE TOP OF THE FILE -- local store local googleIAP = false local isSim = false if ( system.getInfo( "platformName" ) == "Android" ) then store = require( "plugin.google.iap.v3" ) googleIAP = true elseif ( system.getInfo( "platformName" ) == "iPhone OS" ) then store = require( "store" ) else native.showAlert( "Notice", "In-app purchases are not supported in the Corona Simulator.", { "OK" } ) isSim = true end -- OTHER STUFF HERE UNTIL -- function unlockFull( event ) local self = event.target if event.phase == "began" then print("clicked the button to buy") display.getCurrentStage():setFocus( self ) self.isFocus = true elseif event.phase == "ended" then display.getCurrentStage():setFocus( nil ) self.isFocus = false if isSim == true then print("Sim no support IAP");return true; end if not store.isActive then print("store not active");return true; end if system.getInfo("targetAppStore") == "google" then store.purchase( "android.test.purchased" ) -- \< for test would normally be my IAP ID STRING else store.purchase( { "IAP ID STRING FOR IOS GOES HERE" } ) end end return true end -- REST OF SCENE ETC FOLLOWS --

So does anybody know why this is not working? Help would be appreciated, I am going spare!

Dave

QUICK EDIT: Using Corona Version 2015.2666 (2015.6.29)

Hi @puredave72,

My guess is that this is scope issue, and the fact that you’ve require()-d the store library/plugin in both modules. So, Lua doesn’t know what reference you’re attempting to make, and when. You should make one common reference to the store that is “visible” throughout all Composer scenes, either as a pseudo-global variable (not true global, as those should be avoided), or you could assign the store handle to a Composer variable via “composer:setVariable()”.

Basically, do your store setup in “main.lua” and make the handle known/visible to other scenes, so it can be called from them.

Best regards,

Brent

@Brent

Okie Dokie shall try that out, I thought the setup in main.lua had some special voodoo, as the guide didn’t mentioned anything about the scope. 

Anyway I changed the local store stuff from the composer scene to using my ‘global’ table. (local my = require("lua.base.mydata) <-- thats what i call it)  and after store was picked set my.store = store. This didn’t really fix the issue, it turns out the bane of my woes was due to the timestamp code, it doesn’t work on my device for some reason. I removed it and now it all works.

Odd

Hi @puredave72,

My guess is that this is scope issue, and the fact that you’ve require()-d the store library/plugin in both modules. So, Lua doesn’t know what reference you’re attempting to make, and when. You should make one common reference to the store that is “visible” throughout all Composer scenes, either as a pseudo-global variable (not true global, as those should be avoided), or you could assign the store handle to a Composer variable via “composer:setVariable()”.

Basically, do your store setup in “main.lua” and make the handle known/visible to other scenes, so it can be called from them.

Best regards,

Brent

@Brent

Okie Dokie shall try that out, I thought the setup in main.lua had some special voodoo, as the guide didn’t mentioned anything about the scope. 

Anyway I changed the local store stuff from the composer scene to using my ‘global’ table. (local my = require("lua.base.mydata) <-- thats what i call it)  and after store was picked set my.store = store. This didn’t really fix the issue, it turns out the bane of my woes was due to the timestamp code, it doesn’t work on my device for some reason. I removed it and now it all works.

Odd