Adding code but nothing happens.

Hello,

I have never worked with in app purchases before and I was just wondering why nothing happens when I go to where I have implemented the code. My build.settings is correct and I have followed the tutorials. Everything is set up in the google developer console but when I go to the page where my code is nothing happens. Is there a simple explanation? (I’m using plugin.google.iap.v3)

Thanks!

We’re going to need to to see your code before we can provide any assistance. 

Remember to use the <lua> formatting when posting your code!

local store = require( "plugin.google.iap.v3" ) local function loadProductsListener( event ) print("In loadProductsListener") local products = event.products for i=1, #event.products do print(event.products[i].title) print(event.products[i].description) print(event.products[i].localizedPrice) print(event.products[i].productIdentifier) end for i=1, #event.invalidProducts do print("invalid", event.invalidProducts[i]) end end store.init( "google", transactionCallBack ) if store.canLoadProducts then print("Loading products!") store.loadProducts( productList, loadProductsListener ) timer.performWithDelay(2000, function() store.restore(); end) else print("Can't load products") end

I basically stripped it down multiple times and just have this as of right now because no matter what I did nothing worked.

I’m not saying I’m an IAP expert, but I believe you still need some sort of store GUI frontend so that players can interact with, “click” on and purchase your IAP products. Check out the IAP sample delivered with the Corona SDK install to learn more about how to implement a store.

Do you have transactionCallBack implemented?

@Alex, you don’t provide GUI frontend for IAPs, store vendors do that (like green popup in Google Play)

Sorry, I meant more like a frontend interface that shows products available, and a buy button, or similar. I didn’t mean a complete system that takes you through the transaction. I assumed that from the statement “nothing happens” and that “when I go to where I’ve implemented my code” meant that this developer was attempting a frontend such as the one I’m describing (and is available in the Corona IAP sample) and wasn’t getting the desired result.

Sorry for not being more clear!

Alright I finally got it working. I’m just not sure of one thing. Once all the code is in place which it seems to be how do I award the player their purchase? Like, I’m giving in game currency but i’m not sure what “if-then” statement to use to award the player their currency once it is purchased. Any help would be much appreciated thanks!

The basic work flow is:

