AppStore VerifyReceipt in Corona Store object?

hi satheesh,

thanks, but this was not the point. I just wanted to let you know that if this error gets raised, your listener crashes and the listener set in validate.start won’t get called. i fixed it, i just wanted to let you know.

Oh… ok ok! 
Can you post your fix here? I will update it in git. 

And thanks for pointing that out.  :) 

I dont have the code here right now, so i try to pseudo it from your git code.

 local function localListener(event) local response = event.response local decoded = json.decode(response) if type(decoded) == "table" then event.iTunes\_StatusCode = decoded.status --receipt data if(event.iTunes\_StatusCode == 0 or event.iTunes\_StatusCode == "0") then event.identifier = decoded.receipt.product\_id; event.bid = decoded.receipt.bid; event.iTunes\_Response = decoded; event.iTunes\_StatusCodeDescription = errorMap[tostring(event.iTunes\_StatusCode)]; else event.identifier = ""; event.bid = ""; event.iTunes\_Response = tostring(event.iTunes\_StatusCode); event.iTunes\_StatusCodeDescription = "21007"; end end listener(event); end network.request(link,"POST",localListener,{body=postData})

I think you get the idea, if there is an error, the decoded.receipt.product_id call fails. sorry for the noobish code!

I just post the code I use here (IOS only). Remember to store your receipt somewhere safe - I use AES coding and store it in a file on the device. Its not 100% hacker proof, but at least they can´t read my stored data.

Here is the code snippet that I use to check for premium subscriptions (cut-n-paste):

function validateListner(event) --json-decoded itunes Response storeResponse=json.decode(event.response) print("Validation status:"..storeResponse.status) --print("Restore table:"..table\_print (storeResponse)) -- Must go through receipt records to find important values: local idex=0 for key, value in pairs (storeResponse) do idex=idex+1 if type (value) == "table" then --print(idex..". "..key.." is a table") if key=="receipt" then -- Third record is usually the receipt table (but no certainity of that): local idex2=0 for key, value in pairs (value) do idex2=idex2+1 if key=="purchase\_date" then --scoreitems[6]=makeTimeStamp(lastchecktime) RestorePurchaseDate=string.sub(value,1,19) end if key=="expires\_date\_formatted" then RestoreExpiresDate=string.sub(value,1,19) end if key=="product\_id" then RestoreProductID=string.sub(value,1) end if key=="quantity" then RestoreQuantity=tonumber(value) end end end -- elseif "number" == type(key) then -- print(idex..". "..key.."="..tostring(value)) -- else -- print(idex..". "..key.."="..value) end end print("purchasedate:"..RestorePurchaseDate) print("expiresdate:"..RestoreExpiresDate) print("quantity:"..RestoreQuantity) print("id:"..RestoreProductID) if RestoreProductID=="com.yourcompany.yourgame.yoursubscription" then -- receipt for current subscription obtained. Check further: if RestoreQuantity\>0 and tonumber(storeResponse.status)==0 then -- more than zero products purchased and valid, so keep premium on: premiumon=true print("premium on!") premiumchk=premiumchk+1 else print("subscription not active..") premiumoff=premiumoff+1 -- May be active in another record.. end end end -- for testing: --premiumreceipt=fromhex(\*\*\*\*\*) \<-- Put a real reciept here to test (paste it from console during sandbox testing) if premiumreceipt~=nil then --if premiumreceipt~=nil then premium has been purchased so -- Check subscription (if still within subscription period) validate.start { receipt = premiumreceipt, password = \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*, -- Your store secret here listener = validateListner, testing = testingproduct, --Should be true if you use sandbox receipt, false if you use actual receipt } validtimer=timer.performWithDelay(5000, function() if premiumoff2==premiumoff then if premiumchk\>0 then premiumon=true timer.cancel(validtimer) elseif premiumoff\>0 then print("premiumon switched off by premium receipt check") premiumon=false timer.cancel(validtimer) end else premiumoff2=premiumoff end end,10) end

The code will go through all the responses from the store; E.g. even if you supply only the last recipe, iTunes store will respond with multiple transactions (if the user made several). In sandbox mode during testing it can easily become 50+. Change test account if you want to get clear of all those old transactions.

For subscriptions you want to check the purchase date and expiration date. Add the days for all those transactions and check with current date (Use first purchase date from all the valid ones to see how many days have passed with an subscription).

Hope it helps you!

What are you using to provide the AES encryption?

How are you protecting your key?

AES lua con be found here:

https://github.com/bighil/aeslua

Just remember to use the bit plugin that is integrated in corona or it will be quite slow.

Base64 implementations; well just google it and use one of them.

The key must be stored, but you can try to hide it as an AES coded string. It will never be 100% hacker proof anyway as long as you store the receipt on the device. If the program can decode it, so can a hacker as well, but it is more difficult than using plain text.

Thanks for the link.

How are you storing your key?

Also, how are you declaring your app with Apple, are you filing for a certificate for encryption?

Yes, you must sign off that the encryption is only used to store game specific data and then its ok apparently. My game “Smartball: gravity games” just hit the app store and it uses AES. You can look at the files it generates yourself and tellme what you think?

You don’t file with the government to register the app?  I haven’t done it with Apple but I was under the impression any encryption had to file the government paperwork.  I created my own encryption lib, it’s more complex obfuscation but it thwarts 99.99% of people with no need to deal with keys or encryption approval.

I can’t find the app on the app store. I assume your just saving the key somewhere on the file system without encryption and likely renaming it or something?  Unless the end user is bringing something to the table (like a password or one time that protects the key) it is likely just sitting there ready to unlock the encryption. Or you use the same passphrase to unlock the key (if it is encrypted) to then unlock the encryption.

dingo,

Any chance you could share the changes that you made to get Satheesh’s code to work for normal (ie. not auto-renewable) products?

Just started implementing in-app purchasing myself and was wondering about this verification and must admit that it is a little above my current Lua and IAP knowledge.   :slight_smile:

hi thegdog,

I might miss something… but i am using Satheesh’s code also for consumables and non-consumables.

I validate the receipt and then also check if the bundle id and product  I get is the right one.

renato.bugge,

Thanks for providing your sample code.  I borrowed some of that and it is working great.  The only thing is that I notice that it seems to only verify that one receipt.  You said that the store would send back responses for all purchases, not just the one associated with the receipt you send.

Does this mean that if you have multiple non-consumable items, and you send 1 receipt, it should verify all the purchases for each of the multiple items?  Or will it actually only verify the one non-consumable item on that receipt?  I ask because for me, it is only doing the one non-consumable item on the receipt I send and was wondering if maybe I was dong something wrong.

Thanks for any help you can provide.

dingo,

Any chance you could share the changes that you made to get Satheesh’s code to work for normal (ie. not auto-renewable) products?

Just started implementing in-app purchasing myself and was wondering about this verification and must admit that it is a little above my current Lua and IAP knowledge.   :slight_smile:

hi thegdog,

I might miss something… but i am using Satheesh’s code also for consumables and non-consumables.

I validate the receipt and then also check if the bundle id and product  I get is the right one.

renato.bugge,

Thanks for providing your sample code.  I borrowed some of that and it is working great.  The only thing is that I notice that it seems to only verify that one receipt.  You said that the store would send back responses for all purchases, not just the one associated with the receipt you send.

Does this mean that if you have multiple non-consumable items, and you send 1 receipt, it should verify all the purchases for each of the multiple items?  Or will it actually only verify the one non-consumable item on that receipt?  I ask because for me, it is only doing the one non-consumable item on the receipt I send and was wondering if maybe I was dong something wrong.

Thanks for any help you can provide.