iOS Question In-App Purchases Receipt validation (SOLVED)

walterf25

Expert
Licensed User
Longtime User
Hello All, i have a need to validate the receipts from in-app purchases, i have found lots of threads here in the forums about this but i don't find a conclusive answer, i have also search google and have found various php scripts, and have tried a few of them, but none of them seem to work.

I followed Ere's code to get the receipt information from the product with the following code:

B4X:
Dim no As NativeObject = Product
                Dim b() As Byte = no.NSDataToArray(no.GetField("transactionReceipt"))
That code gives me the following string after i purchase a subscription that I'm testing using the Sandbox
{
"signature" = "A6THysyqaesU8PrBitz7MC2wCG7TEUJnxRb1qzwBjjiueVj3vFuxEmIxRAjhrBS5bk3ogv8S0fkl61APJHFCh4ILz6gz7P6Zzmzln9ffbvJvzt/QxlqJah8xY2LtA2BFmprxYFRg8va1+8DgXldrGkL0rPSjkCRU95WGTQdrADC942WJ0TXTAiKJPq5FE0ZnCSc/N+BDKApQFXBOhPIQjfl1SzANJcV6vg2iVhQ8ycqX3eXcN12Y0QPwyH1UUm+5z3J6mIVbkLPwfBHtNMB3IvPHwLm+AR3RM5j18aGvODHMmFfTEvpgiGdb97qYTRArUZTgNCF3zz48N4gfoMiEpVAAAAWAMIIFfDCCBGSgAwIBAgIIDutXh+eeCY0wDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTUxMTEzMDIxNTA5WhcNMjMwMjA3MjE0ODQ3WjCBiTE3MDUGA1UEAwwuTWFjIEFwcCBTdG9yZSBhbmQgaVR1bmVzIFN0b3JlIFJlY2VpcHQgU2lnbmluZzEsMCoGA1UECwwjQXBwbGUgV29ybGR3aWRlIERldmVsb3BlciBSZWxhdGlvbnMxEzARBgNVBAoMCkFwcGxlIEluYy4xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApc+B/SWigVvWh+0j2jMcjuIjwKXEJss9xp/sSg1Vhv+kAteXyjlUbX1/slQYncQsUnGOZHuCzom6SdYI5bSIcc8/W0YuxsQduAOpWKIEPiF41du30I4SjYNMWypoN5PC8r0exNKhDEpYUqsS4+3dH5gVkDUtwswSyo1IgfdYeFRr6IwxNh9KBgxHVPM3kLiykol9X6SFSuHAnOC6pLuCl2P0K5PB/T5vysH1PKmPUhrAJQp2Dt7+mf7/wmv1W16sc1FJCFaJzEOQzI6BAtCgl7ZcsaFpaYeQEGgmJjm4HRBzsApdxXPQ33Y72C3ZiB7j7AfP4o7Q0/omVYHv4gNJIwIDAQABo4IB1zCCAdMwPwYIKwYBBQUHAQEEMzAxMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLXd3ZHIwNDAdBgNVHQ4EFgQUkaSc/MR2t5+givRN9Y82Xe0rBIUwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSIJxcJqbYYYIvs67r2R1nFUlSjtzCCAR4GA1UdIASCARUwggERMIIBDQYKKoZIhvdjZAUGATCB/jCBwwYIKwYBBQUHAgIwgbYMgbNSZWxpYW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRlIHBvbGljeSBhbmQgY2VydGlmaWNhdGlvbiBwcmFjdGljZSBzdGF0ZW1lbnRzLjA2BggrBgEFBQcCARYqaHR0cDovL3d3dy5hcHBsZS5jb20vY2VydGlmaWNhdGVhdXRob3JpdHkvMA4GA1UdDwEB/wQEAwIHgDAQBgoqhkiG92NkBgsBBAIFADANBgkqhkiG9w0BAQUFAAOCAQEADaYb0y4941srB25ClmzT6IxDMIJf4FzRjb69D70a/CWS24yFw4BZ3+Pi1y4FFKwN27a4/vw1LnzLrRdrjn8f5He5sWeVtBNephmGdvhaIJXnY4wPc/zo7cYfrpn4ZUhcoOAoOsAQNy25oAQ5H3O5yAX98t5/GioqbisB/KAgXNnrfSemM/j1mOC+RNuxTGf8bgpPyeIGqNKX86eOa1GiWoR1ZdEWBGLjwV/1CKnPaNmSAMnBjLP4jQBkulhgwHyvj3XKablbKtYdaG6YQvVMpzcZm8w7HHoZQ/Ojbb9IYAYMNpIr7N4YtRHaLSPQjvygaZwXG56AezlHRTBhL8cTqA==";
"purchase-info" = "ewoJIm9yaWdpbmFsLXB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE5LTEyLTIzIDIyOjM5OjIwIEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwoJInVuaXF1ZS1pZGVudGlmaWVyIiA9ICI3ZDc3Yzc4MmE4YmZhOGQyNzhiNTRiMjVhZjQwYTkwMTczYzQxYmU0IjsKCSJvcmlnaW5hbC10cmFuc2FjdGlvbi1pZCIgPSAiMTAwMDAwMDYwODQ0NzIzOSI7CgkiYnZycyIgPSAiMS4wLjAiOwoJInRyYW5zYWN0aW9uLWlkIiA9ICIxMDAwMDAwNjA4NjgxMTM3IjsKCSJxdWFudGl0eSIgPSAiMSI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZS1tcyIgPSAiMTU3NzE2OTU2MDAwMCI7CgkidW5pcXVlLXZlbmRvci1pZGVudGlmaWVyIiA9ICIwRkEwQkY2QS00NTgxLTQ1ODgtQUI1Qi1BQURBNjNFMzEyNjYiOwoJInByb2R1Y3QtaWQiID0gIk0xMjEyOTAyIjsKCSJpdGVtLWlkIiA9ICIxNDkyNzQ3MTQzIjsKCSJ2ZXJzaW9uLWV4dGVybmFsLWlkZW50aWZpZXIiID0gIjAiOwoJImlzLWluLWludHJvLW9mZmVyLXBlcmlvZCIgPSAiZmFsc2UiOwoJInB1cmNoYXNlLWRhdGUtbXMiID0gIjE1NzcyMzE2MzI4MDIiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMTktMTItMjQgMjM6NTM6NTIgRXRjL0dNVCI7CgkiaXMtdHJpYWwtcGVyaW9kIiA9ICJmYWxzZSI7Cgkib3JpZ2luYWwtcHVyY2hhc2UtZGF0ZSIgPSAiMjAxOS0xMi0yNCAwNjozOToyMCBFdGMvR01UIjsKCSJiaWQiID0gImdlbmVzaXMuYTFyZWFkZXIiOwoJInB1cmNoYXNlLWRhdGUtcHN0IiA9ICIyMDE5LTEyLTI0IDE1OjUzOjUyIEFtZXJpY2EvTG9zX0FuZ2VsZXMiOwp9";
"environment" = "Sandbox";
"pod" = "100";
"signing-status" = "0";
}

