Ошибка проверки квитанции 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 Любое предложение?

1

Решение

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

Другие решения

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

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

Вот как я это сделал (Цель 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];
}
2

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

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

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

к этой линии

let receiptdata = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)

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

2

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

if let parseJSON = json {
if String(parseJSON["status"]! == "ok" {
//do something
0
По вопросам рекламы [email protected]