Monetization

Flutter Restore Purchases: Why Apple Rejects Apps Without It

If your Flutter app sells subscriptions or non-consumable in-app purchases, Apple requires you to provide a "Restore Purchases" flow that lets users recover their purchases after reinstalling the app or switching devices. Apps without a visible restore option get rejected during App Store review. This article covers how to implement restore purchases in Flutter using the in_app_purchase package, how to validate entitlements server-side, and the common mistakes that trigger rejection.

Quick checklist: What Apple requires

RequirementDetails
Visible restore buttonMust be accessible in the app UI, not hidden
Handles all product typesSubscriptions, non-consumables, and consumables
Server-side validationReceipts must be verified server-side, not trusted on-device
Graceful failure handlingShow a clear message if restore fails or finds no purchases
Works after reinstallRestore must work when the app is reinstalled on a new device

Why restore purchases matters

When a user reinstalls your app or gets a new phone, their previous purchases are not automatically restored. The App Store maintains a purchase history tied to the user's Apple ID, but your app must actively query that history and re-apply the entitlements. Without a restore flow, users who paid for a subscription or one-time purchase lose access and contact support — or request a refund.

Apple reviewers specifically test the restore flow during review. If the restore button is missing, hidden, or broken, the app will be rejected under the In-App Purchase guideline.

Implementation checklist

1. Add a visible "Restore Purchases" button

Place a "Restore Purchases" button in your app's Settings, Account, or subscription management page. The button should be clearly labeled and accessible without navigating through multiple hidden menus.

2. Query past purchases with in_app_purchase

Use the Flutter in_app_purchase package to query the user's purchase history. Call InAppPurchase.instance.restorePurchases() when the user taps the restore button. This triggers the store to return all past purchases for the user's Apple ID.

3. Verify receipts server-side

Send the purchase receipts to your backend and verify them with Apple's App Store Server API. Never trust purchase state on the client side — a user could modify the app's local storage to unlock features without paying. Server-side validation ensures only legitimate purchases are restored.

4. Re-apply entitlements

After server-side verification, update the user's entitlements in your database. If the user had a subscription, re-activate it. If they had non-consumable purchases, unlock the content. Sync the entitlements back to the app so the UI updates immediately.

5. Handle edge cases

Handle the case where no purchases are found (show "No purchases to restore"), where the receipt is expired (show a renewal prompt), and where the network fails (show a retry option). These edge cases are what Apple reviewers test.

6. Test with a sandbox account

Before submitting, test the restore flow using a sandbox tester account in App Store Connect. Make a purchase, delete and reinstall the app, then test restore. This verifies the full flow works end-to-end.

Common rejection mistakes

  • No restore button: The app sells subscriptions but has no visible restore purchases option.
  • Hidden restore: The restore button exists but is buried in a sub-menu or settings page that reviewers cannot find.
  • Client-side only validation: The app checks local storage for purchase state instead of verifying with Apple's server. This is a security risk and will be rejected.
  • No failure handling: When restore fails (no internet, expired receipt), the app shows no feedback or crashes.
  • Doesn't handle reinstall: The restore flow works on the same device but fails when the app is installed on a new device.

Before you submit

Restore purchases is one of the most common In-App Purchase rejection reasons. Run a free Launch Score check to verify your monetization implementation before submitting to App Store review.

Run your free Launch Score

Frequently asked questions

Does my app need a restore purchases button?

If your app sells subscriptions or non-consumable purchases, Apple requires a visible restore purchases button. The button must be accessible in the app UI, not hidden in a sub-menu. Apps without a restore option will be rejected during App Store review.

How do I implement restore purchases in Flutter?

Use the in_app_purchase Flutter package. Call InAppPurchase.instance.restorePurchases() when the user taps the restore button, verify the receipts server-side with Apple's App Store Server API, and re-apply the user's entitlements. Test with a sandbox account before submitting.

Why was my app rejected for missing restore purchases?

Common reasons include no visible restore button, the button is hidden in a sub-menu, client-side only validation without server-side receipt verification, or no failure handling when restore fails. Apple reviewers specifically test the restore flow during review.