Hello,
I have some trouble with implementing in-app purchases. In fact, when I try a purchase, I get access to the different steps of google playstore which finally says: “Purchase successeful”. But then, when I press “ok”, I get another message: “You already own this item”. And nothing has changed in my game…
Here is my code:
local iap = require("plugin.iap\_badger") 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="prductID", google="prductID", amazon="prductID"}, --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 print( "transaction failed", transaction.errorType, transaction.errorString ) 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) 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"}) 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) 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=="prductID") 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 if (iap.getInventoryValue("unlock")== true) then --Otherwise add a tap listener to the button that unlocks the game print(iap.getInventoryValue("unlock")) local defiesBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies button.png", overFile = "Defies button pressed.png", onEvent = handleButton2 } defiesBtn.x = 540; defiesBtn.y = 1050 sceneGroup:insert( defiesBtn ) else print(iap.getInventoryValue("unlock")) local defiesLockedBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies locked.png", overFile = "Defies locked pressed.png", onEvent = handlePurchase } defiesLockedBtn.x = 540; defiesLockedBtn.y = 1050 sceneGroup:insert( defiesLockedBtn ) 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 buyUnlock() end end local function restore( event ) if ( "ended" == event.phase ) then restorePurchases() end end local restoreButton = widget.newButton { width = 400, height = 175, defaultFile = "restore button.png", overFile = "restore button pressed.png", onEvent = restore } restoreButton.x = 540; restoreButton.y = 1600
Thanks in advance for your replies