我正在向我的应用添加 iOS 应用内订阅。我正在关注 Ray Wenderlich 页面上的这个很棒的教程:
https://www.raywenderlich.com/659-in-app-purchases-auto-renewable-subscriptions-tutorial
基本上本教程遵循以下步骤:
- 购买订阅
- 收到收据
- 在 Apple 中验证收据(这应该通过后端完成,但为简单起见,本教程在应用程序中进行)。
- 一旦收据被验证,Apple 就会以 JSON 解析的收据作为响应。
- 应用程序将购买的商品存储在本地,并将它们与 sessionID(创建的 UUID)相关联。
完成本教程后,似乎一切正常,但我在 2 个问题上遇到了困难。
每次用户进入应用程序时,都会检查它是否具有有效的 sessionID 和有效的收据:
guard SubscriptionService.shared.currentSessionId != nil,
SubscriptionService.shared.hasReceiptData else {
showRestoreAlert()
return
}
loadSelfies()
hasReceiptData 这样做:
var hasReceiptData: Bool {
return loadReceipt() != nil
}
private func loadReceipt() -> Data? {
guard let url = Bundle.main.appStoreReceiptURL else {
return nil
}
do {
let data = try Data(contentsOf: url)
return data
} catch {
print("Error loading receipt data: \(error.localizedDescription)")
return nil
}
}
}
这些方法只检查收据是否存在,没有别的。
要检查 currentSessionId,这是方法:
var currentSessionId: String? {
didSet {
NotificationCenter.default.post(name: SubscriptionService.sessionIdSetNotification, object: currentSessionId)
}
}
而 currentSessionId 的值是在 Apple 响应收据验证时设置的:
func uploadReceipt(completion: ((_ success: Bool) -> Void)? = nil) {
if let receiptData = loadReceipt() {
SelfieService.shared.upload(receipt: receiptData) { [weak self] (result) in
guard let strongSelf = self else { return }
switch result {
case .success(let result):
strongSelf.currentSessionId = result.sessionId
strongSelf.currentSubscription = result.currentSubscription
completion?(true)
case .failure(let error):
print(" Receipt Upload Failed: \(error)")
completion?(false)
}
}
}
}
好吧,我的疑问是:
每次用户进入应用程序时,它只检查 currentSessionId 和收据是否存在,没有别的,所以如果它们存在但订阅已过期,应用程序不知道它并且用户在没有任何活动订阅的情况下继续使用它。我应该怎么做才能避免这种情况?将解析的 JSON 收据本地存储在 UserDefaults 中并检查到期日期?
而且由于 currentSessionId 不是永久存储的,如果我杀死应用程序并重新启动它,currentSessionId 为空,第一次检查失败,用户无法使用该应用程序。管理此问题的最佳方法是什么?再次在 UserDefaults 中本地存储 currentSessionID?
编辑
看了一些类似的问题,好像每次用户进入应用,在检查currentSessionID和receipt之前,我都要验证本地存储的receipt,是这样吗?