12

我正在将应用程序从 iOS6 转换为 iOS7。在我使用不推荐使用的transactionReceipt方法之前,现在我正在尝试推荐的方法来检索收据,然后在 base 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 字节。

4

4 回答 4

8

我遇到过这个问题,到处找,包括苹果的开发论坛。苹果将​​给出几个股票回复,仅此而已。我认为这是苹果方面的一个错误。在设备上进行本地验证将起作用,因此请尝试转换为该验证。如果您绝对必须使用服务器端验证,那么transactionReceipt现在似乎只能工作。

该功能只是被弃用,没有被禁止,所以我会使用它并希望Apple批准该应用程序。事实上,这就是我刚刚做的,交叉手指,等待批准。

您可以通过像这样将代码括起来来关闭 Xcode 中的警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// code using transactionReceipt
#pragma clang diagnostic pop
于 2013-10-15T16:15:22.620 回答
7

我在使用 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"}
                  ]
    }
}

印刷精美的压痕是为了我的利益,严格来说不是苹果给出的。

这是我的客户端的胆量:

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 样式收据的这段代码方面取得了成功。

于 2013-11-13T20:01:19.067 回答
3

我有同样的症状:从我自己的服务器验证 io7 样式收据时出现错误 21002 (java.lang.IllegalArgumentException)。

结果发现有两个问题:

  1. 我的收据数据很糟糕。不知何故,在将数据传递到我的服务器时,它最终在 base64 编码的收据数据中出现了一堆“\r\n”字符。(我用一些搜索和替换代码去掉了这些)。

  2. 如果您使用自动续订订阅,则必须在 JSON 有效负载中传递两个参数以验证收据:“receipt-data”和“密码”,这应该是您在 iTunes 连接中的共享密钥。

一旦我修复了这两件事,我的 verifyReceipt http 请求就会按预期工作。

于 2015-01-10T23:54:28.593 回答
0

Apple使用来自RFC 4648的 url 和文件名安全 base64,其中 -_ 作为最后两个字符。+/ 是许多实现的典型。

OP 的代码仅适用于 10.9+ 或 7.0+,这是以前的私有 API,允许支持 4.0+ 和 10.6+:

[NSData base64Encoding]
于 2014-04-17T02:58:45.707 回答