验证在App购买中的收据

时间:2009-08-19 10:29:17

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

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

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

商店套件编程指南和类引用对于这个特定区域来说并不是无用的,因为它们并没有真正给你任何类型的例子,我确实找到了一个有用的article帮了我一点但是事情仍然是错误的。

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

由于

9 个答案:

答案 0 :(得分:70)

首先,发布的代码中有一些拼写错误。试试这个。 (免责声明: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版本中的一个小小的永恒(或其他任何在线的事情)。显示某种不确定的进度指标可能是可取的。

答案 1 :(得分:7)

完整的源代码以及PHP实现的托管示例位于:http://www.chrismaddern.com/validate-app-store-iap-receipt-codes-online-tool/

希望它可以帮到你!

答案 2 :(得分:3)

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

  1. 用户启动应用内购买
  2. 完成后,应用程序会要求您的服务器进行验证
  3. 您使用Apple验证收据:如果它有效,您可以执行与购买相关的任何操作(解锁/交付内容,注册订阅......)
  4. 该应用程序从队列中删除了该事务(finishTransaction)
  5. 如果服务器关闭,您不应该完成交易,而是向用户显示“不可用消息”。

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

    稍后会再次调用。

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

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

答案 3 :(得分:1)

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

可以找到相关文档here

答案 4 :(得分:1)

我很惊讶没有找到Ray Wenderlich的教程 - 只是挽救了我的生命。在没有服务器的情况下验证收据(不是推荐的解决方案,但无论如何都要大量完成)。

http://www.raywenderlich.com/23266/in-app-purchases-in-ios-6-tutorial-consumables-and-receipt-validation

答案 5 :(得分:1)

您必须将收据作为文件发送到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

答案 6 :(得分:0)

再次打开这个并加上我的2美分作为回报,以鞭打这些表格以供参考。

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

然后在我们的服务器上,我们将它插入到阵列中并对其进行json-ed。像这样:

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

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

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

答案 7 :(得分:0)

这是一个很棒的库,可以满足任何人对这个主题的需求:

https://github.com/aporat/store-receipt-validator

它超越并验证:

  • iTunes
  • Play商店
  • Amazon App Store

我希望它可以帮助更多类似的人在帮助我。

答案 8 :(得分:-1)

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

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