一尘不染

验证应用内购买的收据

php

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

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

商店工具包编程指南和类引用对于这个特定领域并没有什么用,因为它们并没有真正给您提供任何示例,我确实找到了一篇有用的文章,对我有所帮助,但是仍然有些错误。

基本上,我想知道是否有人在进行收据验证工作,是否愿意在我无处可去的情况下共享他们的代码。

谢谢


阅读 291

收藏
2020-05-26

共1个答案

一尘不染

首先,发布的代码中有一些错别字。尝试这个。(免责声明:重构等人留给读者练习!)

- (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 消息的类上创建以下Internal方法:

@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- 是您最喜欢的HTTP发布机制。( cURL
是一种可能的选择。YMMV。PHP.net对此有好处!)

我有点担心的一件事是从应用程序到服务器(通过GET)的URL中有效负载的长度。我忘了每个RFC是否存在长度问题。可能还可以,或者特定于服务器。(读者:欢迎您提出建议!)

使这个同步请求也可能有些懈怠。您可能要异步发布它,并放上 UIActivityIndi​​catorView 或其他HUD。例子:
initWithData:encoding:
调用对我来说耗时很长。几秒钟,这在iPhone领域(或与此有关的其他任何地方)是永恒的。建议显示某种不确定的进度指示器。

2020-05-26