Restore Purchases 按钮缺失:6 类问题 + 修复实现

付费墙或设置页没有 Restore Purchases 按钮,或藏太深,Apple 打回。

拒回引 Guideline 3.1.1,文案像 “the app does not allow users to restore previously purchased non-consumable in-app purchases or subscriptions”。你的付费墙有醒目的 SubscribeBuy Now 按钮,但没有 Restore。或者有,但藏在 Settings → Account → Subscriptions → 点两下,审核员从启动后撞到的付费墙里根本找不到。或者你用邮箱密码登录、把它当成”restore 路径”,但 Apple 期望的是一个单独调 StoreKit 的按钮。

任何非消耗品 IAP 或自动续订订阅,Apple 都期望有可见、可用的 Restore Purchases 入口。修复是机械的:付费墙加一个带文案的按钮、设置加一个顶层入口、绑 StoreKit restore 调用、把成功 / 失败状态显式露出,下次提交就过。

常见原因

按命中率排序。

1. 付费墙只有 Buy 没有 Restore

原始设计为转化优化,每像素都喊 “buy”。Restore 被当”以后做的”从未加上。

如何判断:干净 build 打开付费墙。不滚动数不到一个 “Restore Purchases” 链接或按钮就是这种情况。审核员侧:拒回引用的就是付费墙截图。

2. Restore 在但藏在深层导航

Restore 在 Settings → Account → Subscription → Manage → Restore。60 秒的审核员找不到。Apple 期望 Restore 从付费墙或主设置一两步可达。

如何判断:从启动到 Restore 按钮数点击次数。超过 2 下就太深。

3. App 把账号登录当 restore

你有邮箱登录。用户登账号、服务器告诉他们是 Pro。你以为这就是 “restore”。Apple 不同意——restore 必须调 StoreKit 并和 Apple 的购买历史对账,不能只靠你的账号库。

如何判断:搜代码里有没有 restoreCompletedTransactionsTransaction.currentEntitlements。没从 UI 按钮调过任一就是缺 StoreKit restore 原语。

4. iOS 上有 restore 但新设计的屏没有

你最近重做了付费墙。旧的有 Restore;新的因为工程师 porting 设计时漏了小文字链接,上线后没有。iOS 生产里缺,Android 还能用。

如何判断:把当前付费墙 view 和上一版 git 历史 diff。如果 Restore Purchases view 或按钮被删了,就是这次回归。

5. Restore 跑了但没显式反馈

按钮在。点了调 StoreKit。但视觉上什么都没发生——没 loading spinner、没成功提示、没失败信息。审核员点了什么都没看到,认为坏了,拒。

如何判断:干净安装手动点一下 Restore。5 秒内看不到明确反馈,审核员也看不到。

6. Restore 藏在免费层登录墙后

付费墙可达,但 Restore 按钮只有用户注册免费账号后才出现。Apple 期望 restore 不用任何注册就能触达;否则老付费用户得新注册一个免费账号才能 restore。

如何判断:冷装、不注册、走到付费墙。Restore 不可见就要修。

动手前先确认

  • 读一遍 Apple App Review Guidelines 3.1.1,让实现和期望逐字对齐。
  • 决定 Restore 放哪:付费墙 + 设置是安全默认;Onboarding 可选。
  • 改之前把当前付费墙和设置截图归档。
  • 弄清你用的是 StoreKit 1 还是 2,restore API 不同。

需要收集的信息

  • 审核员拒回原文和引用的 Guideline 子条款。
  • 当前付费墙、设置、任何 restore 流程的截图。
  • 你的 StoreKit 版本(SK1 vs SK2)和当前 restore 实现。
  • App 是否有免费层用户,他们不该启动就看到付费墙。
  • 老用户当前点 Restore 的频率(帮你 UX 调位)。

最短修复路径

Step 1:付费墙加 Restore Purchases 按钮

