In App Purchase Problems

I have a non-consumable set up in my app to remove the ads, and I want to allow the user to restore the ads. The first time I purchase it works, but for whatever reason, I’m having trouble restoring my purchase.
When I call store.restore(), it immediately acts like I had restored the purchase, even if that test user account had not yet bought the IAP. I’ve been having this problem for a while, and I would love to get it fixed is anyone knows what the problem might be.
Thanks,
Peter

EDIT: It seems that store.init() calls the callback 4 times, with the transaction.state “restored”. However, when this happens, the productIdentifier is nil. When I try to call store.restore(), it doesn’t call the callback listener. [import]uid: 38000 topic_id: 31518 reply_id: 331518[/import]

could you share some code? [import]uid: 90610 topic_id: 31518 reply_id: 125975[/import]

Even if I cut and paste from the sample code it doesnt work…
Anyways, here’s some code:

----------------------------------------------------------------------------------  
--  
-- restore.lua  
--  
----------------------------------------------------------------------------------  
   
local storyboard = require( "storyboard" )  
local scene = storyboard.newScene()  
require"store"  
local widget = require'widget'  
widget.setTheme( "theme\_ios" )  
local function printT(txt) -- Print as alerts, comment out for release  
 native.showAlert("alert", txt, {"OK"})  
 print(txt)  
end  
  
local listOfProducts =   
{  
 -- 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   
 "1000Coins",  
 "5000c.noAds",  
}  
local 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  
  
local function transactionCallback( event )  
 local infoString  
  
 -- Log transaction info.  
 printT("transactionCallback: Received event " .. tostring(event.name))  
 printT("state: " .. tostring(event.transaction.state))  
 printT("errorType: " .. tostring(event.transaction.errorType))  
 printT("errorString: " .. tostring(event.transaction.errorString))  
  
 if event.transaction.state == "purchased" then  
 infoString = "Transaction successful!"  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 elseif event.transaction.state == "restored" then  
 -- Reminder: your app must store this information somewhere  
 -- Here we just display some of it  
 if event.transaction.productIdentifier == "5000c.noAds" then  
 printT("noAds was purchased")  
 --SAVE NO ADS TO USER SETTINGS  
 end  
 infoString = "Restoring transaction:" ..  
 "\n Original ID: " .. tostring(event.transaction.originalTransactionIdentifier) ..  
 "\n Original date: " .. tostring(event.transaction.originalDate)  
 printT(infoString)  
 printT("productIdentifier: " .. tostring(event.transaction.productIdentifier))  
 printT("receipt: " .. tostring(event.transaction.receipt))  
 printT("transactionIdentifier: " .. tostring(event.transaction.transactionIdentifier))  
 printT("date: " .. tostring(event.transaction.date))  
 printT("originalReceipt: " .. tostring(event.transaction.originalReceipt))  
 printT(settings:retrieve("noAds"))  
  
 elseif event.transaction.state == "cancelled" then  
 infoString = "Transaction cancelled by user."  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 elseif event.transaction.state == "failed" then   
 infoString = "Transaction failed, type: " ..   
 tostring(event.transaction.errorType) .. " " .. tostring(event.transaction.errorString)  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 else  
 infoString = "Unknown event"  
 printT(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  
  
-- Called when the scene's view does not exist:  
function scene:createScene( event )  
 local group = self.view  
 local title = display.newEmbossedText( group, "Restore Purchases", 0, 0, native.systemFontBold, 26 )  
 title:setReferencePoint( display.CenterReferencePoint )  
 title.x = 240  
 title.y = 20  
 local aboutText1 = display.newRetinaText( "If you have previously purchased the 'remove ads' in app purchase, tap 'restore' to restore your purchase and remove the ads. If you have any questions, feel free to contact us!", 20, 30, 440, 300, "Trebuchet MS", 16 )  
 aboutText1:setReferencePoint( display.TopCenterReferencePoint )  
 aboutText1.x = 240  
 aboutText1.y = 40  
 group:insert(aboutText1)  
  
 local function emailUs(event)  
 --Send email to support@bluesprucegames.com  
 print("contact button pressed")  
 local options =  
 {  
 to = "support@bluesprucegames.com",  
 subject = "Ads Removal Request for Sharks Vs. Subs"  
 }  
 native.showPopup("mail", options)  
 end  
 local function restoreAds(event)  
 store.restore()  
 end  
 local function exitScreen(event)  
 storyboard.gotoScene("menu")  
 end  
 local contactButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Contact Us",  
 onRelease = emailUs  
 }  
 group:insert(contactButton)  
 contactButton.x, contactButton.y = 240, 290  
 local restoreButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Restore",  
 onRelease = restoreAds  
 }  
 group:insert(restoreButton)  
 restoreButton.x, restoreButton.y = 90, 290  
 local exitButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Exit",  
 onRelease = exitScreen  
 }  
 group:insert(exitButton)  
 exitButton.x, exitButton.y = 390, 290  
