transactionCallBack not working for Android

So i’m trying to implement IAP for the first time in my app. I have 3 items that can be purchased. Basically I have a menu screen with buttons to go to different scenes. I have made 3 of them with an alpha of 0 and used Rob’s loadsave.lua module to save 3 values to a table. Everytime you go to the menu scene it loads the table and checks those values to see if any of the buttons should be made visible.

On the store scene I have 3 buy buttons that coorospond to 3 products. A successful purchase should change the table to a paid value so next time you load the menu that button should be visible. And vice versa if a refund happens. I’ve tried using both the static response (android.test.purchased) and a test account with a draft APK uploaded.

Both are having the same issue. When you press the button to buy, the android UI pops up, I go through the short process of buying it and get the Purchase Successful UI. But the table is not being updated properly, so the buttons are staying invisible and the natice.showAlert is not firing.

I have checked and tested my coding for using the loadsave.lua module and everything is perfect. The only problem I can see is the that the tstate events in the transactionCallBack function are NOT working. Here is the relevant parts of the code:

local function transactionCallback( event ) print("In transactionCallback", event.transaction.state) local transaction = event.transaction local tstate = event.transaction.state local product = event.transaction.productIdentifier if tstate == "purchased" then print("Transaction succuessful!") if whichOne[1] == product then storeSettings.sinePaid = true elseif whichOne[2] == product then storeSettings.speedPaid = true elseif whichOne[3] == product then storeSettings.boltPaid = true end loadsave.saveTable(storeSettings, "store.json") native.showAlert("Success", "Function is now unlocked!", {"Okay"}) store.finishTransaction( transaction ) elseif tstate == "restored" then print("Transaction restored (from previous session)") if whichOne[1] == product then storeSettings.sinePaid = true end if whichOne[2] == product then storeSettings.speedPaid = true end if whichOne[3] == product then storeSettings.boltPaid = true end loadsave.saveTable(storeSettings, "store.json") store.finishTransaction( transaction ) elseif tstate == "refunded" then print("User requested a refund -- locking app back") if whichOne[1] == product then storeSettings.sinePaid = false elseif whichOne[2] == product then storeSettings.speedPaid = false elseif whichOne[3] == product then storeSettings.boltPaid = false end loadsave.saveTable(storeSettings, "store.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 local function purchase( event ) if event.phase == "ended" then if event.target.num == 1 then print("Purchase Sine") store.purchase({"android.test.purchased"}) elseif event.target.num == 2 then print("Purchase Speed") store.purchase({"android.test.refunded"}) elseif event.target.num == 3 then print("Purchase Bolt") store.purchase({"android.test.canceled"}) end end end local function restorePurchases( event ) if event.phase == "ended" then print("Restore all purchases") store.restore() end end function scene:createScene( event ) local screenGroup = self.view Runtime:addEventListener( "key", onKeyEvent ) storeSettings = loadsave.loadTable("store.json") if store.availableStores.apple then timer.performWithDelay(1000, function() store.init( "apple", transactionCallback); end) end if store.availableStores.google then timer.performWithDelay( 1000, function() store.init( "google", transactionCallback ); end) end whichOne = {} whichOne[1] = "android.test.purchased" whichOne[2] = "android.test.refunded" whichOne[3] = "android.test.canceled" sineBuy = widget.newButton { id = "sineBuy", width = 125, label = "Buy", labelColor = { default = {255, 255, 255}, over = {19, 124, 21}}, font = "BerlinSansFB-Reg", fontSize = 16, defaultFile = "Images/buyOver.png", overFile = "Images/buy.png", onRelease = purchase, } sineBuy.num = 1 sineGroup:insert(sineBuy) sineBuy.x = display.actualContentHeight-85 sineBuy.y = 110 speedBuy = widget.newButton { id = "speedBuy", width = 125, label = "Buy", labelColor = { default = {255, 255, 255}, over = {19, 124, 21}}, font = "BerlinSansFB-Reg", fontSize = 16, defaultFile = "Images/buyOver.png", overFile = "Images/buy.png", onRelease = purchase, } speedBuy.num = 2 speedGroup:insert(speedBuy) speedBuy.x = display.actualContentHeight-85 speedBuy.y = 170 boltBuy = widget.newButton { id = "boltBuy", width = 125, label = "Buy", labelColor = { default = {255, 255, 255}, over = {19, 124, 21}}, font = "BerlinSansFB-Reg", fontSize = 16, defaultFile = "Images/buyOver.png", overFile = "Images/buy.png", onRelease = purchase, } boltBuy.num = 3 boltGroup:insert(boltBuy) boltBuy.x = display.actualContentHeight-85 boltBuy.y = 230 restoreButt = widget.newButton { id = "restoreButt", width = 125, label = "Restore", labelColor = { default = {255,255,255}, over = {198,68,68}}, font = "BerlinSansFB-Reg", fontSize = 20, defaultFile = "Images/restoreButtOver.png", overFile = "Images/restoreButt.png", onRelease = restorePurchases, } restoreButt.num = 3 restoreButt.pressed = false screenGroup:insert(restoreButt) restoreButt.x = display.actualContentHeight-85 restoreButt.y = 290

And on the menu page:

function scene:createScene( event ) local screenGroup = self.view storeSettings = loadsave.loadTable("store.json") if (loadsave.loadTable("store.json") == nil) then storeSettings = {} storeSettings.sinePaid = false storeSettings.speedPaid = true --set to true atm to test refunds storeSettings.boltPaid = false loadsave.saveTable(storeSettings, "store.json") end if storeSettings.sinePaid then sineButt.alpha = 1 else sineButt.alpha = 0 end if storeSettings.speedPaid then speedButt.alpha = 1 else speedButt.alpha = 0 end if storeSettings.boltPaid then boltButt.alpha = 1 else boltButt.alpha = 0 end 

I can’t for the life of me figure out what is not working :frowning:

An update, I added native.showAlerts to all the possible tstate lines and the transactionCallBack IS getting called, but it’s going straight to “Failed” with a “Invalid Client” error.

What does a “Invalid Client” error mean when I’m using Android’s static (android.test.purchased) productID’s? I have searched the forums and I see other people getting this error, but always when testing with a tester account and a in draft APK. I’m getting this error just using the Android Static Product IDs…

it can happen if Google Play isn’t setup.  If your google play can’t login.  If you’re logged into to your developer account and not a test account (Google does not allow a developer to buy from themselves even if its a test object).

I’d start with those

Rob

Hi Rob, I did manage to get it working with a draft APK and a test account after waiting 6 hours after uploading. The main reason I thought something was wrong, before waiting on Google to acknowledge the new APK file, was that their android.test.purchased productIDs give me the invalid client error.

From what I have read you do not need to have a APK file uploaded, thus no wait time, when using their static productIDs to test. In fact, I still get the error using their static IDs even though it now works using my own IDs and the draft APK… weird.

An update, I added native.showAlerts to all the possible tstate lines and the transactionCallBack IS getting called, but it’s going straight to “Failed” with a “Invalid Client” error.

What does a “Invalid Client” error mean when I’m using Android’s static (android.test.purchased) productID’s? I have searched the forums and I see other people getting this error, but always when testing with a tester account and a in draft APK. I’m getting this error just using the Android Static Product IDs…

it can happen if Google Play isn’t setup.  If your google play can’t login.  If you’re logged into to your developer account and not a test account (Google does not allow a developer to buy from themselves even if its a test object).

I’d start with those

Rob

Hi Rob, I did manage to get it working with a draft APK and a test account after waiting 6 hours after uploading. The main reason I thought something was wrong, before waiting on Google to acknowledge the new APK file, was that their android.test.purchased productIDs give me the invalid client error.

From what I have read you do not need to have a APK file uploaded, thus no wait time, when using their static productIDs to test. In fact, I still get the error using their static IDs even though it now works using my own IDs and the draft APK… weird.