iOS7 - receipts not validating at sandbox - error 21002 (java.lang.IllegalArgumentException)
I've had this problem and looked everywhere, including on Apple's development forums. Apple will give a couple of stock replies, and that's it. I think it's a bug on Apple's side. Validation locally on the device will work, so try to convert to that. If you absolutely must use server side validation, only transactionReceipt
seems to work right now.
The function is just deprecated, not banned, so I would just use it and hope Apple approves of the app. In fact, it's what I just did, fingers crossed, waiting for approval.
You can turn off the warning in Xcode by bracketing your code like this:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// code using transactionReceipt
#pragma clang diagnostic pop
Validating in-app purchase via jquery -- 21002 errors
For posterity,
When you use jQuery's post method, the string it forms from the second argument JavaScript object is not a JSON literal. Instead, it probably is doing something like this:
{
"receipt-data" : "< receipt data here >"
}
becomes
"receipt-data=< receipt data here >"
almost like a GET request.
However, when you use JSON.stringify(), you get a JSON literal from a JavaScript object.
In this respect, Apple's validation server is a bit non-standard, since it expects POST data to be formatted as JSON instead of the older GET-style string.
iOS 7 receipt validation issue on PHP server
Here is the code I use to send the receipt directly to the Apple servers.
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
[self checkTransactionWithAppleUseProduction:productionWebsite andData:receiptData];
NSString *string64=[receiptData base64EncodedStringWithOptions:kNilOptions];
NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}",string64];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
NSString *serverURL;
if(productionWebsite)serverURL= @"https://buy.itunes.apple.com/verifyReceipt";
if(!productionWebsite)serverURL=@"https://sandbox.itunes.apple.com/verifyReceipt";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverURL]];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:payloadData];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[conn start];
equivalent to transaction.transactionReceipt.bytes
iOS6 and iOS7 receipts are different. Apple changed the receipt format when going to iOS7 in an effort to improve security. Take a look at: Can Purely On-Device In-App Purchase Receipt Validation Be Done With iOS6? and iOS7 - receipts not validating at sandbox - error 21002 (java.lang.IllegalArgumentException).
If you are talking about using these two methods on iOS7, in my tests, both return iOS7 style receipts. I'm not sure if the receipts are byte by byte identical, however.
Note that transactionReceipt is deprecated in iOS7.
Keep getting {status:21002} while verifying Apple In-App Purchase receipt
Two problems exist in your code:
- You need to use
base64EncodedStringWithOptions
instead ofbase64EncodedDataWithOptions
. The former as you can see returns a data hash, not a base64 encoded string which Apple requires. - Since you're posting JSON to Apple you need to set the
Content-type
toapplication/json
instead ofapplication/x-www-form-urlencoded;charset=UTF-8
.
In App Purchase Receipt verification within app
After number of tries, I decided to do the receipt verification from server side. Actually this is the recommended way.
Here is my code,
-(void)recordTransaction:(SKPaymentTransaction *)transaction {
NSString* receiptString = [[[NSString alloc] initWithData:transaction.transactionReceipt encoding:NSUTF8StringEncoding] autorelease];
// POST this string to your server
// I used ASIFormDataRequest
}
// server side
$url = 'https://sandbox.itunes.apple.com/verifyReceipt';
// encode the receipt data received from application
$purchase_encoded = base64_encode( $purchase_receipt );
//Create JSON
$encodedData = json_encode( Array(
'receipt-data' => $purchase_encoded
) );
// POST data
//Open a Connection using POST method, as it is required to use POST method.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $encodedData);
$encodedResponse = curl_exec($ch);
curl_close($ch);
//Decode response data using json_decode method to get an object.
$response = json_decode( $encodedResponse );
// check response
if ($response->{'status'} != 0)
// Invalid receipt
else
// valid reciept
I found help form,
http://gamesfromwithin.com/in-app-purchases-part-3
iOS InApp Purchase Receipt Validation iOS 7
In what I've seen, iOS7 doesn't change the need for whether or not you should do receipt validation, just how receipt validation is possible. iOS7 has enabled receipt validation on the device (e.g., see link from https://stackoverflow.com/users/1226963/rmaddy above, and see A complete solution to LOCALLY validate an in-app receipts and bundle receipts on iOS 7).
Does on-device receipt validation add extra security? It seems to me that it does. It gives you one more tool with which to secure your purchases. In my app (yet to be released), I want to support iOS6 and iOS7 so I decided to have a back-end server to do receipt validation for the iOS6 case. And since I have that server in place, for iOS7 receipts, I do on-device validation first and if that succeeds, I do server validation as a second check.
Whether or not you do receipt validation (in iOS5, iOS6, iOS7 etc) really depends on how much your security means to you. If you don't have much in the way of security needs, then why spend much time on security. If you do, then do more.
What if Apple changes the format of the receipts? Well, of course, this can and likely will happen. Given that the iOS6 to iOS7 change restructured receipts and in-app purchases considerably, it seems we should expect iOS8 to do so again. That's the future. Deal with what we have now.
Related Topics
PHP E-Mail Form Sender Name Instead of E-Mail
Get Key of Multidimensional Array
Include PHP File into HTML File
Laravel 5.2: the Process Class Relies on Proc_Open, Which Is Not Available on Your PHP Installation
Does the Condition After && Always Get Evaluated
iOS Push Notification Does Not Work When Using Crontab Scheduler
Check If Links Are Broken in PHP
Request Exceeded the Limit of 10 Internal Redirects
Change Cart Total Price in Woocommerce
Get the Metadata of an Order Item in Woocommerce 3
I am Confused About PHP Post/Redirect/Get
Handling Multiple File Uploads in Sonata Admin Bundle
Post Arrays Not Showing Unchecked Checkboxes