You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

App Store Connect销售/分析面板未显示内购数据?求排查方案

分析与解决方案

首先,这种情况确实存在被恶意绕过iOS内购获取免费积分的可能性,同时你的代码逻辑也存在关键漏洞,这大概率是问题的根源。下面一步步拆解:

一、为什么会出现数据库有记录但App Store Connect无数据?

App Store Connect的销售数据延迟一般不会超过7天,所以这种情况基本可以排除是平台延迟。更可能的原因是:

  • 恶意用户通过篡改本地代码、模拟SKPaymentTransaction对象,或者在越狱设备上直接触发你的.purchased回调逻辑,让你的App误以为交易完成并发放了积分,但实际上并没有真正完成Apple内购流程,所以App Store Connect完全没有这笔交易的记录。
  • 另一种小概率情况:如果你的数据库记录是客户端直接上报的,那也可能是用户伪造了上报请求,直接在数据库里生成了虚假的销售数据。

二、当前代码的核心漏洞

你现在的代码只依赖SKPaymentTransactionState.purchased这个客户端回调状态就完成交易并发放积分,但Apple的内购回调状态是可以被本地篡改的——恶意用户完全可以绕过真实的支付流程,直接构造一个状态为.purchased的交易对象传入你的回调方法,从而骗取积分。

关键的缺失步骤是:没有对交易进行服务器端的收据验证,这是iOS内购安全的核心环节。

三、修复方案:强制服务器端收据验证

要彻底解决这个问题,必须在发放积分前,通过你的后端服务器向Apple官方验证交易的真实性,具体步骤如下:

  1. 在客户端获取交易收据,发送到后端
    在你的complete(transaction:)方法中,先获取App Store的交易收据,转换为Base64字符串后发送到你的后端服务器,同时带上交易ID、产品ID等关键信息。示例代码如下:

    private func complete(transaction: SKPaymentTransaction) {
        // 获取App Store收据文件路径
        guard let receiptURL = Bundle.main.appStoreReceiptURL,
              let receiptData = try? Data(contentsOf: receiptURL) else {
            // 收据获取失败,拒绝发放积分,结束交易
            SKPaymentQueue.default().finishTransaction(transaction)
            return
        }
        
        // 转换为Base64字符串
        let receiptBase64 = receiptData.base64EncodedString()
        
        // 准备请求参数,包含必要的交易信息
        let requestParams: [String: Any] = [
            "receipt_base64": receiptBase64,
            "transaction_id": transaction.transactionIdentifier ?? "",
            "product_id": transaction.payment.productIdentifier,
            "user_id": "当前用户的唯一标识" // 必须带上,防止跨用户篡改
        ]
        
        // 发送请求到你的后端服务器验证收据
        sendVerificationRequest(to: "你的后端API地址", params: requestParams) { [weak self] success in
            guard let self = self else { return }
            if success {
                // 验证通过,发放积分
                self.grantPointsForTransaction(transaction)
            }
            // 无论验证成功与否,都要结束交易,避免重复回调
            SKPaymentQueue.default().finishTransaction(transaction)
        }
    }
    
  2. 后端服务器向Apple验证收据
    你的后端需要将收到的收据数据发送到Apple的官方验证接口:

    • 生产环境:https://buy.itunes.apple.com/verifyReceipt
    • 沙盒测试环境:https://sandbox.itunes.apple.com/verifyReceipt
      发送POST请求,请求体为{"receipt-data": "你收到的Base64收据字符串"}(如果是自动续期订阅,还需要加上password字段)。
  3. 根据Apple的验证结果处理
    只有当Apple返回的验证结果status0(表示验证成功),并且返回的交易信息(比如交易ID、产品ID、用户标识)和你收到的参数匹配时,才向用户发放积分,同时在数据库中记录这笔已验证的交易。

四、额外的安全建议

  • 绝对不要在客户端做收据验证:客户端的任何逻辑都可以被篡改,验证必须放在后端。
  • 标记已处理的交易:在数据库中记录每个交易ID的处理状态,避免同一交易被重复发放积分。
  • 区分沙盒和生产环境:测试时的沙盒交易不会出现在App Store Connect生产控制台,所以后端要根据环境选择对应的Apple验证接口,避免把测试交易误判为真实交易。
  • 检查用户标识:发放积分时必须绑定用户的唯一标识(比如你的App内的用户ID),防止恶意用户用其他用户的交易凭证骗取积分。

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

火山引擎 最新活动