Apple开发者官网促销内购代码:如何检查交易完成及处理异常?
嘿,我来帮你把App Store促销内购的交易检查、失败和延迟处理的逻辑理清楚,毕竟StoreKit这套流程我摸得门儿清~
内购交易处理全流程指南
针对你提到的促销内购触发后的交易检查,以及失败/延迟场景的处理,我给你拆解成具体的实现步骤:
一、先搞懂核心逻辑:交易状态的监听
你贴的shouldAddStorePayment方法只是个「准入开关」——当用户从App Store点击促销内购跳回App时,系统会调用这个方法,问你要不要接收这笔交易。返回true就把交易加入支付队列,false则拒绝。但真正的交易完成检查、失败/延迟处理,都要靠paymentQueue(_:updatedTransactions:)这个核心方法,它会监听交易的所有状态变化。
首先,你得确保把你的类注册为支付队列的观察者(比如在App启动时就做):
// 比如在AppDelegate的didFinishLaunchingWithOptions里,或者专门的内购管理类初始化时 SKPaymentQueue.default().add(self)
然后完整实现SKPaymentTransactionObserver的两个关键方法:
// MARK: - SKPaymentTransactionObserver func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, forProduct product: SKProduct) -> Bool { // 这里可以做前置检查:比如用户是否登录、产品是否符合当前用户权限 // 比如如果用户没登录,就返回false,不让处理这个促销内购 guard UserDefaults.standard.bool(forKey: "isUserLoggedIn") else { showError(message: "请先登录再购买") return false } return true } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // 遍历所有更新的交易,逐个处理状态 for transaction in transactions { switch transaction.transactionState { case .purchased: // ✅ 交易完成!这里做核心的交付、验证逻辑 handleCompletedTransaction(transaction) case .failed: // ❌ 交易失败,处理错误提示 handleFailedTransaction(transaction) case .deferred: // ⏳ 交易延迟(比如家长控制审批),提示用户等待 handleDeferredTransaction(transaction) case .restored: // 🔄 恢复购买,处理已购内容的恢复 handleRestoredTransaction(transaction) case .purchasing: // 🔄 交易正在进行中,显示加载动画即可 showPurchasingLoading() @unknown default: break } } }
二、交易完成检查的具体实现
当交易状态变为.purchased时,就代表交易成功完成了,这时候你要做三件事:
- 验证交易收据(强烈建议服务器端验证,防止客户端篡改)
- 给用户交付对应的内容(解锁功能、发道具等)
- 结束交易,不然交易会一直留在队列里重复触发
示例代码:
private func handleCompletedTransaction(_ transaction: SKPaymentTransaction) { // 1. 获取并验证收据(这里简化为客户端示例,实际一定要用服务器端!) if let receiptURL = Bundle.main.appStoreReceiptURL, let receiptData = try? Data(contentsOf: receiptURL) { let receiptBase64 = receiptData.base64EncodedString() // 把receiptBase64发给你的后端服务器,由服务器调用苹果的验证接口 // 服务器验证通过后,再执行交付逻辑 deliverPurchasedContent(productID: transaction.payment.productIdentifier) } // 3. 必须调用这个方法结束交易,否则队列会一直重复通知 SKPaymentQueue.default().finishTransaction(transaction) } private func deliverPurchasedContent(productID: String) { // 根据产品ID解锁对应的功能,比如存到UserDefaults或者你的数据库 UserDefaults.standard.set(true, forKey: productID) // 通知UI更新,比如弹出成功提示、刷新界面 let alert = UIAlertController(title: "购买成功", message: "已解锁对应功能", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) if let topVC = UIApplication.shared.keyWindow?.rootViewController { topVC.present(alert, animated: true) } }
三、交易失败与延迟的处理
1. 交易失败(.failed状态)
失败的原因有很多:用户取消支付、网络错误、支付方式无效、产品下架等,你要根据错误码给用户对应的提示:
private func handleFailedTransaction(_ transaction: SKPaymentTransaction) { guard let error = transaction.error as? SKError else { showError(message: "支付失败,请稍后重试") SKPaymentQueue.default().finishTransaction(transaction) return } switch error.code { case .paymentCancelled: // 用户主动取消,不用弹提示或者轻提示一下就行 print("用户取消了支付") case .paymentInvalid: showError(message: "支付无效,请检查你的支付方式") case .paymentNotAllowed: showError(message: "当前账户不允许进行支付,请检查设置") case .storeProductNotAvailable: showError(message: "该产品当前暂时不可购买") default: showError(message: "支付失败:\(error.localizedDescription)") } // 结束失败的交易 SKPaymentQueue.default().finishTransaction(transaction) } private func showError(message: String) { let alert = UIAlertController(title: "提示", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) if let topVC = UIApplication.shared.keyWindow?.rootViewController { topVC.present(alert, animated: true) } }
2. 交易延迟(.deferred状态)
这个状态一般是因为用户开启了家长控制,需要家长审批才能完成支付,或者其他需要等待的系统流程。这时候你只要提示用户等待即可,不需要结束交易——后续审批完成后,系统会再次触发updatedTransactions方法更新状态:
private func handleDeferredTransaction(_ transaction: SKPaymentTransaction) { let alert = UIAlertController(title: "交易处理中", message: "你的支付请求正在等待审批,请稍后查看结果", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "知道了", style: .default)) if let topVC = UIApplication.shared.keyWindow?.rootViewController { topVC.present(alert, animated: true) } }
四、几个关键注意点
- 一定要注册观察者:在App启动时就调用
SKPaymentQueue.default().add(self),不然应用在后台时交易完成,回到前台收不到通知 - 必须调用finishTransaction:不管交易成功、失败还是恢复,处理完后都要调用这个方法,否则交易会一直留在队列里,每次App启动都会重新触发
- 收据验证要在服务器端:客户端验证容易被破解,安全性极低,苹果官方也推荐服务器端验证
- 恢复购买的处理:如果用户重装App,需要调用
SKPaymentQueue.default().restoreCompletedTransactions()来恢复已购内容,对应的状态是.restored,处理逻辑和.purchased类似,注意不要重复交付内容
内容的提问来源于stack exchange,提问作者Astrum
相关产品推荐
相关产品推荐

