如何让Transaction.currentEntitlements返回APP关闭时的订阅自动续期交易?
macOS自动续期订阅重启后无法获取续期交易问题排查与解决
问题描述
我在macOS应用中实现了自动续期订阅功能,通过StoreManager监听Transaction.currentEntitlements和Transaction.updates,遇到以下异常:
- APP运行期间订阅续期,
Transaction.updates能正常接收最新交易 - APP关闭期间自动续期完成,重启APP后
Transaction.currentEntitlements无法返回该续期交易,StoreKit Transactions调试窗口显示这些续期交易为未完成 - 若APP运行时再次触发续期,
Transaction.updates监听器会触发,并返回新续期交易及所有之前未完成的交易 - 补充:
Transaction.latest(for: <productId>)仅返回最新已完成的交易,不会包含自动续期产生的未完成交易
复现步骤
- 启动APP并购买自动续期订阅
- 关闭APP
- 等待订阅过期并自动续期(测试可将月度订阅设置为每30秒续期)
- 重启APP
- 检查
Transaction.currentEntitlements无返回结果
问题原因
Transaction.currentEntitlements仅返回已完成且当前有效的订阅交易,而APP关闭期间自动续期产生的交易处于未完成状态(未调用finish()标记完成),因此不会被该接口返回。当前代码仅监听currentEntitlements的异步流,未主动拉取所有历史未完成交易,导致重启后无法捕获这类续期记录。
解决方案
修改StoreManager,在初始化时主动通过Transaction.all遍历所有交易,处理APP关闭期间产生的未完成续期交易,具体代码如下:
@MainActor class StoreManager: ObservableObject { var updates: Task<Void, Never>? = nil var allTransactionsTask: Task<Void, Never>? = nil init() { updates = updatesListenerTask() allTransactionsTask = fetchAllTransactionsTask() } @Published private (set) var transaction: StoreKit.Transaction? func updatesListenerTask() -> Task<Void, Never> { Task.detached(priority: .background) { for await result in Transaction.updates { Task { @MainActor in do { let transaction = try self.validateResult(result) self.transaction = transaction await transaction.finish() } catch { print("Failed verification =\(error.localizedDescription)") } } } } } func fetchAllTransactionsTask() -> Task<Void, Never> { Task.detached(priority: .background) { do { // 遍历所有交易,处理未完成的自动续期订阅 for await result in Transaction.all { Task { @MainActor in do { let transaction = try self.validateResult(result) // 只处理未完成的自动续期订阅交易 if !transaction.isFinished && transaction.productType == .autoRenewable { self.transaction = transaction await transaction.finish() } } catch { print("Failed verification for all transactions =\(error.localizedDescription)") } } } } catch { print("Error fetching all transactions: \(error.localizedDescription)") } } } func validateResult(_ result: VerificationResult<StoreKit.Transaction>) throws -> StoreKit.Transaction { switch result { case let .verified(transaction): return transaction case .unverified: throw StoreManagerError.failedVerification } } enum StoreManagerError: Error { case failedVerification } }
关键说明
Transaction.all会返回所有交易记录(包括未完成的),APP启动时主动遍历处理,即可捕获关闭期间产生的续期交易- 通过
!transaction.isFinished和transaction.productType == .autoRenewable过滤,避免重复处理已完成交易或非订阅类交易 - 处理完成后调用
transaction.finish()标记交易完成,后续currentEntitlements就能正确返回当前有效的订阅状态
内容的提问来源于stack exchange,提问作者Ashley Mills
相关产品推荐
相关产品推荐

