In-App Purchase IOS

Hi,
Im having som troubles with iap for ios as i think many have.
I have:
*Built the iap function using the inAppPurchase sample code from build 840. It works great with android but not with ios.
*Added the iap non-consumable product in ITC.
*Added a test user and signed out from app store with my regular account, but not logged in on the test user account.
*Added the product to the appleProductList in the code. My bundle id for the app is com.example.test and my product reference name and product id is both Upgrade. So to the appleProductList i have added com.example.test.Upgrade.
*I have waited more than 24 hours since i added the iap product to ITC.

But still i get the native alert from sample code to pop up with “In-app purchases is not supported on this device.” through the showStoreNotAvailableWarning() function which is called in
[lua]if store.isActive == false then
showStoreNotAvailableWarning() end[/lua]

What im i doing wrong? [import]uid: 126729 topic_id: 30161 reply_id: 330161[/import]

Well here is the exact code
[lua]–
– Abstract: In-App Purchase Sample Project

– This project demonstrates Corona In-App Purchase support.
– The code attempts to connect to a store such as iTunes or Google’s Android Marketplace
– to retrieve valid product information and handle transactions.

– IMPORTANT: Parts of this code can be exercised in Corona Simulator for
– testing, but Corona Simulator cannot do in-app purchases.

– To test with the iTunes store, you must:
– 1. Set up your own In App Purchase products in iTunes Connect.
– 2. Modify the code below to suit your products.
– 3. Set up a test user account and provisioning for your iOS test device.
– 3. Build and deploy on device.

– Version: 1.1

– Updated: March 1, 2012

– Update History:
– 1.0 Initial version supporting iTunes in-app purchasing only.
– 1.1 Adding Google’s Android Marketplace support.

– Sample code is MIT licensed, see http://www.coronalabs.com/links/code/license
– Copyright © 2010 Corona Labs Inc. All Rights Reserved.

module( …, package.seeall );

– You must call this first in order to use the “store” API.
local store = require(“store”) – Available in Corona build #261 or later

– This is needed for the UI controls.
local ui = require(“ui”)

– Determing if this app is running within the Corona Simulator.
local isSimulator = “simulator” == system.getInfo(“environment”)
– Unbuffer console output for debugging
–io.output():setvbuf(‘no’) – Remove me for production code
local bg
local validProducts, invalidProducts = {}, {}
local descriptionArea

local yAlignment = _H/2


– Product IDs should match the In App Purchase products set up in iTunes Connect.
– We cannot get them from the iTunes store so here they are hard coded;
– your app could obtain them dynamically from your server.

– To be assigned a product list from one of the arrays below after we’ve connected to a store.
– Will be nil if no store is supported on this system/device.
local currentProductList = nil

– Product IDs for the “apple” app store.
local appleProductList =
{
– These Product IDs must already be set up in your store
– We’ll use this list to retrieve prices etc. for each item
– Note, this simple test only has room for about 4 items, please adjust accordingly
– The iTunes store will not validate bad Product IDs
“com.ios.sj.Upgrade”,
–“com.anscamobile.NewExampleInAppPurchase.NonConsumableTier1”,
–“com.anscamobile.NewExampleInAppPurchase.SubscriptionTier1”,
}

– Product IDs for the “google” Android Marketplace store.
local googleProductList =
{
– These product IDs are used for testing and is supported by all Android apps.
– Purchasing these products will not bill your account.
“upgrade”, --“android.test.purchased”, – Marketplace will always successfully purchase this product ID.
–“android.test.canceled”, – Marketplace will always cancel a purchase of this product ID.
–“android.test.item_unavailable”, – Marketplace will always indicate this product ID as unavailable.
}


– Displays a warning indicating that store access is not available,
– meaning that Corona does not support in-app purchases on this system/device.
– To be called when the store.isActive property returns false.

