Hello,
I already posted in the “general question/ discussion” section but I thought maybe it was not the best place for my question about IAP.
So, here’s my problem. I tried to implement IAP using IAP badger. Everything works fine in debug mode but as soon as I remove the debug mode, I get some problems.
In fact, the window for purchasing opens twice and thus when I recieve, from google play, the message which says that the purchase was successeful, I get the other message “Error! You have already purchased this item”.
Here is my code:
local composer = require( "composer" ) local scene = composer.newScene() local widget = require( "widget" ) local myData = require("myData") local json = require( "json" ) local iap = require("plugin.iap\_badger") function scene:create( event ) local sceneGroup = self.view composer.removeScene( "classicmode" ) -- Add the other windows later print( "\nmainmenu: create event" ) end function scene:show( event ) local sceneGroup = self.view print( "mainmenu: show event" ) local background = display.newImageRect("Main menu screen.png", 1080, 1920) background.x = 540 ; background.y = 960 sceneGroup:insert( background ) local title = display.newImage ("Title.png") title.x = 650; title.y = 350 sceneGroup:insert( title ) local titleCoin = display.newImage ("Title coin.png") titleCoin.x = 125; titleCoin.y = 350 sceneGroup:insert( titleCoin ) local mySoundEffect = audio.loadSound("Sound effect.mp3") local spinner=nil --Create the catalogue local catalogue = { --Information about the product on the app stores products = { --removeAds is the product identifier. --Always use this identifier to talk to IAP Badger about the purchase. fullGameNoAds = { --A list of product names or identifiers specific to apple's App Store or Google Play. productNames = { apple="goldrush.fullgame.4774.5689", google="goldrush.fullgame.4774.5689", amazon="goldrush.fullgame.4774.5689"}, --The product type productType = "non-consumable", --This function is called when a purchase is complete. onPurchase=function() iap.setInventoryValue("unlock", true) end, --The function is called when a refund is made onRefund=function() iap.removeFromInventory("unlock", true) end, } }, --Information about how to handle the inventory item inventoryItems = { unlock = { productType="non-consumable" } } } --Called when any purchase fails local function failedListener() --If the spinner is on screen, remove it if (spinner) then spinner:removeSelf() spinner=nil end end --This table contains all of the options we need to specify in this example program. local iapOptions = { --The catalogue generated above catalogue=catalogue, --The filename in which to save the inventory filename="example1.txt", --Salt for the hashing algorithm salt = "something tr1cky to gue55!", --Listeners for failed and cancelled transactions will just remove the spinner from the screen failedListener=failedListener, cancelledListener=failedListener, --Once the product has been purchased, it will remain in the inventory. Uncomment the following line --to test the purchase functions again in future. It's also useful for testing restore purchases. --doNotLoadInventory=true, --debugMode=true, } --Initialise IAP badger iap.init(iapOptions) --------------------------------- -- -- Making purchases -- --------------------------------- --The functionality for removing the ads from the screen has been put in a separate --function because it will be called from the purchaseListener and the restoreListener --functions local function fullGameNoAds() --Remove the advertisement (need to check it's there first - if this function --is called from a product restore, it may not have been created) print("okForTransaction1") myData.transition.ok = "true" loadsave.saveTable( myData.transition, "transition.json", system.DocumentsDirectory ) composer.removeScene("mainmenu") composer.gotoScene("mainmenu") end --Called when the relevant app store has completed the purchase local function purchaseListener(product ) --Remove the spinner spinner:removeSelf() spinner=nil --Remove the ads fullGameNoAds() --Save the inventory change iap.saveInventory() --Give the user a message saying the purchase was successful native.showAlert("Info", "Your purchase was successful", {"Okay"}) print("okForTransaction2") end --Purchase function --Most of the code in this function places a spinner on screen to prevent any further user interaction with --the screen. The actual code to initiate the purchase is the single line iap.purchase("removeAds"...) buyUnlock=function() --Place a progress spinner on screen and tell the user the app is contating the store local spinnerBackground = display.newRect(960,960,1920,1920) spinnerBackground:setFillColor(1,1,1,0.75) --Spinner consumes all taps so the user cannot tap the purchase button twice spinnerBackground:addEventListener("tap", function() return true end) local spinnerText = display.newText("Contacting " .. iap.getStoreName() .. "...", 540,700, native.systemFont, 80) spinnerText:setFillColor(0,0,0) --Add a little spinning rectangle local spinnerRect = display.newRect(540,960,100,100) spinnerRect:setFillColor(0.25,0.25,0.25) transition.to(spinnerRect, { time=4000, rotation=360, iterations=999999, transition=easing.inOutQuad}) --Create a group and add all these objects to it spinner=display.newGroup() spinner:insert(spinnerBackground) spinner:insert(spinnerText) spinner:insert(spinnerRect) --Tell IAP to initiate a purchase iap.purchase("fullGameNoAds", purchaseListener) print("okForTransaction0") end local function restoreListener(productName, event) --If this is the first product to be restored, remove the spinner --(Not really necessary in a one-product app, but I'll leave this as template --code for those of you writing apps with multi-products). if (event.firstRestoreCallback) then --Remove the spinner from the screen spinner:removeSelf() spinner=nil --Tell the user their items are being restore native.showAlert("Restore", "Your items are being restored", {"Okay"}) end --Remove the ads if (productName=="goldrush.fullgame.4774.5689") then fullGameNoAds() end --Save any inventory changes iap.saveInventory() end --Restore function --Most of the code in this function places a spinner on screen to prevent any further user interaction with --the screen. The actual code to initiate the purchase is the single line iap.restore(false, ...) local function restorePurchases() --Place a progress spinner on screen and tell the user the app is contating the store local spinnerBackground = display.newRect(960,960,1920,1920) spinnerBackground:setFillColor(1,1,1,0.75) --Spinner consumes all taps so the user cannot tap the purchase button twice spinnerBackground:addEventListener("tap", function() return true end) local spinnerText = display.newText("Contacting " .. iap.getStoreName() .. "...", 540,700, native.systemFont, 80) spinnerText:setFillColor(0,0,0) --Add a little spinning rectangle local spinnerRect = display.newRect(540,960,100,100) spinnerRect:setFillColor(0.25,0.25,0.25) transition.to(spinnerRect, { time=4000, rotation=360, iterations=999999, transition=easing.inOutQuad}) --Create a group and add all these objects to it spinner=display.newGroup() spinner:insert(spinnerBackground) spinner:insert(spinnerText) spinner:insert(spinnerRect) --Tell IAP to initiate a purchase --Use the failedListener from onPurchase, which just clears away the spinner from the screen. --You could have a separate function that tells the user "Unable to contact the app store" or --similar on a timeout. --On the simulator, or in debug mode, this function attempts to restore all of the non-consumable --items in the catalogue. iap.restore(false, restoreListener, failedListener) end local function handleButton2( event ) if ( "ended" == event.phase ) then composer.gotoScene ("Defies menu") end end local function handlePurchase( event ) if ( "ended" == event.phase ) then print("ok") buyUnlock() end end if myData.transition.ok == "true" then --Otherwise add a tap listener to the button that unlocks the game print("ok1") print(iap.getInventoryValue("unlock")) print("ok2") local defiesBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies button.png", overFile = "Defies button pressed.png", onRelease = handleButton2 } defiesBtn.x = 540; defiesBtn.y = 1050 sceneGroup:insert( defiesBtn ) else print("ok1") print(iap.getInventoryValue("unlock")) print("ok2") local defiesLockedBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies locked.png", overFile = "Defies locked pressed.png", onRelease = handlePurchase } defiesLockedBtn.x = 540; defiesLockedBtn.y = 1050 sceneGroup:insert( defiesLockedBtn ) end end function scene:hide() if btnAnim then transition.cancel( btnAnim ); end print( "mainmenu: hide event" ) end -- Called prior to the removal of scene's "view" (display group) function scene:destroy( event ) print( "((destroying mainmenu's view))" ) end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "create" event is dispatched if scene's view does not exist scene:addEventListener( "create", scene ) -- "show" event is dispatched whenever scene transition has finished scene:addEventListener( "show", scene ) -- "hide" event is dispatched before next scene's transition begins scene:addEventListener( "hide", scene ) -- "destroy" event is dispatched before view is unloaded, which can be scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene
Here are the messages I got in the command promt when I press the button to purchase:
I/Corona (25226): ok
I/Corona (25226): okForTransaction0
Thanks in advance for your replies as I really don’t know how to fix this issue…