目录

App Store 审核 2.1(性能-应用完整性):订阅页无限加载的修复指南(收据验证与前端超时)

App Store 审核 2.1(性能-应用完整性):订阅页无限加载的修复指南(收据验证与前端超时)

审核反馈常见问题:订阅页加载无限转圈,多由收据验证流程不当和前端请求无超时导致。


一、问题描述(来源于 Guideline 2.1)

  • 现象:订阅页加载不结束,体验不佳。
  • 根因:
    • 后端收据验证只请求了生产环境,遇到沙盒收据未正确切换(需处理 21007)。
    • 前端网络请求无超时/无错误提示,一直等待导致“无限加载”。

二、修复方案总览

  1. 后端:收据验证采用“先生产、后沙盒”的回退策略,并设置超时。
  2. 前端:请求设置合理超时,加载态可见、失败可重试,避免无限等待。
  3. 平台:确保 App Store Connect 付费相关协议已签署。

三、后端:收据验证逻辑(Python/Requests 示例)

import requests

APPLE_PRODUCTION_URL = "https://buy.itunes.apple.com/verifyReceipt"
APPLE_SANDBOX_URL = "https://sandbox.itunes.apple.com/verifyReceipt"

TIMEOUT_SECONDS = 10


def validate_receipt(receipt_data: str, password: str) -> dict:
    """先请求生产环境,如遇 21007 再请求沙盒;全程设置超时。"""
    payload = {
        "receipt-data": receipt_data,
        "password": password,
        "exclude-old-transactions": True,
    }

    # 1) 先生产
    resp = requests.post(APPLE_PRODUCTION_URL, json=payload, timeout=TIMEOUT_SECONDS)
    result = resp.json()

    # 2) 21007 -> 切沙盒
    if result.get("status") == 21007:
        resp = requests.post(APPLE_SANDBOX_URL, json=payload, timeout=TIMEOUT_SECONDS)
        result = resp.json()

    return result

要点:

  • 处理 status == 21007(沙盒收据误发生产)。
  • 每个请求设置超时(例如 10s),避免后端“等死”。
  • 日志中打印 status 与关键字段,便于排障。

四、前端:SwiftUI 订阅页请求(超时 + 可重试)

import SwiftUI

struct SubscriptionView: View {
    @State private var isLoading = false
    @State private var errorMessage: String?

    private let timeout: TimeInterval = 10

    func validateReceipt(receiptData: String, password: String) {
        isLoading = true
        errorMessage = nil
        guard let url = URL(string: "https://your-backend.com/validate-receipt") else {
            errorMessage = "无效的请求地址"
            return
        }
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.timeoutInterval = timeout
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        let body: [String: Any] = [
            "receipt_data": receiptData,
            "password": password
        ]
        request.httpBody = try? JSONSerialization.data(withJSONObject: body)

        URLSession.shared.dataTask(with: request) { data, response, error in
            DispatchQueue.main.async {
                isLoading = false
                if let error = error {
                    errorMessage = "网络错误: \(error.localizedDescription)"
                    return
                }
                guard let data = data else {
                    errorMessage = "未收到数据"
                    return
                }
                // TODO: 解析 data 并根据业务更新 UI
                print("receipt validation response: \(data)")
            }
        }.resume()
    }

    var body: some View {
        VStack(spacing: 12) {
            if isLoading { ProgressView("验证中…") }
            if let errorMessage = errorMessage {
                Text(errorMessage).foregroundColor(.red)
                Button("重试") {
                    // 根据业务重新调用 validateReceipt
                }
            }
            // 订阅页面 UI …
        }
        .padding()
    }
}

要点:

  • URLRequest.timeoutInterval 限制等待时间,失败后给出提示并支持重试。
  • 加载态可见(ProgressView),避免界面“静默卡住”。

五、App Store Connect 检查

  • 账号持有人需在 App Store Connect > 商务 中接受《付费应用协议》。
  • 使用与审核同环境设备(如 iPadOS 18.5)测试验证流程。

六、自测清单

  • 后端对 21007 正常回退沙盒;有超时设置与错误日志。
  • 前端请求超时 ≤ 10–15s;失败有可见错误提示;支持重试。
  • 订阅页有清晰加载与失败状态,不出现“无限转圈”。
  • 协议已签署、测试通过,再提交审核。

总结

  • 后端“先生产后沙盒 + 超时”是关键;
  • 前端“加载态 + 超时 + 异常提示/重试”可避免无限等待;
  • 完成平台侧协议与设备实测,提升过审成功率。