啊宇哥哥 2025-12-27 16:30 采纳率: 98.4%
浏览 2
已采纳

如何处理苹果内购协议审核被拒?

当苹果内购协议审核被拒时,常见技术问题之一是:应用内未正确实现“恢复购买”功能。根据App Store审核指南,所有非消耗性内购必须提供免费的恢复机制,允许用户在更换设备或重新安装后恢复已购内容。若开发者遗漏此功能或逻辑实现不完整(如未调用`restoreCompletedTransactions`),将导致审核被拒。此外,部分开发者误将服务器验证与本地恢复逻辑耦合,造成恢复失败。正确做法是确保客户端调用恢复接口,并在服务端配合验证用户购买记录,保证跨设备一致性。
  • 写回答

1条回答 默认 最新

  • 桃子胖 2025-12-27 16:30
    关注

    苹果内购审核被拒:恢复购买功能的深度解析与实践方案

    1. 问题背景与审核政策解读

    根据《App Store 审核指南》第3.1.2条,所有非消耗性内购(Non-Consumable In-App Purchases)必须提供免费的“恢复购买”功能。该机制允许用户在更换设备、重装应用或登录新账号时重新获取已购内容,而无需再次付费。

    若开发者未实现此功能,或仅在特定页面隐藏入口、调用逻辑不完整(如未正确使用 SKPaymentQueue.default().restoreCompletedTransactions()),将直接导致审核被拒。

    此外,部分开发者误将服务器端验证作为恢复的前提条件,造成客户端无法触发本地恢复流程,形成逻辑死锁。

    2. 常见技术问题清单

    • 未实现“恢复购买”按钮或入口过于隐蔽
    • 未调用 restoreCompletedTransactions() 方法
    • 恢复过程中未处理 SKPaymentTransactionObserver 回调
    • 将恢复逻辑绑定于用户登录状态,导致未登录时无法恢复
    • 服务端强制校验票据有效性前拒绝恢复,违背“免费恢复”原则
    • 恢复后未同步解锁本地功能或内容
    • 跨平台账户体系未统一,导致iOS设备间无法识别同一用户
    • 沙盒测试环境未模拟多设备切换场景
    • 未处理 Apple 返回的原始交易记录(Original Transaction ID)匹配问题
    • 恢复过程中抛出异常但未捕获,UI无反馈

    3. 恢复购买的完整工作流程

    
    func restorePurchases() {
        SKPaymentQueue.default().add(self)
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
    
    // MARK: - SKPaymentTransactionObserver
    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .restored:
                let originalTx = transaction.original?.transactionIdentifier ?? transaction.transactionIdentifier
                // 向服务端发送 originalTx 进行验证并解锁内容
                verifyAndUnlockContent(with: originalTx)
                SKPaymentQueue.default().finishTransaction(transaction)
            case .failed:
                handleError(transaction.error)
                SKPaymentQueue.default().finishTransaction(transaction)
            default:
                break
            }
        }
    }
        

    4. 客户端与服务端协作模型

    阶段客户端行为服务端行为
    触发恢复调用 restoreCompletedTransactions()无动作
    交易回调收到 .restored 状态事务接收客户端上传的 transactionIdentifier
    票据验证上传收据至自有服务向 Apple Verify Endpoint 验证收据
    用户绑定携带用户唯一标识(如 userID)将 purchaseRecord 绑定到 userID
    内容解锁根据服务端响应激活功能返回已购项目列表

    5. 架构设计中的关键解耦策略

    许多开发者错误地将“恢复购买”与“登录系统”强耦合,导致游客模式下无法恢复。正确做法是:

    1. 允许任意用户点击“恢复购买”,无论是否已登录
    2. 客户端从 StoreKit 获取历史交易记录
    3. 提取 transactionIdentifier 并提交至服务端进行归属判断
    4. 服务端通过 Apple 的收据验证接口解析购买信息,并关联当前设备或账户
    5. 若用户尚未登录,可暂存恢复结果,待登录后自动合并权限

    6. Mermaid 流程图:恢复购买全链路时序

    sequenceDiagram
        participant User
        participant App as iOS Client
        participant SK as SKPaymentQueue
        participant Server
        participant Apple
    
        User->>App: 点击“恢复购买”
        App->>SK: restoreCompletedTransactions()
        SK->>Apple: 请求历史交易记录
        Apple-->>SK: 返回已完成的非消耗型交易
        SK->>App: 回调 updatedTransactions(.restored)
        App->>Server: 发送 transactionId + receipt + deviceId/userId
        Server->>Apple: 调用 verifyReceipt 验证收据
        Apple-->>Server: 返回详细购买信息
        Server->>Server: 匹配用户并标记为已拥有
        Server-->>App: 返回成功及解锁内容列表
        App->>User: 解锁功能并提示恢复完成
        App->>SK: finishTransaction
        

    7. 最佳实践建议

    为确保顺利通过审核,推荐以下工程实践:

    • 在设置页或账户页显著位置放置“恢复购买”按钮
    • 使用 NSUbiquitousKeyValueStore 或 Push Notifications 实现跨设备状态同步
    • 对每个非消耗性产品维护独立的解锁标志位
    • 在沙盒环境中使用多个测试 Apple ID 模拟迁移场景
    • 记录日志追踪 restore 流程各阶段执行情况
    • 避免在恢复流程中弹出支付界面或二次确认
    • 服务端应支持基于 Original Transaction ID 的幂等处理
    • 定期清理过期/无效的收据缓存以提升性能
    • 结合 CloudKit 实现轻量级用户数据同步
    • 提供离线恢复能力:本地存储最近一次有效验证结果
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月28日
  • 创建了问题 12月27日