PAYATHON 2026

iOS SDK 中如何用 RMStore 验证自动续订订阅状态?

数据小陈

结论

不要用 isPurchasedProductOfIdentifier: 判断自动续订订阅当前是否有效。这个方法只能确认用户购买过该产品,不会检查订阅是否已经到期。

RMStore 可以根据 App Store receipt 创建 RMAppReceipt。检查指定产品在当前时间是否处于有效订阅期,可以这样写:

RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];

BOOL isActive =
    [receipt containsActiveAutoRenewableSubscriptionOfProductIdentifier:
        @"com.fanfun.apptestsubscription1"
        forDate:[NSDate date]];

完整示例

#import <RMStore/RMStore.h>
#import <RMStore/RMAppReceipt.h>

- (BOOL)isSubscriptionActive
{
    NSString *productIdentifier =
        @"com.fanfun.apptestsubscription1";

    RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];

    if (receipt == nil) {
        return NO;
    }

    return [receipt
        containsActiveAutoRenewableSubscriptionOfProductIdentifier:
            productIdentifier
        forDate:[NSDate date]];
}

调用方式:

if ([self isSubscriptionActive]) {
    // Subscription is active today.
    [self enableSubscriptionFeatures];
} else {
    // Subscription is expired, cancelled, or unavailable in the receipt.
    [self disableSubscriptionFeatures];
}

[NSDate date] 返回当前时间。该方法会在 receipt 的内购记录中查找对应的 product identifier,再根据订阅记录的有效期判断该时间点是否仍在订阅周期内。

使用单条订阅记录检查

如果需要检查 receipt 中的具体记录,也可以遍历 inAppPurchases,对每条记录调用问题中提到的 isActiveAutoRenewableSubscriptionForDate::

#import <RMStore/RMStore.h>
#import <RMStore/RMAppReceipt.h>
#import <RMStore/RMAppReceiptIAP.h>

- (BOOL)isSubscriptionActive
{
    NSString *productIdentifier =
        @"com.fanfun.apptestsubscription1";

    RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];
    NSDate *today = [NSDate date];

    for (RMAppReceiptIAP *purchase in receipt.inAppPurchases) {
        if ([purchase.productIdentifier isEqualToString:productIdentifier] &&
            [purchase isActiveAutoRenewableSubscriptionForDate:today]) {
            return YES;
        }
    }

    return NO;
}

通常可以优先使用 containsActiveAutoRenewableSubscriptionOfProductIdentifier:forDate:,它已经封装了遍历过程。

Receipt 不存在时刷新

在新安装、恢复购买或测试环境中,本地 App Store receipt 可能不存在,也可能尚未更新。遇到这种情况,不应立即把 nil 视为订阅已过期,可以先请求刷新 receipt:

#import <RMStore/RMStore.h>
#import <RMStore/RMAppReceipt.h>

- (void)checkSubscriptionWithCompletion:
    (void (^)(BOOL active, NSError *error))completion
{
    NSString *productIdentifier =
        @"com.fanfun.apptestsubscription1";

    void (^checkReceipt)(void) = ^{
        RMAppReceipt *receipt = [RMAppReceipt bundleReceipt];

        BOOL active =
            [receipt
                containsActiveAutoRenewableSubscriptionOfProductIdentifier:
                    productIdentifier
                forDate:[NSDate date]];

        completion(active, nil);
    };

    if ([RMAppReceipt bundleReceipt] != nil) {
        checkReceipt();
        return;
    }

    [[RMStore defaultStore]
        refreshReceiptOnSuccess:^{
            checkReceipt();
        }
        failure:^(NSError *error) {
            completion(NO, error);
        }];
}

刷新 receipt 时可能会要求用户登录 App Store,因此不适合在每次启动或每次进入页面时无条件调用。一般先读取本地 receipt,只有在 receipt 缺失或业务确实需要更新时才进行刷新。

注意事项

  • isPurchasedProductOfIdentifier: 适合检查一次性购买状态,不能用来判断自动续订订阅是否到期。
  • API 和类型的正确拼写是 RMAppReceipt、RMAppReceiptIAP,不是 Receipe。
  • receipt 中可能有同一订阅的多次续订记录,不能只检查第一条购买记录。
  • 本地检查可以用于控制界面和一般功能访问,但高价值权益不应只依赖设备时间和客户端逻辑。更稳妥的做法是在服务端通过 App Store Server API 或相关通知维护订阅状态。
  • Sandbox 订阅的续订和过期周期与正式环境不同,检查代码相同。
  • 如果当前使用的 RMStore 版本没有上述聚合方法,可以遍历 receipt.inAppPurchases。不同版本的头文件可能略有差异,应以项目实际引入的 RMAppReceipt.h 为准。

备注:内容仅供参考。