App Store Connect销售/分析面板未显示内购数据?求排查方案
分析与解决方案
首先,这种情况确实存在被恶意绕过iOS内购获取免费积分的可能性,同时你的代码逻辑也存在关键漏洞,这大概率是问题的根源。下面一步步拆解:
一、为什么会出现数据库有记录但App Store Connect无数据?
App Store Connect的销售数据延迟一般不会超过7天,所以这种情况基本可以排除是平台延迟。更可能的原因是:
- 恶意用户通过篡改本地代码、模拟
SKPaymentTransaction对象,或者在越狱设备上直接触发你的.purchased回调逻辑,让你的App误以为交易完成并发放了积分,但实际上并没有真正完成Apple内购流程,所以App Store Connect完全没有这笔交易的记录。 - 另一种小概率情况:如果你的数据库记录是客户端直接上报的,那也可能是用户伪造了上报请求,直接在数据库里生成了虚假的销售数据。
二、当前代码的核心漏洞
你现在的代码只依赖SKPaymentTransactionState.purchased这个客户端回调状态就完成交易并发放积分,但Apple的内购回调状态是可以被本地篡改的——恶意用户完全可以绕过真实的支付流程,直接构造一个状态为.purchased的交易对象传入你的回调方法,从而骗取积分。
关键的缺失步骤是:没有对交易进行服务器端的收据验证,这是iOS内购安全的核心环节。
三、修复方案:强制服务器端收据验证
要彻底解决这个问题,必须在发放积分前,通过你的后端服务器向Apple官方验证交易的真实性,具体步骤如下:
在客户端获取交易收据,发送到后端
在你的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) } }后端服务器向Apple验证收据
你的后端需要将收到的收据数据发送到Apple的官方验证接口:- 生产环境:
https://buy.itunes.apple.com/verifyReceipt - 沙盒测试环境:
https://sandbox.itunes.apple.com/verifyReceipt
发送POST请求,请求体为{"receipt-data": "你收到的Base64收据字符串"}(如果是自动续期订阅,还需要加上password字段)。
- 生产环境:
根据Apple的验证结果处理
只有当Apple返回的验证结果status为0(表示验证成功),并且返回的交易信息(比如交易ID、产品ID、用户标识)和你收到的参数匹配时,才向用户发放积分,同时在数据库中记录这笔已验证的交易。
四、额外的安全建议
- 绝对不要在客户端做收据验证:客户端的任何逻辑都可以被篡改,验证必须放在后端。
- 标记已处理的交易:在数据库中记录每个交易ID的处理状态,避免同一交易被重复发放积分。
- 区分沙盒和生产环境:测试时的沙盒交易不会出现在App Store Connect生产控制台,所以后端要根据环境选择对应的Apple验证接口,避免把测试交易误判为真实交易。
- 检查用户标识:发放积分时必须绑定用户的唯一标识(比如你的App内的用户ID),防止恶意用户用其他用户的交易凭证骗取积分。
内容的提问来源于stack exchange,提问作者Murat Yasar




