iOS7 – квитанции, не проверяющие в песочнице – ошибка 21002 (java.lang.IllegalArgumentException)

Я конвертирую приложение из iOS6 в iOS7. Прежде чем я использовал метод устаревших transactionReceipt поэтому теперь я пытаюсь использовать рекомендуемые методы для получения квитанции, а затем кодировать в базе 64:

 NSData *working = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]]; // Tried 64 or 76 chars/line and LF or CR line endings NSString *receipt = [working base64EncodedStringWithOptions:kNilOptions]; 

Вышеупомянутое является единственным изменением кода. Ниже приведен пример того, как я проверяю его, никаких изменений:

 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSMutableString *url = [NSMutableString string]; [url appendFormat:@"%@", WEB_SERVICE]; [url appendFormat:@"receipt=%@", receipt]; NSStringEncoding encoding; NSError *error = [NSError new]; NSURL *URL = [NSURL URLWithString:url]; NSString *json = [NSString stringWithContentsOfURL:URL usedEncoding:&encoding error:&error]; // check json and error // ... code omitted } 

На стороне сервера это код PHP, который я использую для проверки получения, никаких изменений, кроме попыток песочницы для любой ошибки:

 // Encode as JSON $json = json_encode(array('receipt-data' => $receipt)); // Try production first, if it doesn't work, then try the sandbox $working = postJSONToURL('https://buy.itunes.apple.com/verifyReceipt', $json, false); error_log('production - '.print_r($working, true)); if (@$working['status'] !== 0) // === 21007) $working = postJSONToURL('https://sandbox.itunes.apple.com/verifyReceipt', $json, true); error_log('sandbox - '.print_r($working, true)); 

Это вывод журнала ошибок:

 production - Array\n(\n [status] => 21002\n [exception] => java.lang.IllegalArgumentException\n)\n sandbox - Array\n(\n [status] => 21002\n [exception] => java.lang.IllegalArgumentException\n)\n 

Похоже, я бросаю всевозможные исключения в Apple!

Опять же, единственное различие заключается в том, как получение и кодирование получают. Кто-нибудь столкнулся с этой проблемой и исправил ее?

Спасибо за прочтение.

/ Ю.Р.

