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

用户登录场景下StoreKit 2 Transaction.updates处理最佳实践咨询

StoreKit 2:延迟交易处理至用户登录后的最佳实践

你的核心思路可行且符合StoreKit 2的设计规范,但需要调整代码细节并补充关键逻辑,确保覆盖所有场景:

为什么你的方案能覆盖遗漏交易

StoreKit 2的Transaction.unfinished是系统维护的全局未完成交易集合:

  • 它会先返回当前所有未完成的交易(包括应用启动到用户登录这段时间产生的交易)
  • 之后会持续监听并返回新产生的未完成交易
  • 对比Transaction.updates:后者仅能获取订阅之后的交易更新,无法覆盖登录前的遗漏,但Transaction.unfinished刚好补上了这个缺口

代码调整与优化建议

你当前的代码存在变量名错误,且缺少交易完成标记、幂等处理等关键逻辑,以下是优化后的实现:

// 用户登录成功后调用
func processTransactionUpdates() {
    Task.detached {
        // 遍历所有未完成交易(含登录前产生的),并持续监听新交易
        for await transaction in Transaction.unfinished {
            // 跳过已消耗的交易,避免重复处理
            guard !transaction.isConsumed else {
                await transaction.finish()
                continue
            }
            
            // 验证当前用户状态,确保交易与用户绑定
            guard let currentUserID = AuthManager.shared.currentUserID else {
                continue
            }
            
            // 发送交易到后端验证并授予权益
            let validationSuccess = await self.validateTransactionWithBackend(transaction, userID: currentUserID)
            
            if validationSuccess {
                // 标记交易为完成,避免重复出现在未完成列表中
                await transaction.finish()
            }
        }
    }
    
    // 额外监听全量交易更新,处理退款、撤销等特殊状态
    Task.detached {
        for await transaction in Transaction.updates {
            switch transaction.transactionState {
            case .failed:
                // 记录失败日志,清理无效交易
                await transaction.finish()
            case .refunded, .revoked:
                // 通知后端撤销对应用户的权益
                await self.handleRefundOrRevocation(transaction)
                await transaction.finish()
            default:
                // 未完成交易已由Transaction.unfinished处理,此处跳过
                break
            }
        }
    }
}

// 后端交易验证逻辑示例
private func validateTransactionWithBackend(_ transaction: Transaction, userID: String) async -> Bool {
    let jwsToken = transaction.jwsRepresentation
    // 构造请求并发送到后端验证
    let request = PurchaseValidationRequest(userID: userID, jwsToken: jwsToken)
    let response = await BackendClient.send(request: request)
    return response.isValid
}

// 退款/权益撤销处理示例
private func handleRefundOrRevocation(_ transaction: Transaction) async {
    guard let userID = AuthManager.shared.currentUserID else { return }
    await BackendClient.revokeBenefits(userID: userID, transactionID: transaction.id)
}

关键注意事项

  • 必须标记交易完成:处理完交易后一定要调用await transaction.finish(),否则系统会持续将该交易视为未完成,每次启动都会重复返回
  • 后端幂等处理:后端需记录已处理的transactionID,防止同一交易因重复触发处理逻辑而多次授予权益
  • 未登录用户的交易留存:即使用户始终不登录,StoreKit会自动保留未完成交易,直到用户后续登录后通过Transaction.unfinished获取处理,无需额外本地缓存
  • 用户绑定校验:处理交易时必须携带当前登录用户ID,避免切换账号后权益分配混乱

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 08:45:49