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

沙盒环境下IAP收据无效:恢复购买后验证失败

StoreKit收据验证与恢复购买问题解答

收据是否加载正确?

从错误码22002(收据格式错误)来看,你的收据肯定没加载/处理正确。这个错误是Apple服务器明确识别到提交的收据数据格式不符合要求,常见原因包括:

  • 直接读取本地收据二进制数据就提交,没做Base64编码
  • Base64编码时带了多余的换行符
  • 读取的收据文件不完整(比如恢复交易后还没写入完成就读取)

如何确保存储有效收据?

1. 正确加载并编码收据

必须将本地收据的二进制数据转换为不带换行符的Base64字符串,示例代码:

func loadAndEncodeReceipt() -> String? {
    guard let receiptURL = Bundle.main.appStoreReceiptURL,
          FileManager.default.fileExists(atPath: receiptURL.path),
          let receiptData = try? Data(contentsOf: receiptURL) else {
        return nil
    }
    // 生成无换行的Base64编码,避免格式错误
    return receiptData.base64EncodedString(options: [])
}

注意:如果appStoreReceiptURL不存在或文件为空,不要强行提交,需先调用SKReceiptRefreshRequest刷新收据。

2. 调整恢复购买后的收据处理时机

不要在paymentQueueRestoreCompletedTransactionsFinished中立刻读取收据——恢复交易完成后,Apple可能还在异步写入本地收据文件。正确流程:

  • 调用SKPaymentQueue.default().restoreCompletedTransactions()
  • 在paymentQueue(_:updatedTransactions:)中处理每个交易:
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .restored:
                // 标记对应内购项目已恢复
                handleRestoredPurchase(transaction: transaction)
                // 结束交易
                queue.finishTransaction(transaction)
            case .failed:
                queue.finishTransaction(transaction)
            default:
                break
            }
        }
        // 所有恢复交易处理完成后,主动刷新收据
        refreshReceipt()
    }
    
    func refreshReceipt() {
        let request = SKReceiptRefreshRequest(receiptProperties: nil)
        request.delegate = self
        request.start()
    }
    
  • 在requestDidFinish(_:)回调中再加载收据进行验证。

3. 验证请求的格式必须合规

提交到沙箱验证接口的JSON必须符合要求:

  • 包含receipt-data字段(值为Base64编码的收据)
  • 自动续期订阅需添加password字段(你的App内购共享密钥)
  • 建议添加exclude-old-transactions: true减少响应体积
    示例请求体:
{
    "receipt-data": "你的Base64编码收据字符串",
    "password": "your_shared_secret_key",
    "exclude-old-transactions": true
}

同时确保请求的Content-Type设置为application/json。

4. 有效收据的存储策略

  • 验证通过后,将Apple返回的完整验证响应JSON存储到Keychain(敏感信息避免用UserDefaults)
  • 每次启动App时,先检查本地存储的响应是否过期(读取expires_date_ms字段判断)
  • 若未过期,直接使用本地存储的验证结果;若过期或验证失败,再重新加载收据并提交验证

5. 异常场景处理

  • 若收据文件不存在:提示用户检查Apple ID登录状态,或触发SKReceiptRefreshRequest获取收据
  • 网络异常时:依赖本地存储的有效收据,待网络恢复后再重新验证

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:20:33