Iphone 如何签入应用程序购买自动续费订阅是否有效

Iphone 如何签入应用程序购买自动续费订阅是否有效,iphone,objective-c,ios,in-app-purchase,subscription,Iphone,Objective C,Ios,In App Purchase,Subscription,我希望通过应用内购买实现新的自动续费订阅,但我不确定如何或何时检查用户当前是否已订阅。我的理解是,当用户最初订阅应用程序时,可以使用购买日期和订阅日期来计算他们的订阅将持续多长时间。这个日期过后会发生什么?我们如何检查用户是否已自动续订或取消 如果我使用restoreCompletedTransactions获取每次续费的交易和收据,将提示用户输入其iTunes密码。这是否意味着如果他们购买了7天的订阅,他们必须在应用程序检查订阅是否仍然有效时每7天输入一次密码?如果你想从web服务器上检查订阅

我希望通过应用内购买实现新的自动续费订阅,但我不确定如何或何时检查用户当前是否已订阅。我的理解是,当用户最初订阅应用程序时,可以使用购买日期和订阅日期来计算他们的订阅将持续多长时间。这个日期过后会发生什么?我们如何检查用户是否已自动续订或取消


如果我使用
restoreCompletedTransactions
获取每次续费的交易和收据,将提示用户输入其iTunes密码。这是否意味着如果他们购买了7天的订阅,他们必须在应用程序检查订阅是否仍然有效时每7天输入一次密码?

如果你想从web服务器上检查订阅,你可以ping他们的API,它会返回自动续费订阅的状态和上次付款的信息

如果您在设备上,那么您可能必须调用restoreCompletedTransactions,我猜这需要密码


我没有看到任何其他方法。我想您可以通过与服务器端使用的相同web服务联系,从设备上验证订阅?我不知道这样做的利弊如何。

我正在围绕这个问题展开一场运动。以下是我的观察和活动:

自动续费时,应用商店调用
paymentQueue
并发布交易。该交易通过
交易过帐。transactionState==SKPaymentTransactionStateRestored

问题是,不幸的是,这只会发布到一个设备上。第二个设备无法获取发布。因此,要检测自动续费,或者更确切地说,要检测自动续费的缺失并拒绝设备继续订阅,您必须执行
restoreCompletedTransaction
或“http发布包含最后一个事务的64位编码JSON”。如果是前者,用户需要给出自己的密码;正如你在上面指出的那样,这是一种干扰。如果是后者,则需要大量额外的编码。所以,我的问题是…为什么
StoreKit
没有命令:

(不存在)
-[[SKPaymentQueue defaultQueue]restoreAttachedTransactions:(NSArray*)事务]

此命令的流程类似于
restoreCompletedTransactions
,但它只恢复附加的事务,最重要的是,它不需要用户登录。它具有与“http post包含最后一笔交易的64位编码JSON”相同的安全保护,并且它允许在
StoreKit
中完成整个应用内购买过程,而不需要web发布代码


如果这对您有意义,请建议如何将其发送到苹果……谢谢。

今天,我遇到了这个问题

下面,我用这种方式检查订阅是否过期。我的想法:用户APPLE REST API响应:(请求时间+过期时间)检查是否过期

