Hi,
Jailbroken iOS devices can innitiate fake in-app purchases in your application. This is a bit annoying situation, both for users as well as for an application owner. Maybe you can not stop fake in-app purchases but you can stop wrong analytic data based on fake purchases.
Today I'm going to explain in-app purchase validation with php in your server. For this aim, Apple provides a special service. To obtain the response data, you should first send your receipt data to Apple.
Apple link to obtain response data:
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
There are some status codes in the bottom of the page. If your response status code is one of them, there is a problem with your receipt data or your request. If the status code is 0 (zero), your receipt-data is valid.
You can get the receipt-data from your in-app purchase by following the steps bellow:
You can send the receipt data to your server with the above code block. You only change the your_server.com.
NOTE: Plus sign (+) needs to be converted by utf encoding. Becuase it is a valid code in POST requests.
Jailbroken iOS devices can innitiate fake in-app purchases in your application. This is a bit annoying situation, both for users as well as for an application owner. Maybe you can not stop fake in-app purchases but you can stop wrong analytic data based on fake purchases.
Today I'm going to explain in-app purchase validation with php in your server. For this aim, Apple provides a special service. To obtain the response data, you should first send your receipt data to Apple.
Apple link to obtain response data:
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html
21000
|
The App Store could not read the JSON object you provided.
|
21002
|
The data in the
receipt-data property was malformed or missing. |
21003
|
The receipt could not be authenticated.
|
21004
|
The shared secret you provided does not match the shared secret on file for your account.
|
21005
|
The receipt server is not currently available.
|
21006
|
This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response.
Only returned for iOS 6 style transaction receipts for auto-renewable subscriptions.
|
21007
|
This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead.
|
21008
|
This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead.
|
21010
|
This receipt could not be authorized. Treat this the same as if a purchase was never made.
|
21100-21199
|
Internal data access error.
|
You can get the receipt-data from your in-app purchase by following the steps bellow:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL]; NSData *receipt = [NSData dataWithContentsOfURL:receiptURL]; if (receipt) { NSString *receiptEncoded = [receipt base64EncodedStringWithOptions:0]; // In addition we need to replace +(plus) sign for encoding receiptEncoded = [receiptEncoded stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"]; NSString *post = [NSString stringWithFormat:@"receipt-data=%@", receiptEncoded]; NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO]; NSString *postLength = [NSString stringWithFormat:@"%lu",(unsigned long)[postData length]]; NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; [request setURL:[NSURL URLWithString:@"your_server.com/verify_inapp_ios.php"]]; [request setHTTPMethod:@"POST"]; [request setValue:postLength forHTTPHeaderField:@"Content-Length"]; [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [request setHTTPBody:postData]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; if (!error) { int statusCode = [[json objectForKey:@"status"] intValue]; if (statusCode == 0) { // Receipt-data is valid } } }]; [dataTask resume]; }
You can send the receipt data to your server with the above code block. You only change the your_server.com.
NOTE: Plus sign (+) needs to be converted by utf encoding. Becuase it is a valid code in POST requests.
We need a php page for processing this request. Apple provides 2 different URL for this aim. One for sandbox purchases and the other one for real purchases.
Sandbox: https://sandbox.itunes.apple.com/verifyReceipt
Real: https://buy.itunes.apple.com/verifyReceipt
<?php $json['receipt-data'] = $_POST['receipt-data']; $post = json_encode($json); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,"https://buy.itunes.apple.com/verifyReceipt"); curl_setopt($ch, CURLOPT_POST,1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $result=curl_exec ($ch); curl_close ($ch); ?>
Those php codes prints the response data in json format. You can deserialize the data in your objective-c or swift code.
Sample sandbox receipt-data can be found here : https://gist.github.com/sauloarruda/2559455
You can send the sandbox receipt-data to citynom.com/verify_inapp_ios.php for testing. Your json response should resemble this lines below.
Sample sandbox receipt-data can be found here : https://gist.github.com/sauloarruda/2559455
You can send the sandbox receipt-data to citynom.com/verify_inapp_ios.php for testing. Your json response should resemble this lines below.
object
|
{2}
|
receipt
|
{13}
|
original_purchase_date_pst
| : |
2012-04-30 08:05:55 America/Los_Angeles
|
original_transaction_id
| : |
1000000046178817
|
original_purchase_date_ms
| : |
1335798355868
|
transaction_id
| : |
1000000046178817
|
quantity
| : |
1
|
product_id
| : |
com.mindmobapp.download
|
bvrs
| : |
20120427
|
purchase_date_ms
| : |
1335798355868
|
purchase_date
| : |
2012-04-30 15:05:55 Etc/GMT
|
original_purchase_date
| : |
2012-04-30 15:05:55 Etc/GMT
|
purchase_date_pst
| : |
2012-04-30 08:05:55 America/Los_Angeles
|
bid
| : |
com.mindmobapp.MindMob
|
item_id
| : |
521129812
|
status
| : |
0
|