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为准。
备注:内容仅供参考。