在付费墙 view 里放一个清晰的 “Restore Purchases” 文字链接或按钮。标准放在 Buy / Subscribe 主 CTA 下方,字号更小(但可读,≥14pt)。不要刻意视觉弱化。

// SwiftUI 示例
VStack(spacing: 16) {
    Button("Subscribe — $9.99/month") { /* buy */ }
        .buttonStyle(.borderedProminent)

    Button("Restore Purchases") {
        Task { await store.restore() }
    }
    .font(.subheadline)
    .foregroundColor(.secondary)
}

Step 2:设置里加第二个入口

从来没撞到付费墙的用户(比如新装的老订阅用户)也要能 restore。Settings → Restore Purchases 加成顶层一行,不要埋在 Account 下。

NavigationLink(destination: SettingsView()) {
    Section {
        Button("Restore Purchases") {
            Task { await store.restore() }
        }
    }
}

Step 3:把 restore 动作接到 StoreKit

StoreKit 2:

func restore() async {
    isRestoring = true
    do {
        try await AppStore.sync()  // 露出任何漏掉的 transaction
        for await result in Transaction.currentEntitlements {
            guard case .verified(let txn) = result else { continue }
            await applyEntitlement(productID: txn.productID)
        }
        statusMessage = "Restored successfully."
    } catch {
        statusMessage = "Restore failed: \(error.localizedDescription)"
    }
    isRestoring = false
}

StoreKit 1:

SKPaymentQueue.default().restoreCompletedTransactions()
// 实现 paymentQueueRestoreCompletedTransactionsFinished + paymentQueue:restoreCompletedTransactionsFailedWithError

Step 4:进度和结果显式露出

点击后:

  • 立即出 loading spinner 或进度文字。
  • 成功:显示 “Your purchases were restored” 列出找到的产品名。2 秒后自动收起。
  • 无购买:显示 “No previous purchases found”
  • 错误:显示错误信息加重试按钮。

审核员点击后 5 秒内必须看到东西在发生。

Step 5:在 App Review notes 里写明路径

加到 App Review Information:

RESTORE PURCHASES
- Available on the paywall (bottom, "Restore Purchases" link).
- Also available in Settings > Restore Purchases.
- Tap the button; on a fresh install, this triggers StoreKit sync.
- Test with sandbox tester apple-iap-sandbox@yourdomain.com (Pro is pre-purchased).

Step 6:重新提交

App Store Connect → App Store → 点 build → Submit for Review。Restore 按钮这类不动其他 UI 的修复过审很快,预期 24 小时中位。

怎么确认已经修好

  • 冷装 build 在付费墙不滚动就能看到 Restore Purchases。
  • Settings 有顶层 Restore Purchases 行。
  • 点 Restore 立即显示进度反馈。
  • 有购买记录的沙盒账号点 Restore 后立即恢复权限。
  • 无购买记录的新账号点 Restore 看到 “No previous purchases found”

如果还是没修好

  1. 拒回还在就在 Resolution Center 附 30 秒 QuickTime 屏录,演示从付费墙到结果的完整 Restore 流。
  2. 确认 restore 调用真的接到 StoreKit(Console.app 看 StoreKit.Transaction 日志);没接到就是按钮在代码也坏。
  3. 用多个沙盒账号测一遍,确认不同账号状态都能跑通。
  4. 检查冷启动时 Restore 按钮是否启用(不是灰的);有些实现把 Restore gating 在网络调用后。

预防建议

  • 把 Restore Purchases 当付费墙和设置必备 UI 元素——纳入组件库。
  • 加 UI 测试:每个 PR 都断言付费墙和设置里 Restore 按钮可见。
  • 把 Restore 接线写进项目脚手架 / 模板,新项目永远不漏。
  • 每次付费墙重设计都审 diff,专门核查 Restore 有没有被重构掉。
  • 跟踪 Restore 点击频率;发版后突然下降意味着回归。

相关阅读

标签: #排查 #App Store #App 审核 #IAP / 内购