По запросу код для PostJSONToURL:

 function postJSONToURL($url, $json, $disableSSLVerify = false) { $resource = curl_init($url); curl_setopt($resource, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($resource, CURLOPT_POSTFIELDS, $json); curl_setopt($resource, CURLOPT_RETURNTRANSFER, true); curl_setopt($resource, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json', 'Content-Length: '.strlen($json))); curl_setopt($resource, CURLOPT_HEADER, 0); if ($disableSSLVerify) { curl_setopt($resource, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($resource, CURLOPT_SSL_VERIFYPEER, 0); } //curl_setopt($resource, CURLOPT_VERBOSE, true); //curl_setopt($resource, CURLOPT_STDERR, $fp = fopen('/tmp/curl_output'.rand(1000, 9999).'.txt', 'w')); $contents = json_decode(curl_exec($resource), true); if (!$contents) $contents = array(); curl_close($resource); //fclose($fp); return $contents; } 

Новые подробности после некоторых экспериментов определили, что отправка существующих данных в кодировке base 64, вероятно, будет посягательством на некоторый внутренний предел. Если он превышает некоторый внутренний предел, данные даже не отправляются, он не работает локально на устройстве, ниже этого, он отправляется. Столбцы: формат данных, размер кодированных данных, независимо от того, достиг ли он сервера:

 raw receipt data 5K N/A base64 no options 6.66K yes base64 76 chars/line 6.75K no base64 64 chars/line 6.77K no hex coded 10K no 

Обратите внимание, что разница между успешной отправкой и отправкой составляет менее 100 байт.

Solutions Collecting From Web of "iOS7 – квитанции, не проверяющие в песочнице – ошибка 21002 (java.lang.IllegalArgumentException)"

У меня была эта проблема и выглядела повсюду, в том числе на форумах разработчиков Apple. Apple даст пару ответов на акции, и все. Я думаю, что это ошибка на стороне Apple. Валидация локально на устройстве будет работать, поэтому попробуйте конвертировать в это. Если вы абсолютно должны использовать проверку на стороне сервера, похоже, сейчас работает только transactionReceipt .

Функция просто устарела, а не запрещена, поэтому я просто использую ее и надеюсь, что Apple одобрит приложение. Фактически, это то, что я только что сделал, скрестив пальцы, ожидая одобрения.

Вы можете отключить предупреждение в Xcode, скопировав свой код следующим образом:

 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" // code using transactionReceipt #pragma clang diagnostic pop 

У меня есть успех с квитанциями iOS7, полученными из пакета приложений, используя:

 NSURL *receiptURL = [[NSBundle mainBundle] performSelector:@selector(appStoreReceiptURL)]; receipt = [NSData dataWithContentsOfURL:receiptURL]; 

Ответ на мой сервер от сервера Apple до получения стиля iOS7 значительно отличается от того, который был получен до предыдущего стиля получения. Вот пример подтвержденной квитанции:

 {"status":0, "environment":"Sandbox", "receipt": {"receipt_type":"ProductionSandbox", "adam_id":0, "bundle_id":"<snip>", "application_version":"1.0", "download_id":0, "request_date":"2013-11-12 01:43:06 Etc\/GMT", "request_date_ms":"1384220586352", "request_date_pst":"2013-11-11 17:43:06 America\/Los_Angeles", "in_app":[ {"quantity":"1", "product_id":"<snip>", "transaction_id":"1000000092978110", "original_transaction_id":"1000000092978110", "purchase_date":"2013-11-12 01:36:49 Etc\/GMT", "purchase_date_ms":"1384220209000", "purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles", "original_purchase_date":"2013-11-12 01:36:49 Etc\/GMT", "original_purchase_date_ms":"1384220209000", "original_purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles", "is_trial_period":"false"} ] } } 

Печально отпечатанный отпечаток был для моей выгоды, а не строго говоря, как дано Apple.

Вот мужество моей клиентской стороны:

 NSMutableDictionary *parameters = [NSMutableDictionary dictionary]; [parameters addEntriesFromDictionary:[credentials dictionary]]; // receipt is an object of my own making, but base64String just returns an // NSString representation of the receipt data. parameters[PURCHASE_RECEIPT] = [receipt base64String]; NSURLRequest *request = [[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:urlString parameters:parameters]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; operation.responseSerializer = [AFJSONResponseSerializer serializer]; <snip> [operation start]; 

И вот суть того, что я использую на стороне сервера, где URL-адрес является сервером проверки производства или песочницы:

 // Some code from http://stackoverflow.com/questions/5647461/how-do-i-send-a-post-request-with-php private static function validateReceipt($receiptData, $URL) { // Connect to Apple server and validate. $data = json_encode(array("receipt-data" => $receiptData)); // use key 'http' even if you send the request to https://... // This: 'content' => http_build_query($data), // seems to generate an error (21002) $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded", 'method' => 'POST', 'content' => $data ), ); $context = stream_context_create($options); $result = file_get_contents($URL, false, $context); //Utils::writeToLog("done validateReceipt: " . $result); // See http://php.net/manual/en/function.file-get-contents.php // for the use of === comparison. if ($result === FALSE) { return NULL; } else { // Decode the result as an associative array. return json_decode($result, true); } } 

У меня есть успех с этим кодом с iOS6 и iOS7.

У меня были те же симптомы: ошибка 21002 (java.lang.IllegalArgumentException) при проверке квитанций типа io7 с моего собственного сервера.

Оказалось, что есть две проблемы:

  1. Мои данные квитанции были плохими. Так или иначе, передавая данные на мой сервер, у них получилось множество символов «\ r \ n» в базе данных, закодированных в базе64. (Я удалил их с помощью кода поиска и замены).

  2. Если вы используете автоматическое обновление подписки, тогда вы должны передать два параметра в полезной нагрузке JSON, чтобы проверитьReceipt: «получение-данные», а также «пароль», который должен быть вашим общим секретом от itunes connect.

После того, как я исправил эти две вещи, мои запросы на проверку подлинности HTTP выполнялись, как ожидалось.

Apple использует исходный код и имя файла safe64 из RFC 4648, у которого есть -_ в качестве двух последних символов. + / является типичным для многих реализаций.

Код OP работает только с 10.9+ или 7.0+, вот ранее закрытый API, который поддерживает поддержку 4.0+ и 10.6+:

 [NSData base64Encoding]