如何在重新启动或重新安装应用程序时检查iOS中的订阅是否处于活动状态

问题描述

我有一个带订阅(不续订)的iOS应用(目标C)。 当用户重新启动应用程序时,如何检查它是否仍处于活动状态?

我已经阅读了很多有关此内容的信息,但答案似乎不清楚如何正确执行此操作。

应用启动时,我目前所拥有的是注册TransactionObserver,

IAPManager* iapManager = [[IAPManager alloc] init];
[[SKPaymentQueue defaultQueue] addTransactionObserver: iapManager];

然后,当用户进行购买时,

- (void) paymentQueue: (SKPaymentQueue *)queue updatedTransactions: (NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                [self showTransactionAsInProgress:transaction deferred:NO];
                break;
            case SKPaymentTransactionStateDeferred:
                [self showTransactionAsInProgress:transaction deferred:YES];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                [queue finishTransaction: transaction];
                break;
            case SKPaymentTransactionStatePurchased:
                [self persistPurchase: transaction];
                [queue finishTransaction: transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
                [queue finishTransaction: transaction];stopBusy];
                break;
            default:
                break;
        }
    }
}

因此,当用户首次订阅时,此方法工作正常。 但是我对您应该如何存储/跟踪此购买感到困惑。我存储在用户已订阅以启用应用程序功能的静态变量中,但是当用户重新启动应用程序时,如何检查他们是否具有有效的订阅?

我使用的代码将预订存储在iCloud或NSUserDefaults中。 在iCloud中存储根本不起作用,当他们重新启动应用程序时,他们失去了订阅。可以在NSUserDefaults中存储,但是订阅最终将过期,并且可以退款或取消。如何检查它是否处于活动状态?我可以存储订阅日期并假定持续时间,然后尝试检查一下自己,但这似乎是错误的。

如果用户卸载了该应用程序并重新安装或获得了一部新手机/手机,该怎么办?

- (void) persistPurchase: (SKPaymentTransaction*) transaction {
#if USE_ICLOUD_STORAGE
    NSUbiquitousKeyValueStore *storage = [NSUbiquitousKeyValueStore defaultStore];
#else
    NSUserDefaults *storage = [NSUserDefaults standardUserDefaults];
#endif
    if ([transaction.payment.productIdentifier isEqualToString: SUBSCRIPTION]) {
        [storage setBool: true forKey: SUBSCRIPTION];
        [IAPManager upgrade];
    }
    [storage synchronize];
    [self unlockPurchase: transaction];
}

为此,我认为我需要调用restoreCompletedTransactions。我认为不将订阅存储在NSUserDefaults中是有意义的,而是在每次应用启动时调用restoreCompletedTransactions。

但是从我阅读的内容来看,苹果似乎希望您有一个“恢复购买”按钮来执行此操作?每次应用启动时都调用它是否有意义? 这将调用您处理过的每笔付款(我认为是?)的回调,以了解这些付款是否仍处于活动状态?

我可以获取交易日期,但这不会告诉我付款是否过期,除非我假设付款未被取消并假设期限并自己检查日期?还是Apple只为您提供来自restoreCompletedTransactions的有效订阅/付款?

我还需要再次调用finishTransaction进行还原吗?

[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];

解决方法

当用户重新启动计算机时,如何检查它是否仍处于活动状态 应用程序?

https://developer.apple.com/documentation/storekit/in-app_purchase/persisting_a_purchase

对于非续订订阅,请使用iCloud或您自己的服务器保留 持久记录。

我认为最好使用服务器(这样您可以接收退款通知)而不是iCloud(您不会收到通知)。

我猜你的意思是你想检查一下:

  • 不可更新的订阅未退款
  • 订阅尚未过期(期限尚未完成)。

我假设您使用收据。这很重要,因为如果用户选择在未到期的订阅到期之前对其进行续订,您将需要它来计算订阅的持续时间。

