【问题标题】:Verify receipt for in App purchase验证应用内购买的收据
【发布时间】:2010-11-20 21:41:38
【问题描述】:

几天来,我一直在玩应用内购买,一切正常,直到我尝试通过应用商店验证收据,因为我不断返回无效状态。

我将收据数据传递到我的 PHP 服务器,然后从那里转发到应用商店,一旦我得到有效的响应,我打算将收据数据添加到我的数据库中。

商店工具包编程指南和类参考对于这个特定领域来说并没有什么用处,因为它们并没有真正给你任何例子,我确实找到了一个有用的article,它帮助了我一点,但有些东西是还是错了。

基本上我想知道有收据验证工作的人是否愿意分享他们的代码,因为我无处可去。

谢谢

【问题讨论】:

    标签: php iphone objective-c in-app-purchase storekit


    【解决方案1】:

    首先,发布的代码中有一些拼写错误。尝试这个。 (免责声明:Refactoring et. al 留给读者作为练习!)

    - (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction {
        NSString *jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];      
        NSString *completeString = [NSString stringWithFormat:@"http://url-for-your-php?receipt=%@", jsonObjectString];               
        NSURL *urlForValidation = [NSURL URLWithString:completeString];       
        NSMutableURLRequest *validationRequest = [[NSMutableURLRequest alloc] initWithURL:urlForValidation];              
        [validationRequest setHTTPMethod:@"GET"];         
        NSData *responseData = [NSURLConnection sendSynchronousRequest:validationRequest returningResponse:nil error:nil];  
        [validationRequest release];
        NSString *responseString = [[NSString alloc] initWithData:responseData encoding: NSUTF8StringEncoding];
        NSInteger response = [responseString integerValue];
        [responseString release];
        return (response == 0);
    }
    
    - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {
        static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    
        NSMutableData *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
        uint8_t *output = (uint8_t *)data.mutableBytes;
    
        for (NSInteger i = 0; i < length; i += 3) {
            NSInteger value = 0;
            for (NSInteger j = i; j < (i + 3); j++) {
                value <<= 8;
    
                if (j < length) {
                    value |= (0xFF & input[j]);
                }
            }
    
            NSInteger index = (i / 3) * 4;
            output[index + 0] =                    table[(value >> 18) & 0x3F];
            output[index + 1] =                    table[(value >> 12) & 0x3F];
            output[index + 2] = (i + 1) < length ? table[(value >> 6)  & 0x3F] : '=';
            output[index + 3] = (i + 2) < length ? table[(value >> 0)  & 0x3F] : '=';
        }
    
        return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
    }
    

    您可以在处理您的 SKPaymentTransactionObserver 消息的类上创建这些内部方法:

    @interface YourStoreClass (Internal)
    - (BOOL)verifyReceipt:(SKPaymentTransaction *)transaction;
    - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length;
    @end
    

    注意:您可以使用 libcrypto 之类的东西来处理 base64 编码,但随后您会在应用批准时查看出口限制和额外步骤。但我离题了...

    然后,无论您打算在远程服务器上开始记录交易的何处,使用您的交易调用 verifyReceipt: 并确保它返回正面。

    同时,在您的服务器上,这里有一些超级精简的 PHP 来处理事情:

    $receipt = json_encode(array("receipt-data" => $_GET["receipt"]));
    // NOTE: use "buy" vs "sandbox" in production.
    $url = "https://sandbox.itunes.apple.com/verifyReceipt";
    $response_json = call-your-http-post-here($url, $receipt);
    $response = json_decode($response_json);
    
    // Save the data here!
    
    echo $response->status;
    

    其中 call-your-http-post-here 是您最喜欢的 HTTP 发布机制。 (cURL 是一种可能的选择。YMMV。PHP.net 有独家新闻!)

    让我稍微担心的一件事是从应用程序到服务器的 URL 中有效负载的长度(通过 GET)。我忘记了每个 RFC 是否存在长度问题。也许没关系,或者它是特定于服务器的。 (读者:欢迎对这部分提出建议!)

    将其设为同步请求也可能会有些犹豫。您可能希望异步发布它并放置 ol' UIActivityIndi​​catorView 或其他一些 HUD。恰当的例子:initWithData:encoding: 调用对我来说需要很长时间。几秒钟,这在 iPhone 领域(或其他任何在线的地方,就此而言)是一个小小的永恒。建议显示某种不确定的进度指示器。

    【讨论】:

    • 谢谢你终于让它工作了,认为我的主要问题出在我的 php 代码中。再次感谢,安迪
    • 您好,使用您在此处提供的脚本,我从服务器收到以下响应:21002...您能告诉我这是什么意思吗?我在这篇文章中看到另一个人提到了类似的问题。感谢您的宝贵时间。
    • 嗯……不能说我见过这种反应。 :( 我唯一要仔细检查的是您正在执行 HTTP post 而不是 HTTP get。也就是说,有效负载在 post数据,而不是查询字符串。
    • 您应该注意,这很容易受到使用他人收据的重放攻击。此外,您的 iPhone 应用程序容易受到服务器欺骗攻击。有关如何修复这些漏洞,请参阅此答案:stackoverflow.com/questions/1581246/…
    • 非常感谢本!我会读一下,看看我是否可以调整上面的代码来解决这些变化。
    【解决方案2】:

    只是为了再次打开它并添加我的 2 美分以换取这些表格以获取信息。

    我刚刚在我的应用程序中设置了 IAP 服务并遇到了相同的 21002 错误。我发现 21002 发生在您的 PHP 服务器的帖子为空(因此对应用商店的 HTTP 请求为空)或格式不正确时。为了让我们的工作正常运行,在 iPhone 端,我们将 NSString 中的发布数据设置为 base64 编码,然后将其作为 HTTP 请求发送到我们的服务器。

    然后在我们的服务器上,我们将它放入数组并对其进行 json 编辑。像这样:

    $receipt = json_encode(array("receipt-data"=>$_POST['receipt-data']));
    

    您会注意到它与上面的相同,只是我们使用的是 POST 而不是 GET。真的是个人喜好。

    然后我们使用 CURL 将其发布到沙箱并在响应中使用 json_decode。

    【讨论】:

    • 我在请求中使用 CURL 时遇到了问题。请求返回空白。而且我已经使用上面的目标 c 代码验证了我的 $receipt 等于 64 位编码字符串 $receipt = json_encode(array("receipt-data" => $_GET["receipt"])); $url = "sandbox.itunes.apple.com/verifyReceipt"; echo '收据:'.$receipt; $ch = curl_init(); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $receipt); curl_setopt($ch, CURLOPT_URL, 'sandbox.itunes.apple.com/verifyReceipt'); $response_json = curl_exec($ch);有什么想法吗?
    【解决方案3】:

    如果您收到空响应或错误代码,例如 21002,请尝试添加这些行。如果您检查了 curl 错误代码,则为 SSL 证书错误...

    curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt ($curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
    

    【讨论】:

    • 盲目禁用对等验证并不明智。这允许其他任何人冒充 Apple 验证服务器,安全性就是这里的练习。
    • 嗨@Kyle Poole - 你是对的。有点。不幸的是,Apple 的错误代码与 SSL 错误代码重叠,所以它可能是两者之一!很棒的计划是吗?!
    【解决方案4】:

    对于想知道如何处理在您使用应用内购买服务器模型时可能发生的连接或验证错误的任何人。收据验证可确保交易完成且成功。你不想在 iPhone 上这样做,因为你不能真正信任用户的手机。

    1. 用户发起应用内购买
    2. 完成后,应用会要求您的服务器进行验证
    3. 您向 Apple 验证收据:如果收据有效,您可以执行与购买相关的任何操作(解锁/交付内容、注册订阅...)
    4. 应用程序从队列中移除事务 (finishTransaction)

    如果服务器关闭,您不应完成事务,而是向用户显示“不可用消息”。

    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
    

    稍后会再次调用。

    但是如果您发现收据无效,您应该完成关联交易。如果没有,您可能有额外的交易永远存在于交易队列中。这意味着每次您的应用运行时,paymentQueue:updatedTransaction: 将在每个事务中调用一次...

    在我的应用程序中,收据验证是通过网络服务完成的,如果收据无效,则会返回错误代码。这就是需要外部服务器的原因。如果用户以某种方式设法跳过收据验证(通过伪造 Web 服务“成功”响应),他将无法解锁内容/访问功能,因为服务器没有购买痕迹。

    【讨论】:

      【解决方案5】:

      在与这个问题斗争了一段时间后,我终于在 Apple 的文档中找到了一个状态码列表,包括可怕的 21002(即“receipt-data 属性中的数据格式错误。”) .虽然我看到了有关此列表中未包含的其他状态代码的报告,但到目前为止,我还没有看到任何超出 Apple 记录的内容。请注意,这些代码仅适用于自动续订订阅,而不适用于其他类型的应用内购买(或文档中所说的)。

      可以在here找到相关文档。

      【讨论】:

      • 找不到页面。
      【解决方案6】:

      您必须将收据作为文件发送到您的 PHP 服务器。在您的 PHP 端,您可以使用此脚本进行验证:

      <?php
      
      $path = 'receipt'; // $_FILE['receipt-data']["tmp_name"];
      $receipt = file_get_contents($path);
      
      $json['receipt-data'] = base64_encode($receipt);
      
      $post = json_encode($json);
      
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL,"https://buy.itunes.apple.com/verifyReceipt");
      curl_setopt($ch, CURLOPT_POST,1);
      curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
      $result=curl_exec ($ch);
      
      curl_close ($ch);
      
      ?>
      

      https://gist.github.com/eduardo22i/9adc2191f71ea612a7d071342e1e4a6f

      【讨论】:

        猜你喜欢
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 2013-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多