@xnailbender
Eep sorry about that. I think I added that “echo $hex” for testing.
Are you sure removing that makes the code work properly? If so, I’ll remove it from the code ( don’t have the time to test it) [import]uid: 64174 topic_id: 30371 reply_id: 124138[/import]
Satheesh wrote:
Are you sure removing that makes the code work properly?
Yes, when the line “echo $hex”, located near the top of the script, is commented out/removed, your code and template executes properly.
There is 1 issue I have not resolved yet though. It has to do with the “testing” variable.
When developing in the Sandbox, the iTunes Sandbox URL is called from the script properly in the PHP script. When app was approved though, my IAP did not execute. As the code and PHP script worked throughout testing and Approval, I figured the live iTunes URL was not being called in the script, so I commented out the
//if ($testing == 1)
//$url = 'https://sandbox.itunes.apple.com/verifyReceipt';
//else
$url = 'https://buy.itunes.apple.com/verifyReceipt';
to remove the Sandbox URL from the PHP script and the IAP verification code worked again.
Somehow when integrating the IAP verification code into my app, I am not setting “testing = 0”. I’m sure I did something wrong, just not sure what. I’m not sure how to check if app is “live” or in Sandbox" mode. My work around to comment out the “if” statement when my app goes live works ok, I just have to pay attention to when my app goes live so I can modify my PHP as soon as possible.
I want to give you a BIG 2 Thmbs Up for all the code you have made available for Corona peeps! You have saved me literally months of struggle on the learning curve. I can’t believe how many times your name & code snipets come up when searching for solutions to issues I have encountered.
Figuring out what was wrong in the PHP script forced me to learn some basic PHP coding though, which was the launchpad and template for me to integrate my app with my server. This has enabled me to use PHP to integrate my own homegrown analytics and dynamically load keys and variables needed if the user has an internet connection. It’s crazy what can be accomplished with PHP app integration.
I’ve modified and used your IAP verification code as the template for all my PHP integration.
Thank you so much.
Nail [import]uid: 106779 topic_id: 30371 reply_id: 124210[/import]
Ha ha! thanks for the compliments!
Regarding the testing part, try setting testing as nil or false instead of setting it to 0.
[import]uid: 64174 topic_id: 30371 reply_id: 124216[/import]
Can you guys please try to help me with my previous question ?
Or maybe post your transaction callback code so I can see how did you act if the verifyPurchase failed ?
Thnx!
[import]uid: 108334 topic_id: 30371 reply_id: 124217[/import]
FWIW, I’m a newb, here’s how I handle the cracked purchase.
The listener(event) gets called when the receipt is either verified as valid with a status == 0 from Apple or is flagged as invalid with any other status response error code.
I do this in the listener(event):
local function listener(event) --Place execution here depending on "response"
if event.iTunes\_StatusCode == 0 then
--supply whatever is purchased here, coin, credits, levels whatever and then call store.finish in that function.
elseif event.iTunes\_StatusCode ~= "0" then --this is any other Apple return status than "0"
timer.performWithDelay(400, transactionFailed) --I call an alert popup saying "TransactionError"
store.finishTransaction( event.transaction )
--I call store.finish as the cracked app expects, but do not supply the purchased items.
--They will not get charged by Apple for the purchase as the purchase never went to iTunes as
-- it was a fraudulent purchase in the first place and I could care less if they did get charged: :)
end
end
I’m not sure if this is the proper handling of a cracked purchase or not, but it seems to work. A valid purchase gets the purchased items and a fraudulent purchase get NOTHING!
I’d be glad to hear what others do.
Hope this helps,
Nail [import]uid: 106779 topic_id: 30371 reply_id: 124229[/import]
@Satheesh: am I right, i only need to validate the transaction.receipt in the purchased event of the purchase listener, and if its ok, i unlock the content?
that simple with your script, simply put the script on the server, install json, verify the receipt, put the generated secret in the itunes of the app into?
really that easy? [import]uid: 90610 topic_id: 30371 reply_id: 124235[/import]
Thank you for your insight.
That is exactly what I do but I don’t
call store.finishTransaction( event.transaction )
if the verify on my server fails.
I think if you call the finishTransaction, next time the users enters your purchase screen he can just buy (or hit restore) and he will get the content as far as I remember.
Or do you have verify in the restore event too? [import]uid: 108334 topic_id: 30371 reply_id: 124240[/import]
I’m sorry, I don’t have all the info you need.
I only have consumables with IAP, so I don’t have a restore button/function and I don’t have to deal with the complexities linked to non-consumables.
I do believe you want to verify the receipt when a user clicks the restore button for non-consumables. This will prevent the most commonly used crack I believe.
Information on preventing piracy is hard to find, basically no one wants to inform hackers/pirates on their techniques, because they would use this information to circumvent the anti-piracy techniques they have integrated.
I believe there are 2 main cracks used and 1 app specific crack/hack where the targeted app is literally hacked at the code level, but due to the work required is least common, but is the most difficult to deter.
The most common crack is the one used by the Russian Hacker a few months back for the first time with NON jailbroken devices and used in jailbroken devices for quite awhile. It is widely used because it targets all apps for vulnerability, it is not app specific. This is where the store.function call to the AppStore URL’s are intercepted and redirected to a simulated AppStore server either loaded on the device or on a remote server. The simulated server returns a fake and invalid receipt which the IAP callback accepts blindly as valid.
This is where the developer should init the IAP verification routine, send the receipt to your server, your server forwards it to the AppStore for validity, the AppStore will return an invalid status code because the receipt is garbage. I’ve read it is guessed that 99% of all apps do not take the extra step to verify the receipts on an external server, this is why the crack is so successful and widely used.
Verifying a new purchase receipt is fairly straightforward and understandable to me. I have coded and can make this work.
Restoring a purchase is quite a different animal in my view.
If a user clicks on a purchase item button to “restore” it to their device, an already purchased non-consumable, a single receipt will be returned by the AppStore (valid) and the crack (invalid). This would be handled exactly the same as an original purchase and the IAP Verification routine would invalidate a cracked receipt.
The store.restore() called by the restore button is where my eyes glaze over and I’m way out of my league.
I believe there are 2 different methods to pursue here.
I believe when the store.restore() is called, the AppStore returns a copy of all the non-consumable receipts the user has purchased specific to the app. I can understand how to verify a single returned receipt, if the user only made 1 purchase of a non-consumable, but I do not understand how to verify and restore multiple purchases/receipts returned at once. I’m not sure of the timing and mechanics of the restore receipt delivery. I realize the restore callback would fire, just not sure how it handles a rapid sequence delivery of receipts.
The separate receipts would have to be gathered/stored as they are received.
Mehod 1 would be to verify each receipt individually through the IAP verification routine I would think. I am not sure how that would be done exactly, so I can’t give you the code. Then when the receipt is validated, call the function to restore the productIdentifier in the receipt.
Method 2 would be to store each original purchase receipt when first transacted and compare them to each restore receipt received. If they match, call the function to restore the productIdentifier.
Since I have not done a successful restore of multiple purchased products, if would be great if someone that has successfully coded it would share any insights.
I am not addressing the more sophisticated addition to this crack/hack where the crack re-uses/ returns a valid receipt from the AppStore that has been obtained from a valid purchase. I believe Apple has implemented a safegaurd against this. They have added a new identifier to AppStore receipt, it is the “unique_identifier”. No one really knows if this is a hash of the UDID or a new UUID, but it seems Apple is aware of the receipt recycling technique and is comparing it to the AppleID associated to it during the IAP Server Verification process. This is not confirmed, but only makes sense as this was a KEY missing piece of the AppStore receipt/process.
Good Luck,
Nail [import]uid: 106779 topic_id: 30371 reply_id: 124317[/import]
@nail: I didn’t read the whole post but about the restore… as far as i know, you get the listener called for every non consumable the user has bought! so basically, its exactly the same as in the purchased event of the listener, but within the restored event. if i am right, you simply have to check within your purchased and restored event if the receipt is valid. if so, unlock content/save whatever you want to do.
sounds pretty straight forward, maybe sath can confirm this? [import]uid: 90610 topic_id: 30371 reply_id: 124328[/import]
@Satheesh: in your defined listener of the validate.start method, have u access to the productIdentifier etc? How did you solve that?
I simply wanted to validate within the purchased event and get the valid state, but it seems as i have to unlock the content in the listener of validate.start, right?? [import]uid: 90610 topic_id: 30371 reply_id: 124504[/import]
I don’t remember. But the listener returns the exact response as returned by the apple server as event.respons.
Just print event.response in the listener and see. I think it would be a json encoded table of values. [import]uid: 64174 topic_id: 30371 reply_id: 124514[/import]
thanks, that’s all i wanted to know
best, dingo [import]uid: 90610 topic_id: 30371 reply_id: 124518[/import]
OK, I managed to get the proper receipt.
Now the problem is there is no store.rollback (or something similar).
Why do I need this ?
If the user uses the crack method, the transaction.event is still “purchased”
if event.transaction.state == "purchased" then
verifyPurchase(event.transaction.receipt)
but the verifyPurchase detects it is a crack and returns false.
At this moment I would need to call store.rollback.
Now the transaction stays open (I can’t call store.finishTransaction because that would validate the purchase) which causes problems because next time the user visits the store I get the message “The item is already purchased but hasn’t been downloaded” (or somethig similar).
How did you manage this scenario ?
How to rollback the purchase ?
Regards,
Damir. [import]uid: 108334 topic_id: 30371 reply_id: 124137[/import]
@xnailbender
Eep sorry about that. I think I added that “echo $hex” for testing.
Are you sure removing that makes the code work properly? If so, I’ll remove it from the code ( don’t have the time to test it) [import]uid: 64174 topic_id: 30371 reply_id: 124138[/import]
Satheesh wrote:
Are you sure removing that makes the code work properly?
Yes, when the line “echo $hex”, located near the top of the script, is commented out/removed, your code and template executes properly.
There is 1 issue I have not resolved yet though. It has to do with the “testing” variable.
When developing in the Sandbox, the iTunes Sandbox URL is called from the script properly in the PHP script. When app was approved though, my IAP did not execute. As the code and PHP script worked throughout testing and Approval, I figured the live iTunes URL was not being called in the script, so I commented out the
//if ($testing == 1)
//$url = 'https://sandbox.itunes.apple.com/verifyReceipt';
//else
$url = 'https://buy.itunes.apple.com/verifyReceipt';
to remove the Sandbox URL from the PHP script and the IAP verification code worked again.
Somehow when integrating the IAP verification code into my app, I am not setting “testing = 0”. I’m sure I did something wrong, just not sure what. I’m not sure how to check if app is “live” or in Sandbox" mode. My work around to comment out the “if” statement when my app goes live works ok, I just have to pay attention to when my app goes live so I can modify my PHP as soon as possible.
I want to give you a BIG 2 Thmbs Up for all the code you have made available for Corona peeps! You have saved me literally months of struggle on the learning curve. I can’t believe how many times your name & code snipets come up when searching for solutions to issues I have encountered.
Figuring out what was wrong in the PHP script forced me to learn some basic PHP coding though, which was the launchpad and template for me to integrate my app with my server. This has enabled me to use PHP to integrate my own homegrown analytics and dynamically load keys and variables needed if the user has an internet connection. It’s crazy what can be accomplished with PHP app integration.
I’ve modified and used your IAP verification code as the template for all my PHP integration.
Thank you so much.
Nail [import]uid: 106779 topic_id: 30371 reply_id: 124210[/import]
Ha ha! thanks for the compliments!
Regarding the testing part, try setting testing as nil or false instead of setting it to 0.
[import]uid: 64174 topic_id: 30371 reply_id: 124216[/import]
Can you guys please try to help me with my previous question ?
Or maybe post your transaction callback code so I can see how did you act if the verifyPurchase failed ?
Thnx!
[import]uid: 108334 topic_id: 30371 reply_id: 124217[/import]
FWIW, I’m a newb, here’s how I handle the cracked purchase.
The listener(event) gets called when the receipt is either verified as valid with a status == 0 from Apple or is flagged as invalid with any other status response error code.
I do this in the listener(event):
local function listener(event) --Place execution here depending on "response"
if event.iTunes\_StatusCode == 0 then
--supply whatever is purchased here, coin, credits, levels whatever and then call store.finish in that function.
elseif event.iTunes\_StatusCode ~= "0" then --this is any other Apple return status than "0"
timer.performWithDelay(400, transactionFailed) --I call an alert popup saying "TransactionError"
store.finishTransaction( event.transaction )
--I call store.finish as the cracked app expects, but do not supply the purchased items.
--They will not get charged by Apple for the purchase as the purchase never went to iTunes as
-- it was a fraudulent purchase in the first place and I could care less if they did get charged: :)
end
end
I’m not sure if this is the proper handling of a cracked purchase or not, but it seems to work. A valid purchase gets the purchased items and a fraudulent purchase get NOTHING!
I’d be glad to hear what others do.
Hope this helps,
Nail [import]uid: 106779 topic_id: 30371 reply_id: 124229[/import]
@Satheesh: am I right, i only need to validate the transaction.receipt in the purchased event of the purchase listener, and if its ok, i unlock the content?
that simple with your script, simply put the script on the server, install json, verify the receipt, put the generated secret in the itunes of the app into?
really that easy? [import]uid: 90610 topic_id: 30371 reply_id: 124235[/import]
Thank you for your insight.
That is exactly what I do but I don’t
call store.finishTransaction( event.transaction )
if the verify on my server fails.
I think if you call the finishTransaction, next time the users enters your purchase screen he can just buy (or hit restore) and he will get the content as far as I remember.
Or do you have verify in the restore event too? [import]uid: 108334 topic_id: 30371 reply_id: 124240[/import]