Restore Purchases Button Missing: Fix the 3.1.1 Rejection

Apple flagged your app under Guideline 3.1.1 for a missing Restore Purchases button. Add the button to your paywall and Settings, wire it to StoreKit, and resubmit.

Fastest fix: put a visible Restore Purchases button on your paywall (below the primary buy CTA) and as a top-level row in Settings, wire it to a StoreKit restore call (AppStore.sync() for StoreKit 2), show a result alert, and resubmit with a one-line note telling the reviewer exactly where the button is. This is a UI-only change for most apps and clears App Review fast.

Your rejection cites Guideline 3.1.1 — Apple’s wording is usually a line like “We were unable to locate a way to restore previously purchased non-consumable in-app purchases or subscriptions in your app.” Three patterns trigger it: (1) your paywall has crisp Subscribe and Buy Now buttons but no Restore; (2) you do have a Restore action, but it’s buried under Settings → Account → Subscriptions and the reviewer can’t find it from the paywall they hit on launch; or (3) your app uses an email-and-password login that you considered “the restore path,” but Apple still wants a button that calls StoreKit directly.

For any non-consumable IAP or auto-renewable subscription, Apple’s App Review Guideline 3.1.1 expects a visible, working Restore Purchases entry point. (Consumables and non-renewing subscriptions do not require one.) As of June 2026 the requirement and the fix below are unchanged from prior years; only the App Store Connect submission flow has been renamed (see Step 6).

Which bucket are you in?

Match your situation to the row, then jump to the fix.

SymptomLikely causeFix
Paywall shows only buy/subscribe CTAsRestore never addedStep 1
Restore exists but reviewer “couldn’t find it”Buried in deep navigationStep 1 + Step 2
You point Apple to your email loginNo StoreKit call wiredStep 3
Restore worked before, gone after a redesignUI regressiondiff the paywall, then Step 1
Button is there, reviewer “saw nothing happen”No progress/result UIStep 4
Restore only appears after sign-upLogin wall in front of restoremove it out, Step 1

Common causes

Ordered by hit rate.

1. Paywall has only Buy buttons, no Restore

The original design optimized for conversion: every pixel on the paywall says buy. Restore was treated as a “later” feature and never added.

How to spot it: Open your paywall on a fresh build. If you cannot count a “Restore Purchases” link or button without scrolling, this is your case. Reviewer-side: their rejection cites a paywall screenshot.

2. Restore exists but is buried in deep navigation

Restore lives at Settings → Account → Subscription → Manage → Restore. A reviewer with 60 seconds will not find it. Apple wants restore reachable in one or two taps from the paywall or main settings.

How to spot it: Count the taps from launch to the Restore button. If it’s more than 2, it’s too deep.

3. The app treats account login as restore

You ship an email login. Users log into their account, and the server tells them they’re Pro. You assume this is “restore.” Apple disagrees — restore must call StoreKit and reconcile with Apple’s purchase history, not just your account database.

How to spot it: Search your code for restoreCompletedTransactions or Transaction.currentEntitlements. If neither is called from a UI button, you’re missing the StoreKit restore primitive.

4. Restore exists on iOS but not on a new redesigned screen

You redesigned the paywall recently. The old paywall had Restore; the new one launches without it because the engineer porting the design missed the small text link. iOS gets the missing UI in production while Android keeps working.

How to spot it: Diff the current paywall view against the previous version’s git history. If a Restore Purchases view or button was removed, that’s the regression.

5. Restore action runs but doesn’t surface results

You have the button. Tapping it calls StoreKit. But nothing happens visibly — no loading spinner, no success alert, no failure message. Reviewer taps, sees nothing, assumes broken, rejects.

How to spot it: Manually tap Restore on a clean install. If you don’t see explicit feedback within 5 seconds, the reviewer didn’t either.

6. Restore is behind a login wall on a free tier

The paywall is reachable but the Restore button is only visible after the user creates a free account. Apple expects restore to be reachable without any sign-up; otherwise a returning paid user has to create a new free account just to restore.

How to spot it: Cold install, do not sign up, navigate to the paywall. If Restore isn’t visible there, fix.

Information to collect

  • The reviewer’s rejection text and which Guideline section it cites.
  • Screenshots of your current paywall, settings, and any restore flow.
  • Your StoreKit version (SK1 vs SK2) and current restore implementation.
  • Whether your app has free-tier users who shouldn’t see paywall on launch.
  • Analytics on how often legitimate users currently tap Restore (helps you UX-tune the new placement).

Shortest path to fix

Step 1: Add a Restore Purchases button to the paywall

In your paywall view, place a clear “Restore Purchases” text link or button. Standard placement: below the Buy / Subscribe primary CTA, with smaller but readable text (>= 14pt). Don’t visually downplay it, and make sure it’s visible without scrolling — reviewers act on the screen they land on.

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

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

Step 2: Add a second entry in Settings

Even users who never hit your paywall (e.g., returning subscribers on a fresh install) need a path to restore. Add Settings → Restore Purchases as a top-level row, not buried under Account.

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

Step 3: Wire the restore action to StoreKit