function showStoreNotAvailableWarning()
if isSimulator then
native.showAlert(“Notice”, “In-app purchases is not supported by the Corona Simulator.”, { “OK” } )
else
native.showAlert(“Notice”, “In-app purchases is not supported on this device.”, { “OK” } )
end
end


– Process and display product information obtained from store.
– Constructs a button for each item

function addProductFields()

– Utility function to build a buy button.
function newBuyButton (index)
– Handler for buy button
local buttonDefault = “img/lvlbtn_0.png”
local buttonOver = “img/lvlbtn_0.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(“Ka-ching! 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 = ui.newButton{
default = buttonDefault, over = buttonOver,
onPress = describeThis_closure (index), onRelease = buyThis_closure (index),
text = “”, textColor = {255, 255, 255, 255}, font=“Postmaster”, size = 45, emboss = false
}

myButton:setText(label)
return myButton
end

– Utility to build a restore button
function newRestoreButton ()
local buttonDefault = “img/lvlbtn_0.png”
local buttonOver = “img/lvlbtn_0.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 (“Test restore feature”)
descriptionArea.text = “Trying to restore earlier purchase”
–timer.performWithDelay( 2000, hideDescription)
end
local label = “Restore”
local myButton1 = ui.newButton{
default = buttonDefault, over = buttonOver,
onPress = describeThis, onRelease = restore,
text = “”, textColor = {255, 255, 255, 255}, font=“Postmaster”, size = 45, emboss = false
}

myButton1:setText(label)
return myButton1
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(
“Sorry!\nIn-App purchases is not supported on this device.”,
display.contentWidth / 2, display.contentHeight / 3,
display.contentWidth / 2, 0,
native.systemFont, 16)
noProductsLabel:setTextColor(0, 0, 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(
150, 100,
468, 400)
descriptionArea.text = "Upgrade to the full version "
descriptionArea:setTextColor (29,62,103)–(2, 0, 127)
descriptionArea.size = 18

descriptionArea.hasBackground = false
local buttonSpacing = 5
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/2
myButton.y = yAlignment
yAlignment = yAlignment + 110
end

– Create and position Restore button.
–if (store.isActive or isSimulator) then
if ((store.isActive or isSimulator) and store.availableStores.apple) then
local myButton = newRestoreButton()
myButton.x = display.contentWidth/2
myButton.y = yAlignment
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


– Handler for all store transactions
– This callback is set up by store.init()

function transactionCallback( event )
local infoString

– Log transaction info.
print("transactionCallback: Received event " … tostring(event.name))
print("state: " … tostring(event.transaction.state))
print("errorType: " … tostring(event.transaction.errorType))
print("errorString: " … tostring(event.transaction.errorString))

if event.transaction.state == “purchased” then
infoString = “Transaction successful!”
print(infoString)
descriptionArea.text = infoString
db:exec(“UPDATE sj_store SET upgraded = 1 WHERE row_id = 1”)

elseif event.transaction.state == “restored” then
– Reminder: your app must store this information somewhere
– Here we just display some of it
infoString = “Restoring transaction:” …
"\n Original ID: " … tostring(event.transaction.originalTransactionIdentifier) …
"\n Original date: " … tostring(event.transaction.originalDate)
print(infoString)
descriptionArea.text = infoString
print("productIdentifier: " … tostring(event.transaction.productIdentifier))
print("receipt: " … tostring(event.transaction.receipt))
print("transactionIdentifier: " … tostring(event.transaction.transactionIdentifier))
print("date: " … tostring(event.transaction.date))
print("originalReceipt: " … tostring(event.transaction.originalReceipt))

elseif event.transaction.state == “refunded” then
– Refunds notifications is only supported by the Google Android Marketplace.
– Apple’s app store does not support this.
– This is your opportunity to remove the refunded feature/product if you want.
infoString = “A previously purchased product was refunded by the store.”
print(infoString … "\nFor product ID = " … tostring(event.transaction.productIdentifier))
descriptionArea.text = infoString
db:exec(“UPDATE sj_store SET upgraded = 0 WHERE row_id = 1”)

elseif event.transaction.state == “cancelled” then
infoString = “Transaction cancelled by user.”
print(infoString)
descriptionArea.text = infoString

elseif event.transaction.state == “failed” then
infoString = "Transaction failed, type: " …
tostring(event.transaction.errorType) … " " … tostring(event.transaction.errorString)
print(infoString)
descriptionArea.text = infoString

else
infoString = “Unknown event”
print(infoString)
descriptionArea.text = infoString
end

– Tell the store we are done with the transaction.
– If you are providing downloadable content, do not call this until
– the download has completed.
store.finishTransaction( event.transaction )
end


– Displays in-app purchase options.
– Loads product information from store if possible.

function setupMyStore(event)
– 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 = “Upgrade”
validProducts[1].description = “Upgrade to get 3 new worlds with 40 new levels”
if currentProductList then
validProducts[1].productIdentifier = currentProductList[1]
end

addProductFields()
end
else
– Don’t load any products. In-app purchases is not supported on this device.
addProductFields()
end
end

– Main

– Connect to store at startup, if available.
if store.availableStores.apple then
currentProductList = appleProductList
store.init(“apple”, transactionCallback)
print(“Using Apple’s in-app purchase system.”)

elseif store.availableStores.google then
currentProductList = googleProductList
store.init(“google”, transactionCallback)
print(“Using Google’s Android In-App Billing system.”)

else
print(“In-app purchases is not supported on this system/device.”)
end

function new()
local localGroup = display.newGroup()

local setupthestore = setupMyStore()
collectgarbage()
local function changeScene(e)
if (e.phase == “began”)then
e.target.xScale = 1.1
e.target.yScale = 1.1
end
if (e.phase == “ended” or e.phase == “cancelled”) then
if (descriptionArea) then
descriptionArea.isVisible = false
end

e.target.xScale = 1.0
e.target.yScale = 1.0
director:changeScene( {world=e.target.id, reload=true}, e.target.scene, “crossfade”);
end
end
local menu_btn = display.newImage(“img/ikoner/sj_btn_menu.png”)
menu_btn.x = _W/2
menu_btn.y = 1130 ;
menu_btn.scene = “menu”;
menu_btn:addEventListener(“touch”, changeScene)
if (descriptionArea) then
localGroup:insert(descriptionArea)
end
localGroup:insert(menu_btn)
return localGroup

end[/lua]

Why isnt there any working sample code without all mumbo-jumbo artwork and calls to ui classes. I would really like a copy-paste working code in the corona api since i imagine im not he first implementing iap. [import]uid: 126729 topic_id: 30161 reply_id: 120760[/import]

Someone who can help me out here? Im really stuck! [import]uid: 126729 topic_id: 30161 reply_id: 120915[/import]

@e7k_Erik, the code you posted is quite long, so I can only give you a general feedback. It seems like your problem is store.isActive is false when you try making a purchase. Why do you think store.isActive is false?

I did a quick search for setupMyStore, and it doesn’t seem like it is ever fired in the code you posted. I fire mine using timer like so: timer.performWithDelay (1000, setupMyStore)

Could it be the cause of your problem?

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 120920[/import]

Thanks for the help, but thats not the problem. setupMyStore fires on row 416. Otherwise i would not get the error message since the errormessage function fires from row 114 as a chainreaction from setupMyStore. I have done so since im using director and need a function new().

store.isActive is checked the first time on row 359 in setupMyStore, and there its no problem. But next time its checked on row 113, store.isActive is now false. Why i dont know. Everything in the iap code is from the sample code. The only changes i have done is changed button images, removing bg image, changed the productLists and added a new function for director from were setupMyStore is called instead of a timer.

Do you know reasons to why the store.isActive becomes false? [import]uid: 126729 topic_id: 30161 reply_id: 120925[/import]

@e7k_Erik, with my first game, I used Director, and I did not initialize store anywhere other than main.lua. I also fire setupMyStore in main.lua. The store related functions I call from other module (where the users make purchasing decisions) are store.canMakePurchases, store.purchase and unpackValidProducts – there could be some more, but I think these are the main ones, and anything associated with it would be in the external module, but otherwise, everything else resides in main.lua

Naomi

Edit: by the way, did you try running the IAP sample code using your app id and see what happens? And then, you can start pulling the working code apart to meet your needs (including putting some part of it in module other than main.lua) Going through step-by-step process of elimination for whatever is tripping you might help find the cause of your problem. [import]uid: 67217 topic_id: 30161 reply_id: 120926[/import]

Ok i solved it, the same issue as i was having for android. The product id.
Here i thought that i should use the app bundle + the product id like so com.ios.sj.Upgrade, but my product id is only Upgrade. So when i changed in the product list, It worked.

Is there any rules to what the product id should be for passing the review or doesnt it matter? [import]uid: 126729 topic_id: 30161 reply_id: 120927[/import]

@e7k_Erik, my product id looks more like com.mycompany.appname.Upgrade – and otherwise, it wouldn’t work at all. That said, I’ve heard some others who can’t get it to work that way and had to strip it down to Upgrade. I dont know why… but… could it be that you entered your product ID in iTC as Upgrade (not as com.mycompany.appname.Upgrade) Maybe that’s what’s going on?

I think whatever you enter as your product’s product ID in iTC is what you’d need to use. I’m assuming that the suggested convention is com.mycompany.appname.productname, but if you only entered productname under product ID in iTC, productname is your product ID rather than com.mycompany.appname.productname…? I’m just guessing, because it’s been puzzling me too. Let me know what you entered in iTC as your product ID – you might just be able to confirm my guessing work.

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 120931[/import]

I only entered Upgrade as my product id, same with android, since i thought that the rest came from the app bundle id, something like a domain name. But now i know i was wrong :slight_smile:

The ones who only put Upgrade as the id, did they pass the apple review with that? Because im thinking about if i should change it or let it be.

[import]uid: 126729 topic_id: 30161 reply_id: 120933[/import]

@e7k_Erik, I don’t know how it went with the others who used the product ID that didn’t follow the suggested naming convention. I imagine it worked out fine, because I haven’t seen any posts indicating otherwise. (I don’t know if I remember this correctly, but I sort of remember reading somewhere that once a specific product ID is created in iTC, no one else can use the very same product ID for another IAP product – so, perhaps no one ever used Upgrade as a product ID? It feels unreal, but maybe… Dunno.)

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 120935[/import]

It feels unreal if thats the case. It has to be connected to the bundle id in the background, probably :slight_smile: [import]uid: 126729 topic_id: 30161 reply_id: 120939[/import]

Hey, @e7k_Erik, maybe Apple wouldn’t approve the short form Product ID. Take a look at this thread (all the way down from there):

http://developer.coronalabs.com/forum/2012/03/18/iap-cannot-connect-itunes#comment-96227

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 121087[/import]

Thanks @Naomi I think i will change the product it to the bundle version just to be safe [import]uid: 126729 topic_id: 30161 reply_id: 121137[/import]

Well here is the exact code
[lua]–
– Abstract: In-App Purchase Sample Project

– This project demonstrates Corona In-App Purchase support.
– The code attempts to connect to a store such as iTunes or Google’s Android Marketplace
– to retrieve valid product information and handle transactions.

– IMPORTANT: Parts of this code can be exercised in Corona Simulator for
– testing, but Corona Simulator cannot do in-app purchases.

– To test with the iTunes store, you must:
– 1. Set up your own In App Purchase products in iTunes Connect.
– 2. Modify the code below to suit your products.
– 3. Set up a test user account and provisioning for your iOS test device.
– 3. Build and deploy on device.

– Version: 1.1

– Updated: March 1, 2012

– Update History:
– 1.0 Initial version supporting iTunes in-app purchasing only.
– 1.1 Adding Google’s Android Marketplace support.

– Sample code is MIT licensed, see http://www.coronalabs.com/links/code/license
– Copyright © 2010 Corona Labs Inc. All Rights Reserved.

module( …, package.seeall );

– You must call this first in order to use the “store” API.
local store = require(“store”) – Available in Corona build #261 or later

– This is needed for the UI controls.
local ui = require(“ui”)

– Determing if this app is running within the Corona Simulator.
local isSimulator = “simulator” == system.getInfo(“environment”)
– Unbuffer console output for debugging
–io.output():setvbuf(‘no’) – Remove me for production code
local bg
local validProducts, invalidProducts = {}, {}
local descriptionArea

local yAlignment = _H/2


– Product IDs should match the In App Purchase products set up in iTunes Connect.
– We cannot get them from the iTunes store so here they are hard coded;
– your app could obtain them dynamically from your server.

– To be assigned a product list from one of the arrays below after we’ve connected to a store.
– Will be nil if no store is supported on this system/device.
local currentProductList = nil

– Product IDs for the “apple” app store.
local appleProductList =
{
– These Product IDs must already be set up in your store
– We’ll use this list to retrieve prices etc. for each item
– Note, this simple test only has room for about 4 items, please adjust accordingly
– The iTunes store will not validate bad Product IDs
“com.ios.sj.Upgrade”,
–“com.anscamobile.NewExampleInAppPurchase.NonConsumableTier1”,
–“com.anscamobile.NewExampleInAppPurchase.SubscriptionTier1”,
}

– Product IDs for the “google” Android Marketplace store.
local googleProductList =
{
– These product IDs are used for testing and is supported by all Android apps.
– Purchasing these products will not bill your account.
“upgrade”, --“android.test.purchased”, – Marketplace will always successfully purchase this product ID.
–“android.test.canceled”, – Marketplace will always cancel a purchase of this product ID.
–“android.test.item_unavailable”, – Marketplace will always indicate this product ID as unavailable.
}


– Displays a warning indicating that store access is not available,
– meaning that Corona does not support in-app purchases on this system/device.
– To be called when the store.isActive property returns false.

function showStoreNotAvailableWarning()
if isSimulator then
native.showAlert(“Notice”, “In-app purchases is not supported by the Corona Simulator.”, { “OK” } )
else
native.showAlert(“Notice”, “In-app purchases is not supported on this device.”, { “OK” } )
end
end


– Process and display product information obtained from store.
– Constructs a button for each item

function addProductFields()

– Utility function to build a buy button.
function newBuyButton (index)
– Handler for buy button
local buttonDefault = “img/lvlbtn_0.png”
local buttonOver = “img/lvlbtn_0.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(“Ka-ching! 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 = ui.newButton{
default = buttonDefault, over = buttonOver,
onPress = describeThis_closure (index), onRelease = buyThis_closure (index),
text = “”, textColor = {255, 255, 255, 255}, font=“Postmaster”, size = 45, emboss = false
}

myButton:setText(label)
return myButton
end

– Utility to build a restore button
function newRestoreButton ()
local buttonDefault = “img/lvlbtn_0.png”
local buttonOver = “img/lvlbtn_0.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 (“Test restore feature”)
descriptionArea.text = “Trying to restore earlier purchase”
–timer.performWithDelay( 2000, hideDescription)
end
local label = “Restore”
local myButton1 = ui.newButton{
default = buttonDefault, over = buttonOver,
onPress = describeThis, onRelease = restore,
text = “”, textColor = {255, 255, 255, 255}, font=“Postmaster”, size = 45, emboss = false
}

myButton1:setText(label)
return myButton1
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(
“Sorry!\nIn-App purchases is not supported on this device.”,
display.contentWidth / 2, display.contentHeight / 3,
display.contentWidth / 2, 0,
native.systemFont, 16)
noProductsLabel:setTextColor(0, 0, 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(
150, 100,
468, 400)
descriptionArea.text = "Upgrade to the full version "
descriptionArea:setTextColor (29,62,103)–(2, 0, 127)
descriptionArea.size = 18

descriptionArea.hasBackground = false
local buttonSpacing = 5
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/2
myButton.y = yAlignment
yAlignment = yAlignment + 110
end

– Create and position Restore button.
–if (store.isActive or isSimulator) then
if ((store.isActive or isSimulator) and store.availableStores.apple) then
local myButton = newRestoreButton()
myButton.x = display.contentWidth/2
myButton.y = yAlignment
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


– Handler for all store transactions
– This callback is set up by store.init()

function transactionCallback( event )
local infoString

– Log transaction info.
print("transactionCallback: Received event " … tostring(event.name))
print("state: " … tostring(event.transaction.state))
print("errorType: " … tostring(event.transaction.errorType))
print("errorString: " … tostring(event.transaction.errorString))

if event.transaction.state == “purchased” then
infoString = “Transaction successful!”
print(infoString)
descriptionArea.text = infoString
db:exec(“UPDATE sj_store SET upgraded = 1 WHERE row_id = 1”)

elseif event.transaction.state == “restored” then
– Reminder: your app must store this information somewhere
– Here we just display some of it
infoString = “Restoring transaction:” …
"\n Original ID: " … tostring(event.transaction.originalTransactionIdentifier) …
"\n Original date: " … tostring(event.transaction.originalDate)
print(infoString)
descriptionArea.text = infoString
print("productIdentifier: " … tostring(event.transaction.productIdentifier))
print("receipt: " … tostring(event.transaction.receipt))
print("transactionIdentifier: " … tostring(event.transaction.transactionIdentifier))
print("date: " … tostring(event.transaction.date))
print("originalReceipt: " … tostring(event.transaction.originalReceipt))

elseif event.transaction.state == “refunded” then
– Refunds notifications is only supported by the Google Android Marketplace.
– Apple’s app store does not support this.
– This is your opportunity to remove the refunded feature/product if you want.
infoString = “A previously purchased product was refunded by the store.”
print(infoString … "\nFor product ID = " … tostring(event.transaction.productIdentifier))
descriptionArea.text = infoString
db:exec(“UPDATE sj_store SET upgraded = 0 WHERE row_id = 1”)

elseif event.transaction.state == “cancelled” then
infoString = “Transaction cancelled by user.”
print(infoString)
descriptionArea.text = infoString

elseif event.transaction.state == “failed” then
infoString = "Transaction failed, type: " …
tostring(event.transaction.errorType) … " " … tostring(event.transaction.errorString)
print(infoString)
descriptionArea.text = infoString

else
infoString = “Unknown event”
print(infoString)
descriptionArea.text = infoString
end

– Tell the store we are done with the transaction.
– If you are providing downloadable content, do not call this until
– the download has completed.
store.finishTransaction( event.transaction )
end


– Displays in-app purchase options.
– Loads product information from store if possible.

function setupMyStore(event)
– 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 = “Upgrade”
validProducts[1].description = “Upgrade to get 3 new worlds with 40 new levels”
if currentProductList then
validProducts[1].productIdentifier = currentProductList[1]
end

addProductFields()
end
else
– Don’t load any products. In-app purchases is not supported on this device.
addProductFields()
end
end

– Main

– Connect to store at startup, if available.
if store.availableStores.apple then
currentProductList = appleProductList
store.init(“apple”, transactionCallback)
print(“Using Apple’s in-app purchase system.”)

elseif store.availableStores.google then
currentProductList = googleProductList
store.init(“google”, transactionCallback)
print(“Using Google’s Android In-App Billing system.”)

else
print(“In-app purchases is not supported on this system/device.”)
end

function new()
local localGroup = display.newGroup()

local setupthestore = setupMyStore()
collectgarbage()
local function changeScene(e)
if (e.phase == “began”)then
e.target.xScale = 1.1
e.target.yScale = 1.1
end
if (e.phase == “ended” or e.phase == “cancelled”) then
if (descriptionArea) then
descriptionArea.isVisible = false
end

e.target.xScale = 1.0
e.target.yScale = 1.0
director:changeScene( {world=e.target.id, reload=true}, e.target.scene, “crossfade”);
end
end
local menu_btn = display.newImage(“img/ikoner/sj_btn_menu.png”)
menu_btn.x = _W/2
menu_btn.y = 1130 ;
menu_btn.scene = “menu”;
menu_btn:addEventListener(“touch”, changeScene)
if (descriptionArea) then
localGroup:insert(descriptionArea)
end
localGroup:insert(menu_btn)
return localGroup

end[/lua]

Why isnt there any working sample code without all mumbo-jumbo artwork and calls to ui classes. I would really like a copy-paste working code in the corona api since i imagine im not he first implementing iap. [import]uid: 126729 topic_id: 30161 reply_id: 120760[/import]

Someone who can help me out here? Im really stuck! [import]uid: 126729 topic_id: 30161 reply_id: 120915[/import]

@e7k_Erik, the code you posted is quite long, so I can only give you a general feedback. It seems like your problem is store.isActive is false when you try making a purchase. Why do you think store.isActive is false?

I did a quick search for setupMyStore, and it doesn’t seem like it is ever fired in the code you posted. I fire mine using timer like so: timer.performWithDelay (1000, setupMyStore)

Could it be the cause of your problem?

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 120920[/import]

Thanks for the help, but thats not the problem. setupMyStore fires on row 416. Otherwise i would not get the error message since the errormessage function fires from row 114 as a chainreaction from setupMyStore. I have done so since im using director and need a function new().

store.isActive is checked the first time on row 359 in setupMyStore, and there its no problem. But next time its checked on row 113, store.isActive is now false. Why i dont know. Everything in the iap code is from the sample code. The only changes i have done is changed button images, removing bg image, changed the productLists and added a new function for director from were setupMyStore is called instead of a timer.

Do you know reasons to why the store.isActive becomes false? [import]uid: 126729 topic_id: 30161 reply_id: 120925[/import]

@e7k_Erik, with my first game, I used Director, and I did not initialize store anywhere other than main.lua. I also fire setupMyStore in main.lua. The store related functions I call from other module (where the users make purchasing decisions) are store.canMakePurchases, store.purchase and unpackValidProducts – there could be some more, but I think these are the main ones, and anything associated with it would be in the external module, but otherwise, everything else resides in main.lua

Naomi

Edit: by the way, did you try running the IAP sample code using your app id and see what happens? And then, you can start pulling the working code apart to meet your needs (including putting some part of it in module other than main.lua) Going through step-by-step process of elimination for whatever is tripping you might help find the cause of your problem. [import]uid: 67217 topic_id: 30161 reply_id: 120926[/import]

Ok i solved it, the same issue as i was having for android. The product id.
Here i thought that i should use the app bundle + the product id like so com.ios.sj.Upgrade, but my product id is only Upgrade. So when i changed in the product list, It worked.

Is there any rules to what the product id should be for passing the review or doesnt it matter? [import]uid: 126729 topic_id: 30161 reply_id: 120927[/import]

@e7k_Erik, my product id looks more like com.mycompany.appname.Upgrade – and otherwise, it wouldn’t work at all. That said, I’ve heard some others who can’t get it to work that way and had to strip it down to Upgrade. I dont know why… but… could it be that you entered your product ID in iTC as Upgrade (not as com.mycompany.appname.Upgrade) Maybe that’s what’s going on?

I think whatever you enter as your product’s product ID in iTC is what you’d need to use. I’m assuming that the suggested convention is com.mycompany.appname.productname, but if you only entered productname under product ID in iTC, productname is your product ID rather than com.mycompany.appname.productname…? I’m just guessing, because it’s been puzzling me too. Let me know what you entered in iTC as your product ID – you might just be able to confirm my guessing work.

Naomi [import]uid: 67217 topic_id: 30161 reply_id: 120931[/import]