IOS receipt validation error 21002

喜夏-厌秋 提交于 2019-11-30 20:40:52

问题


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 what to do. Sometimes I get error 21002 when I validate receipt first time after launch app.

App side:

func validateReceipt(productID: String) {

    let receipt = NSData(contentsOfURL: NSBundle.mainBundle().appStoreReceiptURL!)!

    let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))

    let request = NSMutableURLRequest(URL: NSURL(string: "my_server_url")!)

    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    request.HTTPBody = receiptdata.dataUsingEncoding(NSUTF8StringEncoding)

    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in

        let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves) as? NSDictionary

        if (error != nil) {
            print(error!.localizedDescription)
            let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
            print("Error could not parse JSON: '\(jsonStr)'")
        }
        else {
            if let parseJSON = json {
                 if String(parseJSON["status"]! == "ok" {
                     //do something
                     print("Validate OK")
                        }else{
                            print("Validate NOK")
                    }
            }
            else {
                let jsonStr = NSString(data: data!, encoding: NSUTF8StringEncoding)
                print("Receipt Error: \(jsonStr)")
            }
        }
    })

    task.resume()
}

server side php script:

function getReceiptData($receipt)
{
$endpoint = 'https://sandbox.itunes.apple.com/verifyReceipt';

$ch = curl_init($endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$errmsg = curl_error($ch);
curl_close($ch);
$msg = $response.' - '.$errno.' - '.$errmsg;
echo $response;
}

foreach ($_POST as $key=>$value){
$newcontent .= $key.' '.$value;
}

$new = trim($newcontent);
$new = trim($newcontent);
$new = str_replace('_','+',$new);
$new = str_replace(' =','==',$new);

if (substr_count($new,'=') == 0){
if (strpos('=',$new) === false){
    $new .= '=';
}
}

$new = '{"receipt-data":"'.$new.'"}';
$info = getReceiptData($new);

Everything I do based on example http://www.brianjcoleman.com/tutorial-receipt-validation-in-swift/

So, sometimes I feel that app send to serverside wrong receipt and php script cant parse it and I receive 21002 error status. Any suggestion?


回答1:


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]];



回答2:


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.




回答3:


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];
}



回答4:


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



回答5:


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;
}


来源:https://stackoverflow.com/questions/32836058/ios-receipt-validation-error-21002

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!