end  
   
   
-- Called immediately after scene has moved onscreen:  
function scene:enterScene( event )  
 local group = self.view  
 store.loadProducts( listOfProducts, loadProductsCallback )  
 store.init( "apple", transactionCallback )  
  
 if storyboard.getPrevious() then  
 storyboard.removeScene(storyboard.getPrevious())  
 end  
  
end  
   
   
-- Called when scene is about to move offscreen:  
function scene:exitScene( event )  
 local group = self.view  
end  
   
   
-- Called prior to the removal of scene's "view" (display group)  
function scene:destroyScene( event )  
 local group = self.view  
 end  
   
-- "createScene" event is dispatched if scene's view does not exist  
scene:addEventListener( "createScene", scene )  
   
-- "enterScene" event is dispatched whenever scene transition has finished  
scene:addEventListener( "enterScene", scene )  
   
-- "exitScene" event is dispatched before next scene's transition begins  
scene:addEventListener( "exitScene", scene )  
   
-- "destroyScene" event is dispatched before view is unloaded, which can be  
-- automatically unloaded in low memory situations, or explicitly via a call to  
-- storyboard.purgeScene() or storyboard.removeScene().  
scene:addEventListener( "destroyScene", scene )  
   
---------------------------------------------------------------------------------  
   
return scene  

If you want a testUser account that already has the purchase bought (so you can try to restore it), email me at developer(AT)bluesprucegames(DOT)com

UPDATE: I’ve tested it further and found that whenever I call store.init, it calls the callback with state “restored” and productIdentifier “5000c.noAds”, which removes the ads no matter what account you have. I can be logged out of all accounts in settings, and it will still remove the ads when I call store.init(). Does anyone have any suggestions on how to fix this?

UPDATE 2: I just tested this on a new iPhone 5 and it worked just fine. I have absolutely no idea why it worked on an iPhone and not on the first device (a 4th gen iPod Touch), but I’ll do some more testing and (hopefully) this problem will be unique to my iPod Touch. [import]uid: 38000 topic_id: 31518 reply_id: 126163[/import]

could you share some code? [import]uid: 90610 topic_id: 31518 reply_id: 125975[/import]

Even if I cut and paste from the sample code it doesnt work…
Anyways, here’s some code:

----------------------------------------------------------------------------------  
--  
-- restore.lua  
--  
----------------------------------------------------------------------------------  
   
local storyboard = require( "storyboard" )  
local scene = storyboard.newScene()  
require"store"  
local widget = require'widget'  
widget.setTheme( "theme\_ios" )  
local function printT(txt) -- Print as alerts, comment out for release  
 native.showAlert("alert", txt, {"OK"})  
 print(txt)  
end  
  
local listOfProducts =   
{  
 -- 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   
 "1000Coins",  
 "5000c.noAds",  
}  
local 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  
  
