Подтвердить что ты не робот

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!

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

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

/YR

В соответствии с запросом код для 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 байт.

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

У меня есть успех с квитанциями 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.

Ответ 3

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

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

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

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

Как только я исправил эти две вещи, мои проверенные HTTP-запросы Confirm работали, как ожидалось.

Ответ 4

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

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

[NSData base64Encoding]