交易完成后调用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
相关产品推荐
相关产品推荐