local function transactionCallback( event )  
 local infoString  
  
 -- Log transaction info.  
 printT("transactionCallback: Received event " .. tostring(event.name))  
 printT("state: " .. tostring(event.transaction.state))  
 printT("errorType: " .. tostring(event.transaction.errorType))  
 printT("errorString: " .. tostring(event.transaction.errorString))  
  
 if event.transaction.state == "purchased" then  
 infoString = "Transaction successful!"  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 elseif event.transaction.state == "restored" then  
 -- Reminder: your app must store this information somewhere  
 -- Here we just display some of it  
 if event.transaction.productIdentifier == "5000c.noAds" then  
 printT("noAds was purchased")  
 --SAVE NO ADS TO USER SETTINGS  
 end  
 infoString = "Restoring transaction:" ..  
 "\n Original ID: " .. tostring(event.transaction.originalTransactionIdentifier) ..  
 "\n Original date: " .. tostring(event.transaction.originalDate)  
 printT(infoString)  
 printT("productIdentifier: " .. tostring(event.transaction.productIdentifier))  
 printT("receipt: " .. tostring(event.transaction.receipt))  
 printT("transactionIdentifier: " .. tostring(event.transaction.transactionIdentifier))  
 printT("date: " .. tostring(event.transaction.date))  
 printT("originalReceipt: " .. tostring(event.transaction.originalReceipt))  
 printT(settings:retrieve("noAds"))  
  
 elseif event.transaction.state == "cancelled" then  
 infoString = "Transaction cancelled by user."  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 elseif event.transaction.state == "failed" then   
 infoString = "Transaction failed, type: " ..   
 tostring(event.transaction.errorType) .. " " .. tostring(event.transaction.errorString)  
 printT(infoString)  
 descriptionArea.text = infoString  
  
 else  
 infoString = "Unknown event"  
 printT(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  
  
-- Called when the scene's view does not exist:  
function scene:createScene( event )  
 local group = self.view  
 local title = display.newEmbossedText( group, "Restore Purchases", 0, 0, native.systemFontBold, 26 )  
 title:setReferencePoint( display.CenterReferencePoint )  
 title.x = 240  
 title.y = 20  
 local aboutText1 = display.newRetinaText( "If you have previously purchased the 'remove ads' in app purchase, tap 'restore' to restore your purchase and remove the ads. If you have any questions, feel free to contact us!", 20, 30, 440, 300, "Trebuchet MS", 16 )  
 aboutText1:setReferencePoint( display.TopCenterReferencePoint )  
 aboutText1.x = 240  
 aboutText1.y = 40  
 group:insert(aboutText1)  
  
 local function emailUs(event)  
 --Send email to support@bluesprucegames.com  
 print("contact button pressed")  
 local options =  
 {  
 to = "support@bluesprucegames.com",  
 subject = "Ads Removal Request for Sharks Vs. Subs"  
 }  
 native.showPopup("mail", options)  
 end  
 local function restoreAds(event)  
 store.restore()  
 end  
 local function exitScreen(event)  
 storyboard.gotoScene("menu")  
 end  
 local contactButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Contact Us",  
 onRelease = emailUs  
 }  
 group:insert(contactButton)  
 contactButton.x, contactButton.y = 240, 290  
 local restoreButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Restore",  
 onRelease = restoreAds  
 }  
 group:insert(restoreButton)  
 restoreButton.x, restoreButton.y = 90, 290  
 local exitButton = widget.newButton{  
 width = 140,   
 height = 44,  
 label = "Exit",  
 onRelease = exitScreen  
 }  
 group:insert(exitButton)  
 exitButton.x, exitButton.y = 390, 290  
end  
   
   
-- Called immediately after scene has moved onscreen:  
function scene:enterScene( event )  
 local group = self.view  
 store.loadProducts( listOfProducts, loadProductsCallback )  
 store.init( "apple", transactionCallback )  
  
 if storyboard.getPrevious() then  
 storyboard.removeScene(storyboard.getPrevious())  
 end  
  
end  
   
   
-- Called when scene is about to move offscreen:  
function scene:exitScene( event )  
 local group = self.view  
end  
   
   
-- Called prior to the removal of scene's "view" (display group)  
function scene:destroyScene( event )  
 local group = self.view  
 end  
   
-- "createScene" event is dispatched if scene's view does not exist  
scene:addEventListener( "createScene", scene )  
   
-- "enterScene" event is dispatched whenever scene transition has finished  
scene:addEventListener( "enterScene", scene )  
   
-- "exitScene" event is dispatched before next scene's transition begins  
scene:addEventListener( "exitScene", scene )  
   
-- "destroyScene" event is dispatched before view is unloaded, which can be  
-- automatically unloaded in low memory situations, or explicitly via a call to  
-- storyboard.purgeScene() or storyboard.removeScene().  
scene:addEventListener( "destroyScene", scene )  
   
---------------------------------------------------------------------------------  
   
return scene  

If you want a testUser account that already has the purchase bought (so you can try to restore it), email me at developer(AT)bluesprucegames(DOT)com

UPDATE: I’ve tested it further and found that whenever I call store.init, it calls the callback with state “restored” and productIdentifier “5000c.noAds”, which removes the ads no matter what account you have. I can be logged out of all accounts in settings, and it will still remove the ads when I call store.init(). Does anyone have any suggestions on how to fix this?

UPDATE 2: I just tested this on a new iPhone 5 and it worked just fine. I have absolutely no idea why it worked on an iPhone and not on the first device (a 4th gen iPod Touch), but I’ll do some more testing and (hopefully) this problem will be unique to my iPod Touch. [import]uid: 38000 topic_id: 31518 reply_id: 126163[/import]