I\'m trying to use receipt validation with my server side. Everything is ok, but sometimes I see strange: 10 times validation is OK, but on 11 i get 21002 error. I dont know
Try removing from receipt
the characters '\n' and '\r' and replacing '+' with'%2B' before sending it to the server. Something like this:
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptDataString = [receipt base64EncodedStringWithOptions:0];
receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
receiptDataString=[receiptDataString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
NSString *postDataString = [NSString stringWithFormat:@"receipt-data=%@", receiptDataString];
NSString *length = [NSString stringWithFormat:@"%lu", (unsigned long)[postDataString length]];
[request setValue:length forHTTPHeaderField:@"Content-Length"];
[request setHTTPBody:[postDataString dataUsingEncoding:NSASCIIStringEncoding]];
This is late so you might well have solved this now - but i noticed a typo - you left out a ")" where you cast to a string in the condition == "ok"
:
if let parseJSON = json {
if String(parseJSON["status"]! == "ok" {
//do something
The Receipt Data is already base64 encoded. Refer to Receipt Validation Programming Guide
$receipt_data = "MII.................KY\/6oc9w==";
$data = "{\"receipt-data\":\"$receipt_data\"}";
$url = "https://buy.itunes.apple.com/verifyReceipt"; // if use this to test sandbox will return "{"status":21007}"
//$url = "https://sandbox.itunes.apple.com/verifyReceipt"; // for sandbox
var_dump(post($url,$data));
function post($url, $data, $headerArray = array())
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST,FALSE);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
if (array() === $headerArray)
curl_setopt($curl, CURLOPT_HTTPHEADER,["Content-type:application/json;charset='utf-8'","Accept:application/json"]);
$output = curl_exec($curl);
curl_close($curl);
return $output;
}
Code 21002 means that the JSON you are sending to apple which has your shared secret and your receipt data is "misformed" or not in the format apple wants it.
Here is a screenshot with the subsequent error codes and their meaning
This is how i did it (Objective C and Local Validation)
#define kAppReceipt @"LATEST_RECEIPT"
#define kStoreKitSecret @"YOUR SHARED SECRET"
#define kSandboxServer @"https://sandbox.itunes.apple.com/verifyReceipt"
-(void)loadProducts{
NSError *error;
if(![[NSUserDefaults standardUserDefaults]objectForKey:kAppReceipt]){
NSURL *recieptURL = [[NSBundle mainBundle]appStoreReceiptURL];
NSError *recieptError ;
BOOL isPresent = [recieptURL checkResourceIsReachableAndReturnError:&recieptError];
if(!isPresent){
return;
}
NSData *recieptData = [NSData dataWithContentsOfURL:recieptURL];
if(!recieptData){
return;
}
payLoad = [NSMutableDictionary dictionaryWithObject:[recieptData base64EncodedStringWithOptions:0] forKey:@"receipt-data"];
}
else {
[payLoad setObject:[[NSUserDefaults standardUserDefaults]objectForKey:kAppReceipt] forKey:@"receipt-data"];
}
[payLoad setObject:kStoreKitSecret forKey:@"password"];
NSData *requestData = [NSJSONSerialization dataWithJSONObject:payLoad options:0 error:&error];
NSMutableURLRequest *sandBoxReq = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kSandboxServer]];
[sandBoxReq setHTTPMethod:@"POST"];
[sandBoxReq setHTTPBody:requestData];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:sandBoxReq completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(!error){
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
NSString * latestReceipt = [jsonResponse objectForKey:@"latest_receipt"];
// this is the latest receipt that you should store in NSUSER DEFAULT to then later sent this same receipt when you make this same call
[[NSUserDefaults standardUserDefaults] setObject:latestReceipt forKey:kAppReceipt];
}
}] resume];
}
It's all about the NSDataBase64EncodingOptions. Use type EncodingEndLineWithCarriageReturn
instead of 0
.
Simply change this line
let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
to this line
let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
I tried this myself and it worked.