For StoreKit 2, call AppStore.sync() — Apple’s documented equivalent of the old restoreCompletedTransactions() / SKRefreshReceiptRequest. Two things matter here:

  • AppStore.sync() shows a system sign-in prompt asking the user to authenticate with their Apple Account. Because of that, only ever call it from an explicit button tap, never automatically on launch.
  • In normal operation you don’t need sync() at all — Transaction.currentEntitlements already reflects the user’s active purchases, and StoreKit keeps it current in the background. sync() is the manual force-refresh for the restore button.
func restore() async {
    isRestoring = true
    do {
        try await AppStore.sync()  // force-refresh; triggers Apple Account auth prompt
        var restoredAny = false
        for await result in Transaction.currentEntitlements {
            guard case .verified(let txn) = result else { continue }
            await applyEntitlement(productID: txn.productID)
            restoredAny = true
        }
        statusMessage = restoredAny ? "Restored successfully." : "No previous purchases found."
    } catch {
        statusMessage = "Restore failed: \(error.localizedDescription)"
    }
    isRestoring = false
}

For StoreKit 1:

SKPaymentQueue.default().restoreCompletedTransactions()
// implement paymentQueueRestoreCompletedTransactionsFinished(_:)
// and paymentQueue(_:restoreCompletedTransactionsFailedWithError:)

Step 4: Surface progress and result clearly

On tap:

  • Show a loading spinner or progress text immediately.
  • On success: show “Your purchases were restored” with the product names found. Auto-dismiss after 2 seconds.
  • On no purchases: show “No previous purchases found”.
  • On error: show the error message and a retry button.

A reviewer must see something happen within 5 seconds of tapping.

Step 5: Document the path in App Review notes

Add to 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: Resubmit

In App Store Connect, open your app, attach the new build to the version, then click Add for Review and Submit for Review (App Store Connect split the old single “Submit for Review” action into this two-step submission flow). If the rejection is still open in Resolution Center, you can also reply there to push the same build through. Restore-button fixes that don’t change other UI go through quickly; as of June 2026, Apple publicly states that 90% of submissions are reviewed within 24 hours (real-world times during high-volume periods can run longer).

How to confirm the fix

  • A cold-install build shows Restore Purchases on the paywall without scrolling.
  • Settings has a top-level Restore Purchases row.
  • Tapping Restore shows immediate progress feedback.
  • A sandbox tester with an existing purchase sees their entitlement applied after tapping Restore.
  • A fresh tester with no purchases sees “No previous purchases found.”

If it still fails

  1. If the rejection persists, attach a 30-second QuickTime video in Resolution Center showing the new Restore flow from paywall to result.
  2. Verify the restore call actually contacts StoreKit (check Console.app logs for StoreKit.Transaction); if not, your code is broken even though the button exists.
  3. Try restoring with multiple sandbox testers to confirm the path works for different account states.
  4. Check that the Restore button is enabled (not greyed out) on a fresh launch; some implementations gate Restore behind a network call.

Prevention

  • Treat Restore Purchases as a required UI primitive on every paywall and settings screen — bake it into your component library.
  • Add a UI test that asserts the Restore button is visible on the paywall and in Settings on every PR.
  • Include Restore wiring in your boilerplate / starter template so a new project never ships without it.
  • Audit your paywall in every redesign — review the diff specifically for whether Restore survived the refactor.
  • Track how often Restore is tapped; a sudden drop after a release signals a regression.

FAQ

Does every app with in-app purchases need a Restore Purchases button? No. Guideline 3.1.1 only requires a restore mechanism for restorable products: non-consumable IAPs and auto-renewable subscriptions. Consumables (coins, one-time credits) and non-renewing subscriptions are not restorable and don’t need the button.

Isn’t my “Sign in” button enough — won’t logging in restore the subscription? Not for App Review. Apple wants a button that calls StoreKit and reconciles against the Apple Account’s purchase history, independent of your own account system. A returning subscriber must be able to regain access without creating or signing into an account. Keep your login, but add the StoreKit restore button too.

Where exactly must the Restore button be? At minimum, on the paywall (visible without scrolling) and as a top-level row in Settings. Apple’s own guidance says to put it “where users and reviewers will find it.” One reachable spot can pass, but having both is the safe default and stops “buried navigation” rejections.

Why does tapping Restore pop up an Apple Account sign-in prompt? That’s AppStore.sync() working as designed — it forces a refresh from the App Store and authenticates the user. Call it only on an explicit tap, never automatically, or you’ll annoy users with surprise auth prompts. If you just need to check current access, read Transaction.currentEntitlements instead, which does not prompt.

The reviewer says the button does nothing. What do I show them? Surface visible feedback within ~5 seconds: a spinner on tap, then a success alert listing restored products, a “No previous purchases found” message, or an error with a retry. Reviewers reject what looks broken even when the call succeeds silently. Attach a short screen recording of the working flow in Resolution Center.

How fast is the re-review? A restore-only UI fix is among the quickest re-reviews. Apple states 90% of submissions are reviewed within 24 hours as of June 2026, and a clearly explained Notes-for-Review entry shortens it further.

Tags: #Troubleshooting #App Store #App review #IAP