因此,当用户首次订阅时,此方法工作正常。但是我很困惑 有关如何存储/跟踪此购买的信息。我存储在静态 用户订阅以启用应用程序功能的变量,但是 当用户重新启动应用程序时,我应该如何检查他们是否拥有 有效订阅?

我认为正确的方法是检查存储在服务器上的记录。这是因为,Apple会将退款通知发送到您在服务器上配置的URL。

此链接说明了如何通知您的服务器退款。 https://developer.apple.com/documentation/storekit/in-app_purchase/handling_refund_notifications

由于苹果不会直接通知您您的应用退款,因此您必须与服务器联系,以查看是否已经退款。。如何与服务器通信取决于您(定期轮询,推送通知等)。关键是,您应该在服务器上存储一条记录,并告知已向您的应用程序退款,以便您可以决定是否撤消该功能。 由于您需要在服务器上存储记录以检查退款,因此您也可以询问自己的服务器,订阅是否已过期。为此,您只需要在应用程序中存储一个标识符(NSUserDefaults等),以便您可以询问服务器该期限是否已过期。

这比在应用程序中简单地存储预订的结束日期要好,因为如果您以不安全的方式存储结束日期,则用户可以简单地编辑文件并继续延长结束日期。苹果在以下link中声明:

存储收据需要更多的应用逻辑,但会阻止 持久记录不会被篡改。 由于您需要实现服务器来检查退款,因此使用服务器来检查订阅是否已过期很简单。

可以在NSUserDefaults中存储,但是最终将进行订阅 已过期,可以退款或取消。如何检查是否 活性?我可以存储订阅日期并假设持续时间 并尝试检查一下自己,但这似乎是错误的。

.....

是的,我认为在您的设备上存储订阅日期不是最好的方法,原因有以下几种:

  1. 用户可以更改其设备上的时间来欺骗您的应用,然后继续使用您的订阅。完成后,他可以将时间重置为正确的时间。
  2. 如果您以不安全的方式存储订阅到期日期,并且他的手机已越狱,他可以自己编辑日期。

正确的方法是查询服务器。这样,他将无法在设备上编辑时间来欺骗您的应用扩展订阅。如果您没有安全地存储订阅日期,他将无法“编辑”订阅日期。该应用程序只需将标识符发送到您的服务器,并收到有关订阅是否处于活动状态的是或否响应。以下link说明了这样做的方法:

将收据副本连同凭据或凭证一起发送到您的服务器 标识符,以便您可以跟踪哪些收据属于 特定用户。例如,让用户对您的身份进行识别 具有用户名和密码的服务器。不要使用identifierForVendor UIDevice的属性。不同的设备为此具有不同的值 财产,因此您不能使用它来识别和恢复购买的商品 由同一用户在不同设备上进行。

..

为此,我认为我需要调用restoreCompletedTransactions。我认为 不要将订阅存储在NSUserDefaults中, 但是每次应用程序调用一次restoreCompletedTransactions 开始。

但是从我读到的内容来看,苹果似乎希望您拥有一个“恢复 购买”按钮可以执行此操作吗?仅调用它是否有意义 每次应用启动时?这将在每个 您曾经为这些付款处理过的付款(我认为是??) 知道他们是否仍然活跃?

否,在每次启动应用程序时都调用恢复购买不是一个好主意。苹果明确表示不这样做:

https://developer.apple.com/documentation/storekit/in-app_purchase/restoring_purchased_products

重要不要自动恢复购买,尤其是当您的 应用启动。恢复用户的App Store的购买提示 凭据,这会中断您的应用程序流。

它还说:

但是,在给定的条件下,应用可能需要其他方法 情况:... 您的应用使用非续订订阅-您的应用负责 恢复过程。