+ (BOOL)checkInAppPurchaseStatus
{
    // Load the receipt from the app bundle.
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
    if (receipt) {
        BOOL sandbox = [[receiptURL lastPathComponent] isEqualToString:@"sandboxReceipt"];
        // Create the JSON object that describes the request
        NSError *error;
        NSDictionary *requestContents = @{
                                          @"receipt-data": [receipt base64EncodedStringWithOptions:0],@"password":@"SHARE_SECRET_CODE"
                                          };
        NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                              options:0
                                                                error:&error];

        if (requestData) {
            // Create a POST request with the receipt data.
            NSURL *storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
            if (sandbox) {
                storeURL = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
            }
            NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
            [storeRequest setHTTPMethod:@"POST"];
            [storeRequest setHTTPBody:requestData];

            BOOL rs = NO;
            //Can use sendAsynchronousRequest to request to Apple API, here I use sendSynchronousRequest
            NSError *error;
            NSURLResponse *response;
            NSData *resData = [NSURLConnection sendSynchronousRequest:storeRequest returningResponse:&response error:&error];
            if (error) {
                rs = NO;
            }
            else
            {
                NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:resData options:0 error:&error];
                if (!jsonResponse) {
                    rs = NO;
                }
                else
                {
                    NSLog(@"jsonResponse:%@", jsonResponse);

                    NSDictionary *dictLatestReceiptsInfo = jsonResponse[@"latest_receipt_info"];
                    long long int expirationDateMs = [[dictLatestReceiptsInfo valueForKeyPath:@"@max.expires_date_ms"] longLongValue];
                    long long requestDateMs = [jsonResponse[@"receipt"][@"request_date_ms"] longLongValue];
                    NSLog(@"%lld--%lld", expirationDateMs, requestDateMs);
                    rs = [[jsonResponse objectForKey:@"status"] integerValue] == 0 && (expirationDateMs > requestDateMs);
                }
            }
            return rs;
        }
        else
        {
            return NO;
        }
    }
    else
    {
        return NO;
    }
}

希望能有所帮助。

在调用Apple API之前,最好在本地验证收据。每次应用程序运行时,最好验证本地收据,如果您需要检查用户是否有任何活动订阅,您可以从本地收据检索所有购买,并查看是否有仍然活动的购买

我已经实现了一个用
Swift
编写的小型库,以简化在本地使用应用内收据的工作。您可以轻松获取表示收据的对象(
InAppReceipt
)并检索活动采购/所有采购

请随意使用

以下是解决您的问题的示例:

import TPInAppReceipt

do {
    let receipt = try InAppReceiptManager.shared.receipt()
    
    //retrive active auto renewable subscription for a specific product and date
    let purchase = receipt.activeAutoRenewableSubscriptionPurchases(ofProductIdentifier: "ProductName", forDate: Date())
    
    //retrive all auto renewable subscription purchases for a specific product
    let allAutoRenewableSubscriptionPurchases = receipt.purchases(ofProductIdentifier: "productName").filter({ return $0.isRenewableSubscription })
} catch {
    print(error)
}

虽然参加晚会的时间晚了,但由于苹果在WWDC 2020中直接提供了一个全面的后端解决方案,我不得不在这里写这篇文章

Apple发布的版本与Node JS后端配合使用,用户也可以基于其后端推出自己的版本

授权引擎的功能:

  • 它将收据
    base64
    字符串作为输入(请求参数)
  • 它将其发送到Apple
    verifyReceipt
    endpoint
  • 它解析已解码的收据字段(请参阅下面的部分字段解释)
  • 它提供订阅状态。基本上,如果subscription.AuthenticationCode>0.0,则客户有资格接收未锁定的内容
回答有关收据字段的问题:

  • 苹果
    verifyReceipt
    端点响应中的
    is_trial_period
    字段是一个标志,用户应检查以了解免费试用是否有效
  • purchase.expires\u date\u ms
    告诉订阅过期日期
  • cancellation\u date\u ms
    告知苹果支持部门何时取消了该服务
  • 具有有关
    verifyReceipt
    端点响应中各个字段的最新信息

最后但并非最不重要的一点是,如果您使用的是授权引擎示例代码,则这些字段名称将不同。因此,请注意不要混淆这两件事。

您描述的内容似乎与我发现的内容相匹配,因此我决定只实现一个服务器组件。虽然这是一种痛苦,因为它需要额外的编码,服务器组件非常简单,可能值得用于审计跟踪和收据验证。我认为交易是——你无论如何都必须使用服务器组件,因为在进行收据有效性检查时,你还必须在调用苹果服务器时提供共享机密作为参数。我认为你不应该在你的iOS应用程序客户端代码中加入共享密码,因为这样,这个密码就不再是非常机密的了……是否需要共享密码来验证自动续订订阅的沙箱和生产模式上的收据@JonnyHi你有