Problems with in-app purchases

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

And, when I try to restore, the app exists…

local defiesBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies button.png", overFile = "Defies button pressed.png", onEvent = handleButton2

See where you’re enabling onEvent? That options will cause “handleButton2” to be called twice, once on press and once on release. This mimics “touch” behavior. You have to test for event.phase == “began” or "ended in your function or it gets called twice.

The easiest fix in your case is to not use “onEvent” but use “onRelease” instead. Then your handler functions will be called just once without you have to change the functions.

Rob

Hello,

Thanks for your reply.

I just made a test with the changes and it is still not working… I get the exact same errors…

Do you think it has something to do with my code?

And, I forgot to tell you that I used a promotion code I generated to test the IAP. I don’t know whether it could cause any problems or not ?

I would recommend putting in some print statements and see what’s happening. You can put a print in front of your call to store.purchase() and see if it’s printing twice. In your call back listener, you can print out the values and make sure you’re getting what you expect.

If your device is tethered to your computer and you used Corona SDK to install to your device, then the console messages from your device should show up in your console window for your simulator.

Rob

I made a test with your suggestions. Here is the code I tested now:

[lua] 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 [/lua]

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

And, when I click on the button, I can see that two same windows are opened at the same time.
 

And, do you think I shall have posted my messages in the section monetization in Corona APIs?

Thanks in advance for your replies

And, when I try to restore, the app exists…

local defiesBtn = widget.newButton { width = 552, height = 244, defaultFile = "Defies button.png", overFile = "Defies button pressed.png", onEvent = handleButton2

See where you’re enabling onEvent? That options will cause “handleButton2” to be called twice, once on press and once on release. This mimics “touch” behavior. You have to test for event.phase == “began” or "ended in your function or it gets called twice.

The easiest fix in your case is to not use “onEvent” but use “onRelease” instead. Then your handler functions will be called just once without you have to change the functions.

Rob

Hello,

Thanks for your reply.

I just made a test with the changes and it is still not working… I get the exact same errors…

Do you think it has something to do with my code?

And, I forgot to tell you that I used a promotion code I generated to test the IAP. I don’t know whether it could cause any problems or not ?

I would recommend putting in some print statements and see what’s happening. You can put a print in front of your call to store.purchase() and see if it’s printing twice. In your call back listener, you can print out the values and make sure you’re getting what you expect.

If your device is tethered to your computer and you used Corona SDK to install to your device, then the console messages from your device should show up in your console window for your simulator.

Rob

I made a test with your suggestions. Here is the code I tested now:

[lua] 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 [/lua]

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

And, when I click on the button, I can see that two same windows are opened at the same time.
 

And, do you think I shall have posted my messages in the section monetization in Corona APIs?

Thanks in advance for your replies