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

使用StoreKit 2时订阅到期未收到交易更新的问题

问题:StoreKit 2自动续订订阅到期无法触发交易更新

我正在用StoreKit 2实现自动续订订阅,但订阅到期时收不到交易更新。需求是通过监听钩子更新用户已购产品,目前在Xcode里通过取消订阅并等待到期测试。

我用Transaction.updates监听交易更新,也参考了苹果官方内购实现示例。

监听交易更新的代码:

func listenForTransactions() -> Task<Void, Error> {
    return Task.detached {
        //Iterate through any transactions that don't come from a direct call to `purchase()`.
        for await result in Transaction.updates {
            do {
                let transaction = try self.checkVerified(result)
                
                //Deliver products to the user.
                await self.updateCustomerProductStatus()
                
                //Always finish a transaction.
                await transaction.finish()
            } catch {
                //StoreKit has a transaction that fails verification. Don't deliver content to the user.
                print("Transaction failed verification")
            }
        }
    }
}

更新已购订阅的代码:

@MainActor
func updateCustomerProductStatus() async {
    var purchasedProducts: [Product] = []
    
    // Iterate through all of the user's purchased products.
    for await result in Transaction.currentEntitlements {
        do {
            // Check whether the transaction is verified. If it isn’t, catch `failedVerification` error.
            let transaction = try checkVerified(result)
            
            // Check the `productType` of the transaction and get the corresponding product from the store.
            switch transaction.productType {
            case .autoRenewable:
                if let revocationDate = transaction.revocationDate {
                    //do nothing if transaction was revoked
                    print("Transaction was revoked on \(revocationDate)")
                    return
                } else if let expirationDate = transaction.expirationDate,
                            expirationDate < Date() {
                    // Do nothing, this subscription is expired.
                    return
                } else {
                    if let subscription = subscriptions.first(where: { $0.id == transaction.productID}) {
                        // Provide access to the product
                        purchasedProducts.append(subscription)
                    }
                }
            default:
                break
            }
        } catch {
            //storekit has a transaction that fails verification, don't delvier content to the user
            print("Failed - error retrieving products \(error)")
        }
    }
    //finally assign the purchased products
    self.purchasedSubscriptions = purchasedProducts
}

目前必须手动调用updateCustomerProductStatus()才能检查订阅是否到期,猜测订阅到期不被视为交易更新,想问有没有其他钩子可以监听订阅到期事件?


解决方案

核心结论:订阅到期不会触发Transaction.updates事件

StoreKit 2中,Transaction.updates仅监听主动交易行为产生的更新,比如用户主动购买、续订、退款、退款式取消订阅等操作。订阅自然到期属于被动状态变更,不会触发这个流。

正确的到期状态处理方式

  • 定期主动检查权益
    不需要依赖钩子,在这些时机主动调用updateCustomerProductStatus():

    • App启动/从后台回到前台时
    • 用户进入需要订阅权限的页面时
    • 订阅到期前1-2小时触发提醒同步检查
  • 监听App生命周期自动触发
    通过NotificationCenter监听前台激活事件,自动更新状态:

    func setupLifecycleListeners() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(onAppEnterForeground),
            name: UIApplication.willEnterForegroundNotification,
            object: nil
        )
    }
    
    @objc func onAppEnterForeground() {
        Task { await updateCustomerProductStatus() }
    }
    
  • 修复updateCustomerProductStatus()逻辑
    原代码遇到过期交易直接return会忽略后续有效订阅,改为跳过过期项继续检查:

    @MainActor
    func updateCustomerProductStatus() async {
        var purchasedProducts: [Product] = []
        
        for await result in Transaction.currentEntitlements {
            do {
                let transaction = try checkVerified(result)
                
                guard transaction.productType == .autoRenewable else { continue }
                
                if let revocationDate = transaction.revocationDate {
                    print("Transaction was revoked on \(revocationDate)")
                    continue
                }
                
                if let expirationDate = transaction.expirationDate, expirationDate < Date() {
                    print("Subscription \(transaction.productID) expired on \(expirationDate)")
                    continue
                }
                
                if let subscription = subscriptions.first(where: { $0.id == transaction.productID }) {
                    purchasedProducts.append(subscription)
                }
                
            } catch {
                print("Failed - error retrieving products \(error)")
            }
        }
        
        self.purchasedSubscriptions = purchasedProducts
    }
    
  • 服务器端验证(推荐)
    利用App Store Server API定期拉取用户订阅状态,服务器可以主动获取到期信息,下次用户打开App时同步最新状态,避免完全依赖客户端检查。

测试技巧

在Xcode测试时,可使用App Store Connect的沙盒订阅加速功能,将到期时间缩短至分钟级,无需等待自然到期。

内容的提问来源于stack exchange,提问作者smartinrubio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:32:36