One of the PHP scripts i found is this one:
B4X:
<?php

$json['receipt-data'] = $_POST['receipt-data'];

$post = json_encode($json);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://sandbox.itunes.apple.com/verifyReceipt");
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
$result=curl_exec ($ch);

curl_close ($ch);

?>

I have placed that script in my server, and when i call a Post function with the iHTTPUtils library, I get a response but not the expected response, I get the following response:
{"status":21199}
According to Apple's documentation a 21199 response means that there's an Internal data access error.
sometimes I also get a 21002 Response which means The data in the receipt-data property was malformed or missing.

My question is, has any of you guys who has already dealt with this found a practical solution, and how did you guys deal with it, I know there is a post of someone who has already accomplished this but has not posted his solution.

What should the data passed to the php script look like?
In the string received with Erel's code I see that there are some base64 encoded strings, and in one of the php script I see that one of the things they are doing first is base64 encoding the receipt information, but how does that work, if there are already base64 encoded strings, are they re-encoding the same information two times?

I'm so confused, someone Help!

Thanks,
Walter
 

hatzisn

Well-Known Member
Licensed User
Longtime User
If you try with http get (httpjob.download) sometimes a huge amount of data gets cut. If I were you I would encodebase64 the json with stringutils, use post in the http job, decodebase64 in php side and then follow the given path... Please respond if this solved your problem. I would be really interested...
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
If you try with http get (httpjob.download) sometimes a huge amount of data gets cut. If I were you I would encodebase64 the json with stringutils, use post in the http job, decodebase64 in php side and then follow the given path... Please respond if this solved your problem. I would be really interested...
the method needs to be a POST method, not Download, i have already tried everything you mentioned, the issue is not in my code, at least I don't think, thanks for your reply.

Walter
 
Upvote 0

hatzisn

Well-Known Member
Licensed User
Longtime User
I am not familiar with php but it would be useful to check if the communication is done using utf8. In .NET this is set manually in web.config. It could be the fact that what you send is translated differently in the php side...
 
Upvote 0

walterf25

Expert
Licensed User
Longtime User
I have solved this issue, please check this tutorial thread if anyone would like know how to verify the in-app purchases transaction receipt.
Receipt Validation tutorial

Walter
 
Upvote 0
Top