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

iOS 15下iAP自动续期订阅恢复购买旧交易重复及新购异常问题

问题根因

你遇到的所有问题本质上都是IAP交易处理逻辑不符合StoreKit的设计规范,核心错误点如下:

  • 自动续期订阅的有效性验证依赖整包App Store收据,而非单个交易,你逐个上报历史恢复交易属于冗余操作,也是造成后端压力、重复弹窗的核心原因
  • 未按照要求在交易处理完成后第一时间调用finishTransaction,导致历史交易长期堆积在支付队列中,每次触发恢复购买都会重复返回累积的历史交易
  • 在paymentQueueRestoreCompletedTransactionsFinished中无差别批量结束所有交易,会导致未处理的新交易被提前结束,是你后续购买新订阅后收据找不到对应交易的直接原因
  • 按productID去重交易的操作会过滤新增的购买交易,进一步导致新订阅的交易被忽略,收据无法正常更新
正确处理流程

1. 基础规则前置

  • App启动时第一时间设置SKPaymentQueue.default().add(self)注册支付代理,同时遍历当前队列中所有未完成的历史交易,全部调用finishTransaction清空堆积
  • 所有交易无论最终结果是成功、失败、验证不通过,都必须在处理完成后立刻调用finishTransaction,不允许遗留任何未结束的交易
  • 自动续期订阅的所有有效性判断,统一通过Bundle.main.appStoreReceiptURL读取本地整包收据,base64编码后传给后端做一次性苹果验证,不需要处理任何单个交易的凭证信息

2. 首次购买流程

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
    for transaction in transactions {
        switch transaction.transactionState {
        case .purchased:
            // 1. 读取整包收据传给后端验证
            guard let receiptUrl = Bundle.main.appStoreReceiptURL,
                  let receiptData = try? Data(contentsOf: receiptUrl) else {
                // 收据读取失败,结束交易并提示用户
                queue.finishTransaction(transaction)
                showToast("购买失败,请重试")
                return
            }
            let base64Receipt = receiptData.base64EncodedString()
            // 2. 调用后端验证接口
            IAPManager.shared.verifyReceipt(base64Receipt) { [weak self] isValid, expireDate in
                guard let self = self else { return }
                // 3. 无论验证是否通过,都先结束交易
                queue.finishTransaction(transaction)
                if isValid {
                    // 验证通过,更新用户订阅状态,提示购买成功
                    UserDefaults.standard.set(expireDate, forKey: "subscribe_expire_date")
                    showToast("订阅购买成功")
                } else {
                    // 验证失败,提示用户
                    showToast("订阅验证失败,请重试")
                }
            }
        case .failed:
            // 失败直接结束交易,根据错误码提示用户
            queue.finishTransaction(transaction)
            if let error = transaction.error as? SKError {
                switch error.code {
                case .paymentCancelled:
                    showToast("已取消购买")
                default:
                    showToast("购买失败,请重试")
                }
            }
        case .restored:
            // 恢复状态的交易在这里不需要做任何处理,等恢复流程结束后统一处理
            break
        default:
            break
        }
    }
}

3. 恢复购买流程

  • 用户点击恢复购买按钮时调用:SKPaymentQueue.default().restoreCompletedTransactions()
  • 恢复流程完成回调处理:
// 恢复成功完成
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
    // 1. 先结束所有restore状态的交易
    queue.transactions.filter { $0.transactionState == .restored }.forEach {
        queue.finishTransaction($0)
    }
    // 2. 读取整包收据传给后端验证
    guard let receiptUrl = Bundle.main.appStoreReceiptURL,
          let receiptData = try? Data(contentsOf: receiptUrl) else {
        showToast("恢复失败,请重试")
        return
    }
    let base64Receipt = receiptData.base64EncodedString()
    IAPManager.shared.verifyReceipt(base64Receipt) { isValid, expireDate in
        if isValid, let expireDate = expireDate, expireDate > Date() {
            // 有有效订阅,更新用户状态,提示恢复成功
            UserDefaults.standard.set(expireDate, forKey: "subscribe_expire_date")
            showToast("订阅恢复成功")
        } else {
            // 无有效订阅,提示用户
            showToast("未找到有效订阅记录")
        }
    }
}

// 恢复失败回调
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
    // 先结束所有restore状态的交易
    queue.transactions.filter { $0.transactionState == .restored }.forEach {
        queue.finishTransaction($0)
    }
    showToast("恢复失败,请重试")
}

4. 弹窗优化说明

  • 整个流程中只在最终验证完成、交易失败两个节点弹出用户提示,不要在每个交易回调中弹出提示,即可避免多次弹窗的问题
  • 系统自带的购买/恢复成功弹窗是苹果系统控制的,无法取消,属于正常现象
遗留问题修复

针对你现在已经出现的新购买订阅找不到的问题,先在测试环境执行一次清空操作:

  1. 启动App时先遍历所有队列中的交易,全部finish,清空历史堆积
  2. 退出当前测试的AppleID,重新登录沙箱测试账号
  3. 删除App重新安装,即可恢复正常的购买流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 15:18:01