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

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时,就代表交易成功完成了,这时候你要做三件事:

  1. 验证交易收据(强烈建议服务器端验证,防止客户端篡改)
  2. 给用户交付对应的内容(解锁功能、发道具等)
  3. 结束交易,不然交易会一直留在队列里重复触发

示例代码:

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:54:50