(On the non-working version, obviously)
Thanks, Simon
Here what the relevant console output is …
MacBook-Pro-3:platform-tools user$ ./adb logcat Corona:v *:s
- waiting for device -
--------- beginning of system
--------- beginning of main
I/Corona ( 1388): ERROR: Runtime error
I/Corona ( 1388): ?:0: attempt to call field ‘purchase’ (a nil value)
I/Corona ( 1388): stack traceback:
I/Corona ( 1388): ?: in function ‘purchase’
I/Corona ( 1388): /Users/user/Dropbox/AppDev/Karate Diary/payment.lua:98: in function ‘actionFN’
I/Corona ( 1388): /Users/user/Dropbox/AppDev/Karate Diary/com/ponywolf/plugins/button.lua:83: in function ‘method’
I/Corona ( 1388): /Users/jenkins/slaveroot/workspace/Templates/label/android/platform/resources/init.lua:221: in function </Users/jenkins/slaveroot/workspace/Templates/label/android/platform/resources/init.lua:190>
Do you get any output after you call the init function? Or is that the entirety of the output from IAP Badger?
Sorry for the late answer. This is what I get, the debug window never shows…:
I/Corona (19417): -------------------------------------------------------------------- I/Corona (19417): IAP Badger: init I/Corona (19417): Called with: I/Corona (19417): table: 0xb4b972a0 { I/Corona (19417): [verboseDebugOutput] =\> true I/Corona (19417): [filename] =\> "inventory.txt" I/Corona (19417): [failedListener] =\> function: 0xb4b961a8 I/Corona (19417): [debugMode] =\> true I/Corona (19417): [cancelledListener] =\> function: 0xb4b961a8 I/Corona (19417): [salt] =\> "something tr1cky to gue55!" I/Corona (19417): [catalogue] =\> table: 0xb4b972a0 { I/Corona (19417): [inventoryItems] =\> table: 0xb4b97180 { I/Corona (19417): [yearlyTax] =\> table: 0xb4b97220 { I/Corona (19417): [productType] =\> "consumable" I/Corona (19417): } I/Corona (19417): } I/Corona (19417): [products] =\> table: 0xb4b97180 { I/Corona (19417): [buyYearlyTax] =\> table: 0xb4b971a0 { I/Corona (19417): [onRefund] =\> function: 0xb4b961d8 I/Corona (19417): [onPurchase] =\> function: 0xb4b961c0 I/Corona (19417): [productNames] =\> table: 0xb4b971e0 { I/Corona (19417): [apple] =\> "~~" I/Corona (19417): [google] =\> "~~" I/Corona (19417): } I/Corona (19417): [productType] =\> "consumable" I/Corona (19417): } I/Corona (19417): } I/Corona (19417): } I/Corona (19417): } I/Corona (19417): VerboseDebugOutput set to true I/Corona (19417): IAP Badger: leaving init
I/Corona (19417): IAP Badger: entering purchase I/Corona (19417): Called with productList: I/Corona (19417): buyYearlyTax I/Corona (19417): On simulator/debug mode - faking purchases I/Corona (19417): IAP Badger: leaving purchase
Are your devices standard/vanilla Google Play devices?
The weird thing is, the plug-in is tripping on line 1470… which it shouldn’t ever reach when running on an Android device. Also, your console output is missing some debug messages that should come out of iap.init function - messages saying you’re running on Android and you’re (probably) using asynchronous Google IAP v3.
All of which leads me to believe that IAP Badger isn’t able to detect that it’s running on a Google Play device.
So, in your code, just before you call iap.init(…), could you try the following:
print (system.getInfo("targetAppStore"))
and tell me what you get.
Also, I’d also be interested to see what the following gives you after you initialise IAP Badger:
print (iap\_badger.getStoreName())
Looking back, I’ve also noticed that in your Corona build dialog, you don’t have a target app store selected - this is most likely the issue.
Simon
Hi, support said I should direct you to the problem I am having here https://forums.coronalabs.com/topic/72478-iap-badger-not-working-with-sandbox-test-account/
I’ll pick this up on the other thread.
Simon
Hi there,
Just giving you notice of another bug fix - the plug-in and Github repos have been updated.
Version 16
- fixed checkProductExists bug (many thanks to bogomazon for spotting this)
Simon
Hi
I use IAP Badger in the version “The simplest possible code for handling a purchase” without any changes. So far, in Google all is working fine and the product is bought.
Question: I deinstall the application on the device and reinstall it again (or I install the App on a new phone with the same google account). If I buy my IAP again, I get from Google the message “Error Transaction failed: Unable to buy item (response: 7:Item Already Owned)” which is in my opinion correct.
The User is not able, do have restored his “owned” puchase. Is this correct or do I something wrong?
-
Easy for me would be when the IAP Badger would handle this case like a buy and call the “purchaseListener”-function.
-
Is there any possibility to handle this error message by myself ?
-
other suggestion?
Thanks for helping
Hanspeter
Hi Hanspeter,
The solution to this is give the user the option to ‘restore’ items they’ve purchased in the past. On Google Play, you should automatically run a restore when the application starts (see the iap.restore function).
Using iap.restore is very similar to iap.purchase, with only a couple of minor differences.
For more detailed information about restoring products, check out the ‘product restores’ section of this tutorial.
Sample 2 on this page also gives you sample code for making this happen.
Kind regards,
Simon
Dear Simon
Thanks a lot for helping.
I tried all afternoon to program the “restore fonction” ( using the examples), but without sucsess. I definitly don’t know what I’m doing wrong and I don’t know where to look anymore. I attached my code, maybe you see the mistake right away.
- variable IAP_Gekauft is the switch I need.
“N”= not bought --> I try to restore --> if restore not succefull = purchase
“J”= doing nothing
Without the added IAP-Restore line - iap.restore(false, restoreListener, restoreTimeout) - , purchase works fine but I get the messge “already bought”.
Thanks in advance for taking the time to look into my problem.
Best regards
Hanspeter
Hi Hanspeter,
Google definitely won’t let you buy a non-consumable product twice, which is why you get the error (correctly). In my experience, when you call this, your purchase listener still gets called anyway.
The key thing here is - if the user owns the item already, you shouldn’t give them the option of purchasing it.
So, when you’re restore listener gets the message, “the user owns this”, you should remove / disable the menu option to purchase - and unlock access to the IAP.
If you’re having difficulty getting this up and running, I’d:
- take my code sample (as mentioned above)
- insert your Google IAP identifier into the code
- build and sign using your app key (don’t forget to add your Google App’s key into config.lua) - use a version number that is identical to your last build in Google Play’s console
- run this on your device and test
Also: have you tried running your restore code on the simulator, in debug mode? This will test the flow of your logic - eliminating the difficulties of integrating with the Google Play store.
Simon
Just for people that are following this with similar problems, this has also come up in another thread, so here’s more info:
[–
… I didn’t realise you were using “android.test.purchased” as your Google Play product ID.
Your best bet for testing IAP on a real device, with a real connection to Google play, is to set up your IAP in Google Play console and use a real product identifier.
–]
Simon
Hi Simon
Thanks for your answer.
Code more or less (without spinner) = your example
When the user purchased a IAP, the program will remember it, until the user install the app for e.g. on a new phone. Thats the moment, the restore happens.
Purchase works fine in Google Play with real Credit-Card
Not working is: iap.restore( false, restoreListener, restoreTimeout )
- it doesn’t call the function restoreListener nor restoreTimeout. I don’t know why.
With Debugging I do not see any action with the iap.restore.
I attach the code. Maybe you just see the mistake or whats going wrong.
Sorry, to bother you
Thanks again, Hanspeter
[lua]
– --------------------------------------
– Antwort auf die Frage Abbrechen/Kaufen
function onKauf( event )
if ( event.action == “clicked” ) then
local i = event.index
if ( i == 1 ) then
– keine Aktion, da der Benutzer nicht kaufen will
elseif ( i == 2 ) then
IAP_Badger() – In APP KAUF Abwicckeln ***
end
end
end
– --------------
– Kauf abwickeln
function IAP_Badger()
print("**** function IAP_Badger()")
--Called when the relevant app store has completed the purchase
--Make a record of the purchase using whatever method you like
function purchaseListener( product )
IAP_Gekauft = “J”
writeparameter()
gekauft_Vorschlag_anzeigen()
end
iap.purchase(“removeAds”, purchaseListener)
end
– FUNCTION FOR RESTORE IAP
– ------------------------
– If this function is called, the app store never replied to the request
– for a restore (which probably means there were no products to restore)
function restoreTimeout()
print ("**** function restoreTimeout()")
– spinner.hide()
– Tell user something went wrong
ShowAlert = native.showAlert(“Restore failed”, “No response from App Store”, {“Okay”})
end
– This function is called on a successful restore.
– If event.firstRestoreCallback is set to true, then this is the first time the function
– has been called.
function restoreListener(productNames, event)
print ("**** function restoreListener(productName, event)")
– If this is the first transaction…
if (event.firstRestoreCallback) then
– Tell user purchases have been restored
ShowAlert = native.showAlert(“Restore”, “Your items are being restored”, {“Okay”})
end
--Remove ads
IAP_Gekauft = “J”
writeparameter()
end
– ---------------------------------------------
– Frage ob In App Kauf ausgeführt werden soll ?
if IAP_Gekauft == “N” then
--Load IAP Badger
iap = require(“plugin.iap_badger”)
--Create the catalogue
catalogue = {
--Information about the product on the app stores
products = {
--removeAds is the product identifier.
removeAds = {
--A list of product names or identifiers specific to apple’s App Store or Google Play.
productNames = {
apple = “takeit_01”,
google = “takeit_01”,
amazon = “takeit_01”
},
--The product type
productType = “non-consumable”
}
},
--Information about how to handle the inventory item
inventoryItems = {
unlock = { productType=“non-consumable” }
}
}
--This table contains all of the options we need to specify in this example program.
local iapOptions = {
catalogue=catalogue
}
--[[
– DEBUGING MODE ***
local iapOptions = {
--The catalogue generated above
catalogue=catalogue,
--The filename in which to save the inventory
filename=“goodies.txt”,
--Salt for the hashing algorithm
salt = “something tr1cky to gue55!”,
--***New stuff below
--Debugging mode
debugMode = true,
--If on the simulator, pretend to be the following store:
debugStore = “apple”
}
--]]
--Initialise IAP badger
iap.init(iapOptions)
– Restore purchases
print("*************************1")
iap.restore( false, restoreListener, restoreTimeout )
print("*************************2")
– Ist der IAP_Gekauft immer noch “N”, dann war ein Restore unmöglich
if IAP_Gekauft == “N” then
ShowAlert = native.showAlert( K_TextTitel, K_TextLang, { K_TextAbbrechen, K_TextKaufen }, onKauf)
else
– IAP gekauft ***
gekauft_Vorschlag_anzeigen()
end
else
– IAP gekauft ***
gekauft_Vorschlag_anzeigen()
end
[/lua]
[lua]
– config.lua
application =
{
content =
{
width = 768,
height = 1024,
scale = “zoomEven”,
fps = 60,
--[[
imageSuffix =
{
["@2x"] = 2,
["@4x"] = 4,
},
--]]
},
license =
{
google =
{
key = “MIIBIjANBg xxxxxx”,
},
},
}
[/lua]
[lua]
– build settings
–
– For more information on build.settings, see the Project Build Settings guide at:
– https://docs.coronalabs.com/guide/distribution/buildSettings
settings =
{
orientation =
{
– Supported values for orientation:
– portrait, portraitUpsideDown, landscapeLeft, landscapeRight
default = “portrait”,
supported = { “portrait” },
},
–
– Android section
–
android =
{
usesPermissions =
{
“android.permission.INTERNET”,
“com.android.vending.BILLING”,
},
},
–
– iOS section
–
iphone =
{
xcassets = “Images.xcassets”,
plist =
{
UIStatusBarHidden = false,
UILaunchStoryboardName = “LaunchScreen”,
NSMotionUsageDescription = “This app would like to access the accelerometer.”,
},
},
–
– Plugins section
–
plugins =
{
[“plugin.google.iap.v3”] =
{
publisherId = “com.coronalabs”,
supportedPlatforms = { android = true },
},
[“plugin.googleAnalytics”] =
{
publisherId = “com.coronalabs”
},
[“plugin.iap_badger”] =
{
publisherId = “uk.co.happymongoose”,
},
--Amazon IAP
[“plugin.amazon.iap”] =
{
publisherId = “com.coronalabs”,
supportedPlatforms = { [“android-kindle”]=true }
},
},
–
– Project section
–
excludeFiles =
{
– Exclude unnecessary files for each platform
all = { “Icon.png”, “Icon-*dpi.png”, “Images.xcassets”, },
android = { “LaunchScreen.storyboardc”, },
},
}
[/lua]
Dir Simon
No hurry, we are aproximately one week without any internet connection.
Thanks again.
Best regards Hanspeter
When you get your internet back, could you set verboseDebugOutput to true when you call iap.init (as described here).
Then hook up your device via USB, run adb logcat (Google to see how to use this) and try a restore.
Capture the (relevant output), post it here and I’ll take a look at it.
Simon
Dear Simon
Many thanks for the answer, you are really generous.
I found the error thanks to the Verbose debugging. I had overlooked that the program continues to run after the iap.restore and that the result of iap.restore arrives sometimes later. I rearanged the code after the “restore” into the “restore listener” and everything works fine. After I realised my silly mistake, it is very easy
Thanks again for the help and the availability of your fantastic IAP Badger library.
Best regards. Haaspeter
Glad it’s working for you - thanks for the kind words about the plug-in
Simon
For those of you new (or struggling) with IAP, I’ve simplified the example code given the documentation for IAP Badger.
I’ve removed lots of referencing UI elements to try to make the code as simple / followable / copy-and-pastable as possible.
Simon