You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 22:40:24