Ошибка проверки чеков IOS 21002

Я пытаюсь использовать проверку чеков с моей стороны сервера. Все в порядке, но иногда я вижу странное: 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() } 

сервер 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); 

Все, что я делаю на примере http://www.brianjcoleman.com/tutorial-receipt-validation-in-swift/

Поэтому иногда я чувствую, что приложение отправляет на серверный код неправильный квитанция, а скрипт php не анализирует его, и я получаю 21002 статус ошибки. Любое предложение?

Код 21002 означает, что JSON, который вы отправляете яблоку, который имеет ваш общий секрет, и ваши данные о квитанции «неправильно определены» или нет в том формате, в котором это хочет яблоко.

Вот скриншот с последующими кодами ошибок и их значением введите описание изображения здесь

Вот как я это сделал (Objective C и 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]; } 

Попробуйте удалить из 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]]; 

Это уже поздно, поэтому вы, возможно, решили это сейчас, но я заметил опечатку – вы оставили вне ")", где вы передаете строку в condition == "ok" :

 if let parseJSON = json { if String(parseJSON["status"]! == "ok" { //do something 

Это все о NSDataBase64EncodingOptions. Используйте тип EncodingEndLineWithCarriageReturn вместо 0 .

Просто измените эту строку

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

к этой линии

 let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn) 

Я пробовал это сам, и это сработало.