RESTORE Button problem. Need urgent help.

Hello people! I need some help with restore button!

And in case that my app promotion starts really soon, i need it as fast as possible.

Apple rejected my app in case that restore button doesn’t work properly.

I won’t tell you how i did this, i will just paste my code here.

If there is any error, please tell me. Thank you.

one of my items to purchase and it’s function:

 local whiskeyBot = display.newImageRect("whiskey.png", 106, 205) whiskeyBot.x = screenW/1.28 whiskeyBot.y = screenH/1.42 whiskeyBot.alpha=0 local whiskeyFunc = function(event) if event.phase == "ended" then circle.x = whiskeyBot.x circle.y = whiskeyBot.y circle.alpha=1 audio.play( tapsound, {channel=2}) -- Handler that gets notified when the alert closes if require("socket").connect("google.com", 80) == nil then print("No connection") local function onComplete( event ) if "clicked" == event.action then local i = event.index if 1 == i then circle.alpha=0 end end end -- Show alert with two buttons native.showAlert( "Shop", "Store is not available. Please check your internet connection.", { "OK" }, onComplete ) else local buyWhiskey = function ( product ) print ("Congrats! Purchasing " ..product) -- Purchase the item if store.canMakePurchases then infoString = "Can make purchases!" store.purchase( {"whiskey\_bottle"} ) else native.showAlert("Store purchases are not available, please try again later", { "OK" } ) end end -- Enter your product ID here buyWhiskey("whiskey\_bottle") end end end whiskeyBot:addEventListener("touch", whiskeyFunc)

In-app code:

-----------IN-APP-INIT local store = require "store" -----------IN-APP-INIT local listOfProducts = { -- These Product IDs must already be set up in your store -- Note, this simple test only has room for about 4 items, please adjust accordingly -- The iTunes store will not validate bad Product IDs -- Replace with a valid Product ID from iTunes Connect "all\_bottle", "absinth\_bottle", "beer\_bottle", "champagne\_bottle", "cognac\_bottle", "port\_bottle", "vermouth\_bottle", "champagne2\_bottle", "xo\_bottle", "whiskey\_bottle" } ---------------------------------- -- In-App area ---------------------------------- local validProducts = {} local invalidProducts = {} local unpackValidProducts = function() print ("Loading product list") if not validProducts then native.showAlert( "In-App features not available", "initStore() failed", { "OK" } ) else print( "Found " .. #validProducts .. " valid items ") infoString = ( "Found " .. #validProducts .. " valid items ") print(validProducts[1].localizedPrice) PriceOne = (validProducts[1].localizedPrice) print(validProducts[2].localizedPrice) PriceTwo = (validProducts[2].localizedPrice) print(validProducts[3].localizedPrice) PriceThree = (validProducts[3].localizedPrice) print(validProducts[4].localizedPrice) PriceFour = (validProducts[4].localizedPrice) print(validProducts[5].localizedPrice) PriceFive = (validProducts[5].localizedPrice) print(validProducts[6].localizedPrice) PriceSix = (validProducts[6].localizedPrice) print(validProducts[7].localizedPrice) PriceSeven = (validProducts[7].localizedPrice) print(validProducts[8].localizedPrice) PriceEight = (validProducts[8].localizedPrice) print(validProducts[9].localizedPrice) PriceNine = (validProducts[9].localizedPrice) print(validProducts[10].localizedPrice) PriceTen = (validProducts[10].localizedPrice) for i=1, #invalidProducts do -- Debug: display the product info native.showAlert( "Item " .. invalidProducts[i] .. " is invalid.",{ "OK" } ) print("Item " .. invalidProducts[i] .. " is invalid.") end end end local loadProductsCallback = function( event ) -- Debug info for testing print("loadProductsCallback()") print("event, event.name", event, event.name) print(event.products) print("#event.products", #event.products) validProducts = event.products invalidProducts = event.invalidProducts unpackValidProducts () end local transactionCallback = function( event ) if event.transaction.state == "purchased" then print("Transaction successful!") infoString = "Transaction successful!" if event.transaction.productIdentifier == "all\_bottle" then allBot:set("allBot", "yes") allBot:save() absinth:set("absinth", "yes") absinth:save() beer:set("beer", "yes") beer:save() champagne:set("champagne", "yes") champagne:save() cognac:set("cognac", "yes") cognac:save() port:set("port", "yes") port:save() vermouth:set("vermouth", "yes") vermouth:save() champagne2:set("champagne2", "yes") champagne2:save() xo:set("xo", "yes") xo:save() whiskey:set("whiskey", "yes") whiskey:save() end if event.transaction.productIdentifier == "absinth\_bottle" then absinth:set("absinth", "yes") absinth:save() end if event.transaction.productIdentifier == "beer\_bottle" then beer:set("beer", "yes") beer:save() end if event.transaction.productIdentifier == "champagne\_bottle" then champagne:set("champagne", "yes") champagne:save() end if event.transaction.productIdentifier == "cognac\_bottle" then cognac:set("cognac", "yes") cognac:save() end if event.transaction.productIdentifier == "port\_bottle" then port:set("port", "yes") port:save() end if event.transaction.productIdentifier == "vermouth\_bottle" then vermouth:set("vermouth", "yes") vermouth:save() end if event.transaction.productIdentifier == "champagne2\_bottle" then champagne2:set("champagne2", "yes") champagne2:save() end if event.transaction.productIdentifier == "xo\_bottle" then xo:set("xo", "yes") xo:save() end if event.transaction.productIdentifier == "whiskey\_bottle" then whiskey:set("whiskey", "yes") whiskey:save() end local function onComplete( event ) if "clicked" == event.action then local i = event.index if 1 == i then circle.alpha=0 tl = timer.performWithDelay(500, function() director:changeScene("shop", "fade") timer.cancel(tl) end, 1) end end end -- Show alert with two buttons local alert = native.showAlert( "Shop", "Thank you! Your purchase is done!", { "OK" }, onComplete) elseif event.transcation.state == "restored" then if event.transaction.productIdentifier == "all\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<All bottles\> has been restored!", { "OK" }) allBot:set("allBot", "yes") allBot:save() absinth:set("absinth", "yes") absinth:save() beer:set("beer", "yes") beer:save() champagne:set("champagne", "yes") champagne:save() cognac:set("cognac", "yes") cognac:save() port:set("port", "yes") port:save() vermouth:set("vermouth", "yes") vermouth:save() champagne2:set("champagne2", "yes") champagne2:save() xo:set("xo", "yes") xo:save() whiskey:set("whiskey", "yes") whiskey:save() end if event.transaction.productIdentifier == "absinth\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Absinth bottle\> has been restored!", { "OK" }) absinth:set("absinth", "yes") absinth:save() end if event.transaction.productIdentifier == "beer\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Beer bottle\> has been restored!", { "OK" }) beer:set("beer", "yes") beer:save() end if event.transaction.productIdentifier == "champagne\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Champagne bottle\> has been restored!", { "OK" }) champagne:set("champagne", "yes") champagne:save() end if event.transaction.productIdentifier == "cognac\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Cognac bottle\> has been restored!", { "OK" }) cognac:set("cognac", "yes") cognac:save() end if event.transaction.productIdentifier == "port\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Port bottle\> has been restored!", { "OK" }) port:set("port", "yes") port:save() end if event.transaction.productIdentifier == "vermouth\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Vermouth bottle\> has been restored!", { "OK" }) vermouth:set("vermouth", "yes") vermouth:save() end if event.transaction.productIdentifier == "champagne2\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Champagne Holiday bottle \> has been restored!", { "OK" }) champagne2:set("champagne2", "yes") champagne2:save() end if event.transaction.productIdentifier == "xo\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<XO bottle\> has been restored!", { "OK" }) xo:set("xo", "yes") xo:save() end if event.transaction.productIdentifier == "whiskey\_bottle" then native.showAlert( "Shop", "Thank you! Purchase \<Whiskey bottles\> has been restored!", { "OK" }) whiskey:set("whiskey", "yes") whiskey:save() end native.showAlert( "Shop", "Thank you! Purchase \<Whiskey bottles\> has been restored!", { "OK" }) elseif event.transaction.state == "cancelled" then print("Transaction cancelled by user.") infoString = "Transaction canceled by user!" circle.alpha=0 elseif event.transaction.state == "failed" then print("Transaction failed, type: ", event.transaction.errorType, event.transaction.errorString) local alert = native.showAlert("Failed ", infoString,{ "OK" }) circle.alpha=0 else print("Unknown event") local alert = native.showAlert("Unknown ", infoString,{ "OK" }) circle.alpha=0 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 ) circle.alpha=0 end local setupMyStore = function(event) store.loadProducts( listOfProducts, loadProductsCallback ) print ("After store.loadProducts(), waiting for callback") end -------------------------------------------------------------------- 

Restore btn function:

 local restoreFunction = function ( event ) if event.phase == "release" then local function onCompleteX( event ) if "clicked" == event.action then local i = event.index if 1 == i then store.restore(transactionCallback) elseif 2 == i then -- Open URL if "Learn More" (the 2nd button) was clicked end end end -- Show alert with two buttons local alertX = native.showAlert( "Shop", "Restore Purchases?", { "Yes", "No" }, onCompleteX ) end end 

And in app init 

------APP-TURNON----- -- Connect to store at startup, if available. if store.availableStores.apple then print("Using Apple's in-app purchase system.") infoString = "Using Apple's in-app purchase system." store.init("apple", transactionCallback) timer.performWithDelay (500, setupMyStore) elseif store.availableStores.google then print("Using Google's Android In-App Billing system.") infoString = "Using Google's Android In-App Billing system." store.init("google", transactionCallback) else print("In-app purchases is not supported on this system/device.") infoString = "In-app purchases is not supported on this system/device." end ------APP-TURNON-----

The thing is that when i touch Restore btn, nothing happens.

Any help will be appreciated.

The thing is that when i call Restore btn, it only call store.restore, but futher operations are fail to call. The process is even not calling “restored” state in transitionCallback. 

Your code has a line that says “store.restore(transactionCallback)”, but store.restore() doesn’t actually take any arguments (see the documentation here: http://docs.coronalabs.com/api/library/store/restore.html).  Instead, you have to specify your transaction callback function by first calling store.init([storeName], transactionCallback).  Then when you later call store.restore(), with no arguments, the callback you specified in store.init() will be used.

That said, I don’t think passing an argument to store.restore() will cause a problem, it should just be ignored.  But you should probably remove the argument just in case.

  • Andrew

  • Andrew

Andrew, thank you for your response, but i tried with no argument - same result.

Could you please tell me if everything OK with restore function in transactionCallback? Cause i can’t be sure in nothing now… 

Can anybody help? I thing there might be an error in my code maybe

Still hope someone will help me with this. Thank you.

Are your items one time purchases or are you trying to unlock something?  One time purchases don’t get restored.  Without knowing more about your app and what you’re trying to do all we can do is speculate. 

Can you put some print statements in your transaction callback function to see what’s going on?

The thing is that when i call Restore btn, it only call store.restore, but futher operations are fail to call. The process is even not calling “restored” state in transitionCallback. 

Your code has a line that says “store.restore(transactionCallback)”, but store.restore() doesn’t actually take any arguments (see the documentation here: http://docs.coronalabs.com/api/library/store/restore.html).  Instead, you have to specify your transaction callback function by first calling store.init([storeName], transactionCallback).  Then when you later call store.restore(), with no arguments, the callback you specified in store.init() will be used.

That said, I don’t think passing an argument to store.restore() will cause a problem, it should just be ignored.  But you should probably remove the argument just in case.

  • Andrew

  • Andrew

Andrew, thank you for your response, but i tried with no argument - same result.

Could you please tell me if everything OK with restore function in transactionCallback? Cause i can’t be sure in nothing now… 

Can anybody help? I thing there might be an error in my code maybe

Still hope someone will help me with this. Thank you.

Are your items one time purchases or are you trying to unlock something?  One time purchases don’t get restored.  Without knowing more about your app and what you’re trying to do all we can do is speculate. 

Can you put some print statements in your transaction callback function to see what’s going on?

You product IDs should be like com.yourcompany.yourgame.whisky, but you only use whisky?

Hello! You mean this line?

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;if event.transaction.productIdentifier == "whiskey\_bottle" then &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;native.showAlert( "Shop", "Thank you! Purchase \<Whiskey bottles\> has been restored!", { "OK" }) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whiskey:set("whiskey", "yes") &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;whiskey:save()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end &nbsp;

if yes, then i use GGData to save some data, and i called a file that controls whiskey bottle just “whiskey”

And in event.transaction.productIdentifier my product is “whiskey_bottle” because i registered my in-app purchase in iTunes Store this way, “whiskey_bottle”)

So if i understand right i should use the name exactly the same as i used when i registered in-app in iTunes Store?

Apple told me this, when i asked them about what items should be restored:

"Thank you for your inquiry. 

Consumable products are purchased each time the user needs the item. Non-consumable products are only purchased once by users and are always available on all devices that are associated with that user’s iTunes account. 

As specified in the In-App Purchase Programming Guide:

“…if your application supports product types that must be restorable, you must include an interface that allows users to restore these purchases. This interface allows a user to add the product to other devices or, if the original device was wiped, to restore the transaction on the original device.”"

I guess that my bottles, which user can buy, should be restored if device is wiped. But they are none-consumable. 

And i can’t post here any logs because my app don’t even reach the state “restored”.

Thank you

Well… according to the guides I have read, the productID should be on the form “your-app-id.productname”, so if your app’s id is “com.mycompany.mygame” you should use “com.mycompany.mygame.whisky”. If it actually makes a difference I don’t know. You may also try to omit “_” in the name to see if it solves your problem.

The main problem is that when i tap restore button which has store.restore() in it, then store event state can’t even reach the “restored” state…

Sorry I kind of confused you when I said “one time”, I was talking about consumables.  You buy them, you use them and you are done.  The item gets used one time and its done.   Where as non-consumables, unlock content the user continues to use over and over.

Buy one time - use many.

Buy many, each use uses it it.

Well, consumables never get restored so that is what you should expect… If a consumable transaction wasn’t finished, it will try to do so the next time you call store.init, but then its the transactionListener that get the callback.

You product IDs should be like com.yourcompany.yourgame.whisky, but you only use whisky?