Imagine this: a user buys a subscription, switches devices, reinstalls your app, and suddenly loses access. It is an easy way to lose trust.
That’s exactly why the “Restore purchase” exists. It might seem like an optional button, but it’s a must, especially for subscription-based apps.
This option lets users regain access to previously purchased content without paying again.
It’s a small button with a big role in trust, compliance, and user retention.
Why it matters
- User trust: If someone paid once, they expect to keep access, no matter the device.
- Support load: Confusing or missing restore logic = angry users + more tickets.
- App Store compliance: Apple and Google both expect you to include a restore option for certain product types.
What purchases can be restored
Restorable purchases include:
- Non-consumables: One-time purchases like lifetime access or premium features.
- Auto-renewable subscriptions: Monthly/yearly plans that sync across devices.
- Non-renewing subscriptions: Fixed-term access (e.g., a 3-month pass).
Not restorable:
- Consumables like coins, extra lives, or one-time boosts. These are meant to be used up.
How the “Restore purchase” button works
- UI/UX. Add a “Restore purchase” button — usually in the settings or directly on the paywall.
- Trigger. On tap, trigger a restore via:
- SKPaymentQueue.restoreCompletedTransactions() for iOS
- BillingClient.queryPurchaseHistoryAsync() for Android
- Or your backend logic if you manage entitlements server-side
- SKPaymentQueue.restoreCompletedTransactions() for iOS
- Update access. After restore completes, refresh the user’s subscription status and unlock content.
- Add a helpful copy. A simple line helps reduce confusion: “Use this if you’ve switched devices or reinstalled the app.”
- Avoid redundancy. If the user already has access, disable the button or hide it entirely.
- Account logic. For apps with login: tie subscriptions to user accounts and sync via backend to simplify cross-device restoration.
How restoring purchases works on iOS
On iOS, restoring purchases refreshes the app receipt and re-delivers products tied to the user’s Apple ID. The process runs quietly in the background, and the experience should feel simple and clear.
Here’s how to do it using native StoreKit (pre‑StoreKit 2):
1. Start the restore process
Call restoreCompletedTransactions() to trigger the restore flow:
| SKPaymentQueue.default().restoreCompletedTransactions() |
This tells StoreKit to look for any past purchases tied to the current Apple ID.
2. Handle the restored transactions
Listen to the payment queue and check for .restored transaction states. Then, unlock content and mark the transaction as finished:
| func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for transaction in transactions { if transaction.transactionState == .restored { // Unlock access based on restored product unlockContent(transaction: transaction) SKPaymentQueue.default().finishTransaction(transaction) } } } |
Don’t forget to call finishTransaction(_:). Otherwise, StoreKit may continue to retry the same transaction.
3. Handle failures gracefully
Restore may fail due to invalid receipts, network issues, or if the Apple ID has no eligible purchases. Show clear feedback to users and log the error for debugging.
Using Adapty SDK? We handle all of this, including receipt validation and edge cases, with a single method call.
How restoring purchases works on Android
On Android, restoring purchases means checking the user’s purchase history via Google Play and reactivating any non-consumable products or active subscriptions. There’s no built-in “restore” button like on iOS. That means you handle it manually through the billing API.
Here’s how to do it using the Google Play Billing Library:
1. Query purchase history
Use queryPurchaseHistoryAsync() to fetch the user’s past purchases:
| billingClient.queryPurchaseHistoryAsync( BillingClient.SkuType.SUBS ) { billingResult, purchaseHistoryList -> if (billingResult.responseCode == BillingClient.BillingResponseCode.OK && purchaseHistoryList != null) { for (purchase in purchaseHistoryList) { // Restore subscription access restoreEntitlement(purchase) } } } |
You can also query in-app purchases by passing BillingClient.SkuType.INAPP.
2. Validate & unlock
After retrieving the purchase history, validate receipts (ideally server-side), and grant access accordingly. Be sure to verify signature data to prevent spoofing.
Adapty handles purchase validation, user access logic, and cross-platform syncing out of the box.
3. UX considerations
Since Android users don’t expect a “Restore Purchases” button, you can:
- Trigger restoration automatically on login or app launch
- Or offer a discreet option under Settings for transparency
Either way, make sure the restore logic runs silently in the background and updates the UI when access is restored.
Final thoughts
The “Restore purchase” button may look simple, but it plays a critical role in the subscription journey.
- It builds trust.
- It reduces churn.
- It keeps you compliant.
Make it visible. Make it work. And make it easy to understand.