have a button somewhere in your code that the user will click on to buy the coins.  This could be on a menu screen or your own store/shop screen or on a “Loose the level” screen ("Hey, you lost, buying more coins can help, would you like to buy some? type screen).  Once the player taps on the button to buy the item you do:

store.purchase( “youritemname”) – Google/Amazon  or

store.purchase( { “youritemname” } ) – Apple

This will interact with the store  and depending on what happens your transactionCallback function will get triggered.  If they bought the item you will get a event.transaction.state = purchased event.  If they cancelled the dialog, I think you get a transaction cancelled state.

When you get the transaction purchased state, then you can add the coins to their account.  The logic (those if statements) are all determined by your code and number of store products.   For instance, if you only have one item they can buy, then it’s the only purchase, so you don’t need to test to see what product they bought.

local function transactionCallback( event ) print("In transactionCallback", event.transaction.state) local transaction = event.transaction local tstate = event.transaction.state if tstate == "purchased" then print("Transaction succuessful!") if transaction.productIdentifier == "com.omnigeekmedia.mygame.unlock" then -- handle unlocking power ups mydata.settings.isPaid = true utility.saveTable(mydata.settings, "settings.json") ads.hide() native.showAlert("Thank you!", "We appreciate your support!", {"Okay"}) end store.finishTransaction( transaction ) elseif tstate == "restored" then print("Transaction restored (from previous session)") if transaction.productIdentifier == "com.omnigeekmedia.mygame.unlock" then mydata.settings.isPaid = true ads.hide() utility.saveTable(mydata.settings, "settings.json") native.showAlert("Thank you!", "Your settings have been restored!", {"Okay"}) end store.finishTransaction( transaction ) elseif tstate == "refunded" then -- Google feature print("User requested a refund") local productId = transaction.productIdentifier if transaction.productIdentifier == "com.omnigeekmedia.mygame.unlock" then mydata.settings.isPaid = false utility.saveTable(mydata.settings, "settings.json") end store.finishTransaction( transaction ) elseif tstate == "revoked" then -- Amazon feature print("productIdentifier", transaction.productIdentifier) print ("The user who has a revoked purchase is", transaction.userId) --Revoke this SKU here: if transaction.productIdentifier == "com.omnigeekmedia.mygame.unlock" then mydata.settings.unlocked = false mydata.saveTable(mydata.settings, "settings.json") end store.finishTransaction( transaction ) elseif tstate == "cancelled" then print("388 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

In my case, it’s a simple unlock/turn off the ads feature.  Inside each possible transaction state, I test to see if it’s the unlock item and if so I do what’s needed.  In my case, I set a flag saying .isPaid to true that I use later.  In your case, you would just add the number of coins bought to their coin total.   Then for Google, you would need to call store.consumePurchase() to allow them to buy the coins again.

Hope this helps

Rob

It works great! Thanks a bunch Rob!

So i’m just running into one last thing. Its pretty simple and i’m probably doing something dumb. I’m using the corona sample code for my IAP stuff (or at least some of it) the problem i’m having is that no matter what I try the buttons wont go away when I exit the scene. This is the code that seems to be lingering…

function setupMyStore() -- Usually you would only list products for purchase if the device supports in-app purchases. -- But for the sake of testing the user interface, you might want to list products if running on the simulator too. if store.isActive or isSimulator then if store.canLoadProducts then -- Property "canLoadProducts" indicates that localized product information such as name and price -- can be retrieved from the store (such as iTunes). Fetch all product info here asynchronously. store.loadProducts( currentProductList, loadProductsCallback ) print ("After store.loadProducts, waiting for callback") else -- Unable to retrieve products from the store because: -- 1) The store does not support apps fetching products, such as Google's Android Marketplace. -- 2) No store was loaded, in which case we could load dummy items or display no items to purchase validProducts[1] = {} validProducts[1].title = "" validProducts[1].description = "blah!" if currentProductList then validProducts[1].productIdentifier = currentProductList[1] end validProducts[2] = {} validProducts[2].title = "" validProducts[2].description = "blah!" if currentProductList then validProducts[2].productIdentifier = currentProductList[2] end validProducts[3] = {} validProducts[3].title = "" validProducts[3].description = "blah!" if currentProductList then validProducts[3].productIdentifier = currentProductList[3] end validProducts[4] = {} validProducts[4].title = "" validProducts[4].description = "blah!" if currentProductList then validProducts[4].productIdentifier = currentProductList[4] end validProducts[5] = {} validProducts[5].title = "" validProducts[5].description = "blah!" if currentProductList then validProducts[5].productIdentifier = currentProductList[5] end validProducts[6] = {} validProducts[6].title = "" validProducts[6].description = "blah!" if currentProductList then validProducts[6].productIdentifier = currentProductList[6] end validProducts[7] = {} validProducts[7].title = "-" validProducts[7].description = "blah!" if currentProductList then validProducts[7].productIdentifier = currentProductList[7] end addProductFields() end else -- Don't load any products. In-app purchases is not supported on this device. addProductFields() end end

I just can’t figure out how to stop this from carrying over to other scenes. Thanks so much!

Are you adding the buttons to your scene’s view group:   i.e. sceneGroup:insert(yourButton)?

Rob

I’m not really sure where to do that. This is the code for the buttons. This is pretty much taken from the sample code.

unction addProductFields() -- Utility function to build a buy button. function newBuyButton (index) -- Handler for buy button local buttonDefault = "buttonBuy.png" local buttonOver = "buttonBuyDown.png" local buyThis = function ( productId ) -- Check if it is possible to purchase the item, then attempt to buy it. if store.isActive == false then showStoreNotAvailableWarning() elseif store.canMakePurchases == false then native.showAlert("Store purchases are not available, please try again later", {"OK"}) elseif productId then print("Purchasing.. " .. tostring(productId)) store.purchase( {productId} ) end end function buyThis\_closure ( index ) -- Closure wrapper for buyThis() to remember which button return function ( event ) buyThis(validProducts[index].productIdentifier) return true end end local hideDescription = function ( ) descriptionArea.text = "Select a product..." end local describeThis = function ( description ) -- Display product description for testing print ("About this product: " ..description) -- TODO wrap if necessary descriptionArea.text = description timer.performWithDelay( 2000, hideDescription) end function describeThis\_closure ( index ) -- Closure wrapper for describeThis() to remember which button return function ( event ) describeThis (validProducts[index].description) return true end end local label = validProducts[index].title if validProducts[index].price then -- Product price is known. In this case, display the price in the button. label = label .. " " string.format("%.2f", validProducts[index].price) else -- Product price is not known. In this case we expect a localized price to be -- displayed via the in-app purchase system's own UI. end local myButton = widget.newButton { defaultFile = buttonDefault, overFile = buttonOver, label = "", labelColor = { default = { 2/255, 0, 127/255 }, over = { 2/255, 0, 127/255 } }, font = "Marker Felt", fontSize = 14, emboss = false, onPress = describeThis\_closure (index), onRelease = buyThis\_closure (index), } -- myButton:setReferencePoint(display.CenterLeftReferencePoint) myButton.anchorX = .8 -- left myButton:setLabel(label) return myButton end -- Utility to build a restore button function newRestoreButton () local buttonDefault = "buttonRestore.png" local buttonOver = "buttonRestoreDown.png" local restore = function ( product ) -- Request the store to restore all previously purchased products. if store.isActive then print ("Restoring " ) store.restore() else showStoreNotAvailableWarning() end end local hideDescription = function ( ) descriptionArea.text = "Select a product..." end local describeThis = function () -- Display info in description area print ("Restore feature") descriptionArea.text = "Restore feature" timer.performWithDelay( 2000, hideDescription) end local label = "" local myButton = widget.newButton { defaultFile = buttonDefault, overFile = buttonOver, label = "", labelColor = { default = { 2/255, 0, 127/255 }, over = { 2/255, 0, 127/255 } }, font = "Marker Felt", fontSize = 14, emboss = false, onPress = describeThis, onRelease = restore, } -- myButton:setReferencePoint(display.CenterLeftReferencePoint) myButton.anchorX = 100 -- left myButton:setLabel(label) return myButton end -- Display product purchasing options print ("Loading product list") if (not validProducts) or (#validProducts \<= 0) then -- There are no products to purchase. This indicates that in-app purchasing is not supported. local noProductsLabel = display.newText( "", display.contentWidth / 2, display.contentHeight / 3, display.contentWidth / 2, 0, native.systemFont, 16) noProductsLabel:setFillColor(0, 0, 0) noProductsLabel.anchorX = 0 noProductsLabel.anchorY = 0 showStoreNotAvailableWarning() else -- Products for purchasing have been received. Create options to purchase them below. print("Product list loaded") print("Country: " .. tostring(system.getPreference("locale", "country"))) -- Set up native text box for displaying current transaction status. descriptionArea = native.newTextBox( -1000, 0 \* display.contentHeight, display.contentCenterX, display.contentCenterY) descriptionArea.text = "Select a product..." descriptionArea:setTextColor(2/255, 0, 127/255) descriptionArea.size = 0 descriptionArea.hasBackground = false descriptionArea.anchorX = - 1 descriptionArea.anchorY = - 1 local buttonSpacing = 10 print( "Found " .. #validProducts .. " valid items ") -- display the valid products in buttons for i=1, #validProducts do -- Debug: print out product info print("Item " .. tostring(i) .. ": " .. tostring(validProducts[i].productIdentifier) .. " (" .. tostring(validProducts[i].price) .. ")") print(validProducts[i].title .. ", ".. validProducts[i].description) -- create and position product button local myButton = newBuyButton(i) myButton.x = display.contentWidth - myButton.width - buttonSpacing myButton.y = 170 - i \* buttonSpacing + (3.5 \* i - 1) \* myButton.height / 2 end -- Create and position Restore button. if store.isActive or isSimulator then local myButton = newRestoreButton() myButton.x = display.contentWidth - myButton.width - buttonSpacing - 100 myButton.y = display.contentHeight - myButton.height / .5 - buttonSpacing end -- Debug: Display invalid prodcut info loaded from the store. -- You would not normally do this in a relese build of your app. for i=1, #invalidProducts do native.showAlert( "Item " .. tostring(invalidProducts[i]) .. " is invalid.", {"OK"} ) print("Item " .. tostring(invalidProducts[i]) .. " is invalid.") end end end ------------------------------------------------------------------------------- -- Handler to receive product information -- This callback is set up by store.loadProducts() ------------------------------------------------------------------------------- function loadProductsCallback( event ) -- Debug info for testing print("In loadProductsCallback()") print("event, event.name", event, event.name) print(event.products) print("#event.products", #event.products) io.flush() -- remove for production -- save for later use validProducts = event.products invalidProducts = event.invalidProducts addProductFields() end

Thanks for your help!

Where does that code live?  If it’s in a scene but not inside scene:create() or scene:show() event handling functions, then you can do:

scene.view:insert(myButton)

if it’s inside one of those event functions then it’s a simple:

sceneGroup:insert(myButton)

If this code is in some kind of module that’s being called form a Composer scene (which it appears you are doing) you are returning “myButton” to the caller, you can insert it into the scene after you call it.

Rob

I knew it was something simple haha. Thanks again Rob you have been a big help!

We’re going to need to to see your code before we can provide any assistance. 

Remember to use the <lua> formatting when posting your code!

local store = require( "plugin.google.iap.v3" ) local function loadProductsListener( event ) print("In loadProductsListener") local products = event.products for i=1, #event.products do print(event.products[i].title) print(event.products[i].description) print(event.products[i].localizedPrice) print(event.products[i].productIdentifier) end for i=1, #event.invalidProducts do print("invalid", event.invalidProducts[i]) end end store.init( "google", transactionCallBack ) if store.canLoadProducts then print("Loading products!") store.loadProducts( productList, loadProductsListener ) timer.performWithDelay(2000, function() store.restore(); end) else print("Can't load products") end

I basically stripped it down multiple times and just have this as of right now because no matter what I did nothing worked.

I’m not saying I’m an IAP expert, but I believe you still need some sort of store GUI frontend so that players can interact with, “click” on and purchase your IAP products. Check out the IAP sample delivered with the Corona SDK install to learn more about how to implement a store.

Do you have transactionCallBack implemented?

@Alex, you don’t provide GUI frontend for IAPs, store vendors do that (like green popup in Google Play)

Sorry, I meant more like a frontend interface that shows products available, and a buy button, or similar. I didn’t mean a complete system that takes you through the transaction. I assumed that from the statement “nothing happens” and that “when I go to where I’ve implemented my code” meant that this developer was attempting a frontend such as the one I’m describing (and is available in the Corona IAP sample) and wasn’t getting the desired result.

Sorry for not being more clear!

Alright I finally got it working. I’m just not sure of one thing. Once all the code is in place which it seems to be how do I award the player their purchase? Like, I’m giving in game currency but i’m not sure what “if-then” statement to use to award the player their currency once it is purchased. Any help would be much appreciated thanks!