首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >StoreKit:收据验证问题

StoreKit:收据验证问题
EN

Stack Overflow用户
提问于 2020-07-15 12:07:08
回答 1查看 322关注 0票数 1

我为这件事挣扎了很长一段时间。首先,我们的问题是:在我们的应用程序中,我们有可更新的订阅和一次性购买。如果订阅仍然有效,或者是一次性购买(这是有效的生命周期),我想从收据中读取。首先有一个问题:这个文件包含什么?

代码语言:javascript
复制
NSData* receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];

我只需要检查这个文件是否存在,如果没有,我要求刷新正确吗?但是,如果订阅已经自动更新,我也需要刷新这个文件吗?或者,当我在苹果服务器上验证收据时,收据会被更新吗?

现在,我的流程如下所示,并从支付队列开始:

代码语言:javascript
复制
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    if(SANDBOX_TESTING) NSLog(@"updated transaction");
    [self refreshReceipt];
    self.transactionCount = [transactions count];
    if(SANDBOX_TESTING) NSLog(@"Number of transactions: %ld", (long)self.transactionCount);
    
    for (SKPaymentTransaction * transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction restore:NO];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                [[NSNotificationCenter defaultCenter] postNotificationName:IAPProductPurchaseStateChangedNotification object:nil];
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"Restore transaction started received");
                //Only restore transactions that haven't been restored yet AND if they have a still supported identifier
                //IMPORTANT: Original Transaction is only set (transaction.originalTransaction.transactionIdentifier) if it is a restore!
                //This first if only helps in a restore case, that not all subscription renewals get looped. If a valid subscription is found for one subscription type, the loop runs only once
                if(![self.restoredTransactions containsObject:transaction.payment.productIdentifier] && [[self getAllPurchaseIDsForPlatformType:PURCHASESONPLATFORM_IOS] containsObject:transaction.payment.productIdentifier]){
                    [self completeTransaction:transaction restore:YES];
                } else {
                    self.transactionCount--;
                }
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                //Moved from paymentQueueRestoreCompletedTransactionsFinished
                if(self.transactionCount == 0){
                    [self.delegate restoredTransactions:[self.restoredTransactions count] withReceipt:self.appleReceipt];
                }
                break;
            default:
                break;
        }
    };
}

因此,在刷新收据时,我只需这样做:

代码语言:javascript
复制
-(void)refreshReceipt{
    //TODO: Check if this file exists - if not refresh receipt (and only then...)
    NSData* receiptData = [NSData dataWithContentsOfURL:[[NSBundle mainBundle] appStoreReceiptURL]];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\", \"password\" : \"%s\"}",
                    [receiptData base64EncodedStringWithOptions:0], "xxx"];
NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];

//Sending the data to store URL based on the kind of build.
NSURL *storeURL;
if(SANDBOX_TESTING){
    storeURL = [[NSURL alloc] initWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
} else {
    storeURL = [[NSURL alloc] initWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
}

//Sending the POST request.
NSMutableURLRequest *storeRequest = [[NSMutableURLRequest alloc] initWithURL:storeURL];
[storeRequest setHTTPMethod:@"POST"];
[storeRequest setHTTPBody:payloadData];
NSError *error;
NSURLResponse *response;

NSData *data = [[NSURLSession sharedSession] sendSynchronousRequest:storeRequest returningResponse:&response error:&error];
if(error) {
    _appleReceipt = [NSArray arrayWithObjects: error, nil];
}

NSError *localError = nil;
//Parsing the response as JSON.
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&localError];

//Getting the latest_receipt_info field value.
_appleReceipt = jsonResponse[@"latest_receipt_info"];

    if(SANDBOX_TESTING) NSLog(@"Refresh Apple receipt: %@", _appleReceipt);
}

在我有收据之后,我翻看它,找出正确的购买和提取到期日,如果它是终身购买。

但是:我们有一些用户收到一条错误消息,上面说没有返回苹果收据(如果self.appleReceipt =0)是由我触发的,或者在这张收据中没有找到任何购买。但是很少的用户和我看不出他们有什么共同点,错误在哪里。在测试中,我从不出错。我也看到了一个用户的生活,谁做了终身购买,没有收据被退回,我不知道为什么。

那我的错误在哪里?我每次都要刷新收据吗?或者为什么有时我的self.appleReceipt是空的?我的过程有问题吗?

EN

回答 1

Stack Overflow用户

发布于 2021-03-05 16:44:51

当您向Apple服务器验证时,您确实会得到最新的更新,包括任何取消或过期。

有时,在购买之后生成收据可能需要一段时间,这就是为什么有时您会看到空收据数据,特别是如果您只在购买之后立即检查收据数据的话。或者,它甚至可能是用户试图黑客您的应用程序,但实际上没有收据数据。

您还应该做的是解析接收数据Apple返回,查看IAP项,以便您的可更新订阅检查它们何时到期,或者查看您一次性购买的IAP条目--您可以在这里找到关于收据中存在的数据的详细指南(以及您可能遇到的代码):

https://www.namiml.com/blog/app-store-verify-receipt-definitive-guide

请注意,您确实应该让服务器进行此收据检查和处理,因为潜在的黑客可能会拦截对Apple的调用以进行收据验证。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62914470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档