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

交易完成后调用API更新用户信息的技术实现问题问询

问题分析与解决方案

你的核心问题在于在异步API调用完成前就结束了交易,同时对SKPaymentTransactionObserver协议方法的异步处理方式有误——协议要求这个方法是同步的,不能直接改成async签名,否则会不符合协议要求。

当前代码的关键问题

  • 在.purchased分支中,你启动了Task调用postCoins,但立刻执行了queue.finishTransaction(transaction),这会导致交易被标记为完成,但API请求可能还在处理中,一旦请求失败,你无法重新触发验证流程。
  • postCoins的调用没有处理失败场景,无法得知API是否成功,也没有重试机制。

修复方案

你需要把queue.finishTransaction(transaction)移到API调用完成(成功或失败处理后)再执行,同时在Task内部处理API的结果,确保交易生命周期和API调用绑定:

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        switch transaction.transactionState {
        case .purchasing:
            transactionState = .purchasing
            print("Purchasing...")
        case .purchased:
            UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
            print("Purchased.")
            guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
                  FileManager.default.fileExists(atPath: appStoreReceiptURL.path) else {
                // 收据不存在,标记交易失败并结束
                print("Receipt file not found")
                queue.finishTransaction(transaction)
                transactionState = .failed
                return
            }
            
            do {
                let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                let receiptString = receiptData.base64EncodedString(options: [])
                
                Task {
                    do {
                        // 等待API调用完成
                        try await postCoins(receipt: receiptString)
                        // API成功,结束交易
                        queue.finishTransaction(transaction)
                        DispatchQueue.main.async {
                            self.transactionState = .purchased
                            // 这里可以更新UI,比如提示用户金币到账
                        }
                    } catch {
                        print("Failed to update user info: \(error.localizedDescription)")
                        // API失败,根据业务需求处理:比如重试、标记交易待处理,或者结束交易(不推荐)
                        // 建议先标记交易状态,后续重试,避免用户付费后未拿到金币
                        DispatchQueue.main.async {
                            self.transactionState = .failed
                            // 提示用户支付成功但金币未到账,引导联系客服或重试
                        }
                        // 注意:如果不finishTransaction,这个交易会一直留在队列里,下次启动App会再次触发这个方法
                        // 所以如果确定无法重试,可以选择finish,但要做好用户补偿机制
                        // queue.finishTransaction(transaction)
                    }
                }
            } catch {
                print("Couldn't read receipt data with error: \(error.localizedDescription)")
                queue.finishTransaction(transaction)
                transactionState = .failed
            }
        case .restored:
            UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
            queue.finishTransaction(transaction)
            transactionState = .restored
        case .failed, .deferred:
            print("Payment queue error:")
            print(String(describing: transaction.error))
            queue.finishTransaction(transaction)
            transactionState = .failed
        default:
            queue.finishTransaction(transaction)
        }
    }
}

额外注意事项

  • API调用的错误处理:必须处理postCoins的失败情况,避免用户付费后未获得对应的金币,建议实现重试机制(比如把未成功的交易信息存在本地,下次启动App时重新发起API请求)。
  • 主线程更新状态:transactionState如果用于UI展示,必须在主线程更新,所以用DispatchQueue.main.async包裹。
  • 协议方法不能改异步:SKPaymentTransactionObserver的paymentQueue(_:updatedTransactions:)要求是同步方法,你不能给它添加async关键字,只能在方法内部通过Task启动异步任务,这是符合协议要求的正确做法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:50:19