For restorable in-app purchases, give users a visible Restore Purchases action and make its result clear. Silent entitlement refresh may be useful, but it does not provide an obvious user-initiated path.
Add the control in a discoverable place, connect it to the appropriate StoreKit restore or sync flow, refresh entitlements, and show success, no-purchase, and error outcomes.
The workflow below was checked against App Review Guidelines and StoreKit AppStore.sync() on 4 August 2026. The first source controls the rule or workflow; the second supports the article-specific implementation boundary.
The short version
- Confirm the product type is restorable.
- Place Restore Purchases on the paywall or another obvious purchase-management screen.
- Call StoreKit’s supported sync or restore mechanism.
- Refresh entitlement state after the call.
- Show a visible result for success, nothing found, and failure.
Use the iOS subscription and in-app purchase review guide to verify the whole purchase chain. This page focuses on the narrower recovery contract: which products are restorable, where the user initiates restore, and whether the entitlement actually returns.
Start with the product type
Auto-renewable subscriptions and non-consumables represent entitlements that may need recovery. Consumables have different semantics because they are depleted. Check the type in App Store Connect before designing the restore experience or replying to a rejection.
Apple’s own guideline is explicit about the underlying obligation: “Any credits or in-game currencies purchased via in-app purchase may not expire, and you should make sure you have a restore mechanism for any restorable in-app purchases.” If your rejection references a missing or broken restore path, this is the exact line the reviewer is testing against, not a matter of interpretation.
Give the user an explicit action
Place a plain Restore Purchases control where someone managing a purchase would look, commonly the paywall or account settings. One well-labelled, reachable control is better than two broken copies. If your app refreshes entitlements automatically, keep that behaviour, but do not rely on it as the only visible recovery path.
Connect restore to entitlement refresh
With StoreKit 2, Apple documents AppStore.sync() for user-initiated synchronization. After synchronization, re-evaluate current entitlements and update your UI. Catch errors and distinguish them from a valid result where the account owns nothing. The user should never have to infer success from a button that simply stops spinning.
Test the second-device story
Use an account that owns a restorable product, then install the app in a clean state. Trigger restore without purchasing again. Confirm the paid feature unlocks and remains unlocked after relaunch. Repeat with an account that owns nothing so the empty result remains understandable.
Keep a small evidence note with:
- app version and build number;
- product identifiers tested;
- account type and storefront;
- time of the test and observed result;
- whether the entitlement changed after relaunch.
Run the first checks in order
Run these six checks in order against the exact submitted build, not a debug configuration. Each one isolates a different layer, so stop at the first failure and record what you saw before moving to the next.
- In App Store Connect, open the product record and confirm its type is auto-renewable subscription or non-consumable, not consumable.
- In the UI, locate the Restore Purchases control and confirm it sits on the paywall or an account or purchase-management screen a user would actually visit.
- In code, confirm the control’s handler calls AppStore.sync() or your restore API, not a stub or a no-op.
- After the call resolves, confirm your entitlement state updates and the UI reacts to it rather than staying frozen.
- Trigger the flow three times: an account that owns the product, an account that owns nothing, and a forced error. Confirm each produces a distinct, visible message.
- Delete and reinstall the app, or use a second device, sign in with the owning account, tap Restore, and confirm the feature unlocks and survives a relaunch.
A defect at step 1 or 2 is usually a metadata or placement fix. A defect at step 3 or 4 needs a new build. A defect that only shows up at step 6 often means the flow already works but nobody can find it, which points at Review Notes rather than code.
Choose the next action
| What you find | Next action | New build? |
|---|---|---|
| The product’s type in App Store Connect is Consumable when it should be Non-Consumable or a subscription | Correct the type before resubmitting; a consumable does not carry the same restore obligation | Usually no, unless the purchase code also assumed the wrong type |
| Restore Purchases is present but its handler never calls a real StoreKit or RevenueCat restore method | Wire the control to the actual restore call, rebuild, and retest on a clean install | Yes |
| The restore call succeeds but the UI never reflects the refreshed entitlement | Fix the state update after the call resolves, then rebuild | Yes |
| The flow works, but the reviewer could not find the control or reach the paywall | Improve the path in Review Notes and, if warranted, reposition the control | Usually no |
| A consumable is described or labelled as restorable | Correct the description and product classification, not the code | Usually no, unless the purchase logic also treated it as restorable |
Do not appeal a reproducible defect from row two or three. Do not upload a replacement binary when the fix is a type field or a copy change you can still edit in a draft submission. Let the failing check, not a guess, choose the row.
Failure patterns to look for
- A restore label exists but triggers no StoreKit action.
- The action runs but the UI never refreshes entitlements.
- The control is hidden after onboarding or behind an active subscription.
- A consumable is incorrectly described as restorable.
These four patterns account for most restore rejections in the maintained AppRejectKit subscription corpus, cross-checked against the sources below. They tell you where to look inside your own build, not how often Apple cites each one.
Write App Review Notes as a test script
For a restore-specific submission or resubmission, the reviewer needs to reproduce one thing: a purchase made once, recovered on demand. Give them exactly that path.
Include:
- The exact product identifier or identifiers that are restorable, and their App Store Connect type.
- A sandbox or demo account that already owns the product, with credentials if login is required.
- The precise tap sequence from launch to the Restore Purchases control.
- What success looks like on screen, for example a paywall closing, a badge appearing, or a feature unlocking.
- What “nothing to restore” looks like, so a reviewer testing with a fresh account does not read it as a bug.
Do not describe a flow you have not tested on the exact build you submitted. If the sandbox environment behaves differently from production for a known reason, say so once, briefly, and move on.
What not to change blindly
A broken restore button is rarely a reason to recreate the in-app purchase product, rotate its identifier, or switch it to a different type. Those changes reset App Store Connect state and can introduce a second problem while the original UI or code defect stays exactly where it was.
The same caution applies to App Store Connect screenshots from an undated blog post or forum thread. Apple renames fields and reorganizes the purchase and subscription screens. Confirm the current labels in your own account against the official help page cited below before you follow a walkthrough.
Verification note: The restore requirement and the StoreKit method referenced above were checked against Apple’s own documentation on 4 August 2026. Confirm both again before publication or a later resubmission, since Apple can rename App Store Connect fields or change the supported StoreKit APIs.
FAQ about restore purchases App Store review
Does adding or fixing a Restore Purchases button always require a new build?
No. If the control and its StoreKit call are already correct and the reviewer simply could not reach it, a clearer Review Notes path can resolve the rejection without a new binary. If the handler is missing, wrong, or the UI never refreshes after the call, you need a new build.
Can a successful sandbox restore test guarantee approval?
No. It confirms the mechanism works for the account, build, and storefront you tested. App Review separately checks the product’s declared type and description, the paywall copy, and whether the control is reachable during their own test.
Should I delete and recreate the in-app purchase product?
Only if you have evidence the product record itself is broken and Apple’s support path cannot fix it, for example a permanently wrong type. Recreating a product issues a new identifier, which usually creates more entitlement and metadata work than the original defect.
What should I put in Review Notes for a restore-related rejection?
The product identifiers involved, an account that already owns the product, the exact tap path to the Restore control, and the visible result you expect. Skip general claims about the app; the reviewer needs the shortest path to reproduce restore specifically.
Conclusion
A restore rejection almost always comes down to one of three things: the product’s type in App Store Connect, whether the control calls a real StoreKit method, or whether the reviewer could find and use it. Test the exact submitted build against all three before you touch anything else.
Keep the evidence from that test, the product identifiers, and the account state with your reply. It turns the next review into a fact check instead of another guess.
Sources and references
- App Review Guidelines: accessed 4 August 2026.
- StoreKit AppStore.sync(): accessed 4 August 2026.