我认为,没有必要在每次启动应用程序时恢复购买-恢复购买旨在用于在新设备上重新安装/安装。它也不会告诉您订阅是否已退款或有效,因为如果发出退款,Apple只会通知您的服务器URL。 每次启动应用程序时,您都要查询服务器。 Apple本身表示要在服务器上更新用户帐户的余额,以识别退款滥用。

减少退款滥用,并确定重复退款的购买者 将REFUND通知映射到服务器上的玩家帐户。 监视和分析您的数据以识别可疑的退款活动。 如果您跨多个平台提供内容,请保持余额 用户帐户已在您的服务器上更新。使用App Store服务器 通知以获取接近实时状态更新 影响您客户的交易。

您应该实现“恢复购买”按钮,以处理用户卸载/重新安装应用程序/在其他设备上安装的情况。当用户恢复购买时,您应该能够计算您需要与服务器进行对话以检查订阅是否仍处于活动状态的标识符(您可以只使用原始交易ID) 您的收据将包含原始购买日期和产品ID,并且每次购买非续订订阅时都会更新。您可以通过调用刷新收据(SKReceiptRefreshRequest)来访问此数据,以便任何设备都具有收据,并且可以计算订阅期。

我可以获取交易日期,但这不能告诉我 付款是否到期,除非我认为付款没有取消,并且 假设持续时间并自己检查日期?还是苹果只给 您是否通过restoreCompletedTransactions激活了订阅/付款?

在实现订阅行为时,首次启动订阅时,应将到期日期存储在服务器上。如前所述,Apple会将退款通知发送到您的服务器,因此,如果订阅已退款或过期,则应与服务器一起检查应用程序。还请记住-用户可以更改其设备的时间/日期来绕过您在设备上存储到期日期。但是,如果您要检查自己的服务器,则他不能,因为他无法篡改您服务器上的时间。

我还需要再次调用finishTransaction进行还原吗?

我使用swift,然后迅速地只需调用restoreCompletedTransactions()。易于实现。

请参阅:How to restore in-app purchases correctly?

在这一点上,您可能想知道,如果收据中包含每笔非续订订阅的购买记录,那为什么我需要一个服务器来检查订阅是否处于活动状态?

  • 因为(对于非续订订阅)退款通知仅发送到您的服务器。收据上没有提到它们。
  • 您可以使用收据计算订阅的结束日期,但不会告诉您是否已退款。
  • 您仍然想与您的服务器进行核对,以便用户不能简单地在此设备上更改时间/日期来解决您的订阅期限。

记住:

每次用户购买非续订订阅时,都应确保服务器上的逻辑计算出正确的结束日期。例如,如果他选择在当前订阅到期之前购买非续订订阅,则应该通过仅在当前订阅结束时才开始下一个订阅来正确计算结束日期。

,

有一个用于应用内购买的轻量级iOS库,称为RMStore。 RMStore支持事务持久性并提供参考实现。您可以在下面的链接中查看它以获取更多详细信息。

https://github.com/robotmedia/RMStore

,

The documentation explicitly states表示要继续购买非续订订阅,请“使用iCloud或您自己的服务器来保存永久记录”。因此,基本上Apple没有提供内置的方式来针对用户/安装跟踪应用购买,您必须自己动手。

如您所知,在设备上存储(例如使用NUserDefaults)不是很有用,因为在同一用户,同一设备或不同设备上重新安装后,存储将无法生存。

如果您已经有一个身份提供者后端,可以适应存储和检索用户应用内购买数据,那么您可能要使用它。对于每次成功购买,请针对用户关联SKU,有效日期和收据(此时,您的服务器应检查收据是否有效以避免欺诈)。当您的应用启动或恢复时,请通过后端进行身份验证,并检索用户应有权访问的SKU,并在应用内交付内容。

如果您没有身份提供者后端,则可能要使用iCloud key-value store。然后,您可以将应用内购买关联到Apple ID,而不是每次安装。请注意,使用iCloud会有一些副作用,例如you may not be able to ever transfer your app to another organisation

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...