IOS收据验证错误21002

Joh*_*now 2 php in-app-purchase ios receipt-validation applepay

我正在尝试使用我的服务器端的收据验证.一切都很好,但有时我看到很奇怪:10次验证是可以的,但在11我得到21002错误.我不知道该怎么办.当我在启动应用程序后第一次验证收据时,有时会收到错误21002.

应用方面:

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()
}
Run Code Online (Sandbox Code Playgroud)

服务器端php脚本:

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);
Run Code Online (Sandbox Code Playgroud)

我做的一切都基于例子http://www.brianjcoleman.com/tutorial-receipt-validation-in-swift/

所以,有时我觉得应用程序发送到服务器端错误的收据和PHP脚本无法解析它,我收到21002错误状态.有什么建议吗?

Sah*_*Roy 7

代码 21002 意味着您发送给 Apple 的 JSON(包含您的共享密钥和您的收据数据)“格式错误”,或者不是 Apple 想要的格式。

这是包含后续错误代码及其含义的屏幕截图 在此输入图像描述

我就是这样做的(Objective C 和本地验证)

  #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];
}
Run Code Online (Sandbox Code Playgroud)


nul*_*ull 7

尝试从receipt字符'\n'和'\ r'中删除,并将'+'替换为'%2B',然后再将其发送到服务器.像这样的东西:

 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]];
Run Code Online (Sandbox Code Playgroud)

  • +1这是唯一提到用`%2B`替换+的答案,这是主要的罪魁祸首...接收方的PHP将+解释为一个空格(在URL语义上),破坏了base64编码 (2认同)

Jay*_*ybo 6

这都是关于 NSDataBase64EncodingOptions 的。使用类型EncodingEndLineWithCarriageReturn而不是0.

只需更改此行

let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
Run Code Online (Sandbox Code Playgroud)

到这一行

let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
Run Code Online (Sandbox Code Playgroud)

我自己试过了,它奏效了。