Skip to content
Bugwise
Menu
Features & Flows

In-App Purchases and Subscriptions

How App Store and Google Play purchases, free trials and subscriptions work, how to test them with sandbox accounts, and the bugs that cost real money.

40 min intermediate Mobile

Maja pays €39.99 for a year of Premium in her meditation app. A month later she gets a new phone, installs the app, taps Restore purchases… and nothing happens. No error, no Premium. She writes a one-star review (“They took my money!”), asks Apple for a refund and gets it. The company loses the subscription, the money, and a little of its rating.

In-app purchases are where bugs turn directly into lost money: either the customer pays and doesn’t get what they bought, or they get it without paying. Both happen all the time, mostly in situations a quick “tap Buy, it works” test never reaches: a second device, a refund, an expired card, a renewal in the middle of the night. This lesson shows you how purchases really work and how to test all of those situations without spending a cent.

How it works

What you can sell

Both stores know the same basic kinds of products:

Product typeExamplesRestorable?
ConsumableCoins, gems, extra lives, 10 photo credits. Used up, can be bought again.No: once used, it’s gone
Non-consumableRemove ads, “Pro” unlock, a filter pack. Bought once, kept forever.Yes, on every device
Auto-renewable subscriptionPremium monthly or yearly. Renews until the user cancels.Yes
Non-renewing subscriptionA 3-month season pass. Ends by itself; the app must track the end date.Yes, by your own server

Apple uses exactly these four names. Google Play calls them one-time products (consumable or not, depending on whether the app consumes them) and subscriptions (auto-renewing or prepaid).

The store handles the money

When you buy something inside an app, you don’t pay the app. You pay Apple (App Store) or Google (Google Play Billing). The store shows its own purchase sheet, charges the payment method saved in the user’s account, and takes a commission. The app never sees the card number. It only gets a signed record that a purchase happened.

That’s different from buying a pizza or a taxi ride, where the app uses its own payment gatewayPayment gatewayA third-party service that processes card payments for an app or website.Open in glossary → (covered in Shopping cart and checkout). As a rule of thumb, digital content and features used inside the app go through the store; physical goods and services go through a normal checkout. The exact rules differ between countries and change over time, so check what applies to your app.

Your appApp Store / Google PlayYour server
  1. 1. PaywallLoads the products and their prices from the store, then the user taps Subscribe.
  2. 2. Store purchase sheetThe system sheet shows the price, trial and payment method. The app cannot change or see it.
  3. 3. Store charges the userCard, carrier billing or gift card balance, plus any bank check (3-D Secure). The app never sees card details.
  4. 4. App receives the transactionSuccess, canceled, pending (Ask to Buy, slow payment) or failed. Each needs its own screen.
  5. 5. Server verifies itChecks the signed transaction or purchase token with the store and saves an entitlement for the account.
  6. 6. Premium unlocksImmediately, without a restart. Then the app finishes (iOS) or acknowledges (Android) the purchase.
  7. Later: store notificationsRenewals, billing problems, cancellations and refunds arrive as server notifications and update the entitlement.

The entitlement on your server, not a flag on the phone, is the single source of truth for "is this user Premium?"

  1. The app loads products from the store. IDs like premium_monthly are configured in App Store Connect or the Play Console. The store returns the name and the price in the user’s currency.
  2. The user taps Subscribe, and the store’s own sheet appears with the price, the trial and the payment method. The app can’t change what’s on it.
  3. The store charges the user. This can include a bank check (3-D Secure3-D SecureAn extra step where the card's bank asks the shopper to confirm an online payment.Open in glossary →), a parent’s approval, or a slow payment method that takes days.
  4. The app gets a result: purchased, canceled by the user, pending, or failed. Each one needs its own UI.
  5. The server verifies the purchase with Apple or Google and stores an entitlement (“Maja has Premium until 15 Nov 2026”) on her account.
  6. The app unlocks the content right away, then marks the transaction as handled: it finishes it on iOS and acknowledges it on Android. On Google Play, a purchase that isn’t acknowledged within three days is automatically refunded.
  7. Later, the store tells the server about changes: renewals, failed payments, cancellations, refunds. The server updates the entitlement, and the app follows.

The life of a subscription

A subscription isn’t a single purchase. It’s a state machine that keeps changing for months, mostly while the app is closed:

StateWhat it meansShould the user have Premium?
Free trial / intro offerFree (or cheaper) for a while, then the full price. Usually once per user.Yes
ActivePaid, renews automatically.Yes
CanceledAuto-renew is off, but the paid period hasn’t ended.Yes, until the end date
Grace periodA renewal payment failed (expired card), the store keeps retrying.Yes, if the app enabled a grace period
Billing retry / account holdPayment still failing after the grace period.No, but it comes back if the payment is fixed
ExpiredEnded and not renewed.No
Refunded / revokedThe store gave the money back.No, immediately

Plan changes are their own little world:

  • Upgrade (e.g. to a higher tier): on the App Store it usually takes effect immediately, and the unused part of the old plan is refunded proportionally.
  • Downgrade: usually takes effect at the next renewal date.
  • Crossgrade (same level, e.g. monthly to yearly): on the App Store it depends on the duration; on Google Play the app chooses a replacement mode (with proration, deferred, and so on).
  • In every case the user must end up with one subscription and one charge per period, never two.

Restore, new devices and family

People change phones, reinstall apps and own tablets. Everything restorable (non-consumables and subscriptions) must follow the store account:

  • Restore purchases: Apple’s review guidelines require a way to restore restorable purchases, usually a Restore purchases button on the paywall or in settings. On Android, apps typically query existing purchases automatically when they start, but many also show a Restore button.
  • Second device: the same Apple ID or Google account on a tablet should get Premium after a restore, or automatically.
  • App account vs store account: if your app has its own login, which one owns the subscription? What happens when Maja logs in to her husband’s account on her phone? The spec must say, and you must test it.
  • Family Sharing (Apple): if the developer enables it, family members can share non-consumables and subscriptions. They should get access, and lose it when they leave the family group. On Google Play, sharing in-app purchases with family is much more limited, so check the current rules.

Receipts, servers and entitlements

The phone is not a safe place to decide “is this user Premium?”. A flag in local storage can be edited, lost on reinstall, or simply forgotten when a refund happens. Robust apps work like this:

  • The app sends the signed transaction (iOS) or the purchase token (Android) to your server.
  • The server verifies it with Apple or Google and stores an entitlement with an end date.
  • The server receives store notifications (App Store Server Notifications, Google Play Real-time developer notifications) about renewals, refunds and billing problems.
  • Every device asks the server, so Premium follows the account, not the phone.

Cross-platform entitlements build on this. If Maja bought Premium on the company’s website (paid by card through a normal payment gateway), her iPhone app should unlock Premium when she logs in, because the server knows about it. Then you test the hard questions: what if she also subscribes on iOS (double payment)? Where can she cancel? What does the app show if the web subscription expires?

Prices, currencies and tax

  • Prices are set per country (storefront) in the store, often not as an exact conversion: $4.99 in the US might be €5.49 in Germany.
  • The app must show the price string the store returns, never a hard-coded $4.99. The store also formats it for the user’s localeLocaleRegional settings that control language and formats for dates, numbers and currency.Open in glossary →.
  • The store account’s country decides the currency, not the phone’s language. A Serbian Google account on an English-language phone pays in dinars.
  • Tax: in many countries the store price already includes VAT; in some, like the US, sales tax may be added at checkout. That’s another reason to show the store’s price, not your own calculation.
  • Price increases for existing subscribers are announced by the store, and depending on the store, country and size of the increase, the user may have to accept it or the subscription ends.

Interrupted and pending purchases

Not every purchase completes in one go:

  • Ask to Buy (Apple) / parental approval: a child taps Buy, a parent must approve it on their own device. The purchase is pending, maybe for hours, maybe forever.
  • Bank verification (Strong Customer Authentication, 3-D Secure): in Europe the bank may ask the user to confirm in their banking app. The store handles it inside its sheet, but the purchase can be interrupted or take longer. The same idea in normal checkouts is covered in Shopping cart and checkout.
  • Slow payment methods: in some countries Google Play lets people pay in cash at a shop. The purchase stays pending until they do.
  • The app is killed mid-purchase, the network drops, or the phone restarts. When the transaction finally completes, the app must deliver it on the next launch.

Testing without spending money

You never test purchases with your own card. Both stores have test environments:

AppleGoogle Play
Local testingStoreKit Testing in Xcode: a .storekit configuration file, no server needed. Speed up time, simulate failures, Ask to Buy, interrupted purchases and refunds.Test the billing flows of a build installed from a Play testing track
Test accountsSandbox Apple Accounts, created in App Store Connect (Users and Access › Sandbox), signed in on the device in the sandbox account settings, not as the main Apple IDLicense testers, added in the Play Console (Settings › License testing)
PaymentFree, no real chargeTest cards: “always approves”, “always declines”, slow cards that approve or decline after a few minutes
Beta buildsTestFlightTestFlightApple's service for distributing beta builds of iOS apps to testers before App Store release.Open in glossary → purchases are free (no real charge); subscriptions renew once a day, up to 6 timesInternal, closed and open testing tracks

Subscriptions renew much faster in test environments, so you can watch a whole year of renewals during a coffee break. Typical values for Apple sandbox accounts and Google Play license testers (they change, so check the current docs; TestFlight is slower, see above):

Real periodApple sandboxGoogle Play test
1 weekabout 3 minutesabout 5 minutes
1 monthabout 5 minutesabout 5 minutes
1 yearabout 1 hourabout 30 minutes

Test subscriptions also renew only a limited number of times and then stop on their own, and Apple lets you adjust the sandbox renewal rate in the sandbox account settings. Keep a stopwatch or a log open, because renewals happen while you’re doing something else.

Example requirements

What usually goes wrong

#BugWhat you seeTypical severity
1Restore doesn’t workNew phone or reinstall: Restore spins and nothing happens, or “No purchases” for a paying userCritical
2Paid but not unlockedPurchase succeeds, content stays locked until restart, or foreverCritical
3Premium after expiry or refundAccess stays after the subscription ended or the money was returnedHigh (lost revenue)
4Premium lost too earlyCanceling removes access immediately, or a renewal isn’t picked upHigh
5Trial offered againPaywall says “7 days free”, the store charges immediatelyHigh (misleading, store rejection)
6Wrong or hard-coded prices$4.99 shown to a user who pays in dinars or eurosHigh
7Double charge on plan changeUpgrading creates a second subscription instead of replacing the firstCritical
8Pending purchases ignoredAsk to Buy approved hours later, but nothing unlocksHigh
9Purchase not acknowledged / finishedAndroid refunds it after three days; iOS replays the transaction on every launchCritical
10Consumables delivered twice or not at all100 coins become 200, or 0, after a crash or a slow networkHigh
11Cancel in the store sheet treated as an errorUser cancels and sees “Purchase failed, try again”Low–Medium
12Missing legal info on the paywallNo renewal terms, no Terms or Privacy links, unclear trial endMedium (store rejection)

Find the bug

Stillwater, a meditation app, sells Premium on Google Play. You’re signed in as a license tester, so every purchase uses a test card, and you control a test clock. The demo has six bugs. Explore like a tester:

  • Buy a plan. Does Premium unlock straight away? What about after Restart app?
  • Switch to the Tablet (same account, new install) and use Restore purchases.
  • Use the Google Play › Subscriptions panel to Cancel or Refund, then move the test clock. Let a trial run out first if you need a paid charge.
  • After a subscription ends, open the paywall again and compare it with the Google Play sheet.
  • Change the Play account country to Serbia and compare prices.
  • With Monthly active, try Switch to Yearly and read the order history.

🐞 Bug hunt: Stillwater Premium paywall

This demo has 6 hidden bugs. Explore it like a tester, then check your findings.

Found0/6
9:41

Stillwater

Free

Sleep better with Premium

120+ sessions, sleep stories and focus sounds.

Today

Device

Same Google account, signed in on

Play account country (license tester)

Test clock

Thu, Oct 15, 2026

In real license testing, renewals are sped up (a month takes minutes). Here you jump instantly.

Google Play › Subscriptions

No subscriptions yet.

Order history

No charges yet.

From the spec

Premium unlocks right after purchase and works on every device with the same Google account. New subscribers get one 7-day free trial. Prices come from Google Play in the user's currency. Switching plans replaces the old plan. Refunds and expiry remove Premium.

  1. 1

    Hint: Subscribe on the Phone, then switch to the Tablet and tap Restore purchases.

  2. 2

    Hint: Let the trial turn into a paid subscription (+1 week), then press Refund in the Subscriptions panel.

  3. 3

    Hint: Cancel during the trial, move the clock until it expires, then open See plans again.

  4. 4

    Hint: Buy any plan and look at the lock icons and the Free badge right after the toast.

  5. 5

    Hint: Set the Play account country to Serbia, open the paywall, then tap the button.

  6. 6

    Hint: With Monthly active (restart after buying), use Manage plan › Switch to Yearly. Move the clock a week, then read Google Play › Subscriptions and the order history.

Always include the store account type (sandbox or license tester), the order or transaction ID and the time of the purchase in IAP bug reports. Developers will look them up in App Store Connect, the Play Console or the server logs.

Test scenarios

Functional

  • Load the paywall: every product appears with the right name, price, period and trial from the store.
  • Buy each product type: consumable (credits appear once), non-consumable (unlocked forever), subscription (Premium active).
  • Premium unlocks immediately, and stays after an app restart, a phone restart and an app update.
  • Restore on a reinstall and on a second device; Restore with nothing to restore shows a clear message.
  • Upgrade, downgrade and crossgrade between plans: correct timing, one subscription, correct charges.
  • Cancel in the store: Premium stays until the end date, then ends. Resubscribe before and after expiry.
  • Refund: Premium (or the consumable) is removed. Check what happens to content already used.
  • Family Sharing (if enabled): a family member gets access, and loses it when they leave.
  • Cross-platform: a web subscription unlocks the app after login; an app subscription shows correctly on the web.

Subscription lifecycle with the test clock

  • Trial converts to a paid subscription on the right day, with the right price.
  • Renewals happen on time and the end date moves forward in the app.
  • Failed renewal (Google test card “always declines”; on iOS, a failed renewal in StoreKit Testing, or turning off Allow Purchases & Renewals for the sandbox account): grace period keeps access if enabled, billing retry or account hold removes it, fixing the payment brings it back.
  • Intro offer or promo price applies only to the first periods, then the normal price.
  • Price increase: the user is informed, and what happens if they don’t accept matches the spec.

Negative

  • Cancel the store sheet: the app returns quietly to the paywall, no error message.
  • Declined payment: a clear message, no Premium, no half-unlocked state.
  • No network while loading the paywall: a helpful message instead of empty prices or $0.00.
  • Buying a plan you already own: the store refuses, and the app explains it.
  • Signed out of the store, or store account restricted (parental controls): sensible messages.

Edge cases

  • Kill the app right after tapping Subscribe, or during “Processing”. On the next launch the purchase is delivered once.
  • Airplane mode right after paying: the purchase is delivered when the network is back (see Network conditions).
  • Double tap on Subscribe or Buy coins: one purchase, one charge (double submitDouble submitA bug where an action runs twice because the user tapped or clicked twice quickly.Open in glossary →).
  • Pending purchases: Ask to Buy or a slow test card. The app shows “Waiting for approval” and unlocks when it completes, even days later.
  • Different store account on the device than the one that bought, or the app account belongs to someone else.
  • Changing the store country with an active subscription.
  • Time and date: a manually changed device clock must not extend or end Premium.

UI and usability

  • The paywall clearly shows price, period, trial length and what happens after the trial.
  • Auto-renew terms, how to cancel, and links to Terms of Use and Privacy Policy are visible before purchase.
  • A Restore purchases button is easy to find.
  • Long localized prices and plan names fit (see Localization and RTL).
  • The paywall can always be closed. Premium users see their plan and a way to manage it, not a sales screen.

Security

Accessibility

  • With a screen reader, each plan announces name, price, period and selected state (“Yearly, 39.99 dollars per year, selected”).
  • Trial terms and the price after the trial are readable by screen readers, not just shown in a tiny grey label.
  • Close and Restore buttons have names and are at least 44×44 pt (iOS) / 48×48 dp (Android).
  • Large text doesn’t hide the price or push the Subscribe button off screen.

Example test cases

TC-IAP-001Subscribe with a free trial unlocks Premium immediately

Preconditions

  • Fresh sandbox / license tester account that never subscribed
  • App installed from the testing track

Steps

  1. Open the paywall
  2. Select Yearly and tap "Start 7-day free trial"
  3. Confirm in the store sheet

Expected result

The store sheet shows the 7-day trial and the price after it. Right after confirming, Premium content is unlocked without a restart. The subscription appears in the store account.

TC-IAP-002Restore on a second device

Preconditions

  • Active subscription bought on device A
  • Device B signed in to the same store account, app freshly installed

Steps

  1. Open the app on device B
  2. Tap "Restore purchases"

Expected result

Premium is active on device B and a confirmation message is shown.

TC-IAP-003Restore with no purchases shows a clear message

Preconditions

  • Store account that never bought anything

Steps

  1. Open the paywall
  2. Tap "Restore purchases"

Expected result

A message like "No purchases found for this account" appears. Premium stays locked.

TC-IAP-004Canceled subscription keeps access until the end date

Preconditions

  • Active paid monthly subscription in sandbox

Steps

  1. Cancel the subscription in the store settings
  2. Check Premium in the app
  3. Wait for the accelerated period to end (about 5 minutes) and reopen the app

Expected result

Premium stays active after canceling, shows "Ends on <date>", and is removed after the period ends.

TC-IAP-005Refund removes Premium

Preconditions

  • Active paid subscription
  • Refund possible (StoreKit Testing in Xcode, or a Play Console refund for a license tester)

Steps

  1. Refund the latest transaction
  2. Bring the app to the foreground
  3. Restart the app

Expected result

Premium is removed (at the latest after the server receives the refund notification). Content is locked again on all devices.

TC-IAP-006No second free trial

Preconditions

  • Account that used the free trial and whose subscription expired

Steps

  1. Open the paywall
  2. Read the button and the terms
  3. Tap the button and read the store sheet

Expected result

The paywall shows the normal price (no trial wording), matching the store sheet.

TC-IAP-007Upgrade from Monthly to Yearly replaces the plan

Preconditions

  • Active paid monthly subscription

Steps

  1. Open Manage plan and choose Yearly
  2. Confirm in the store sheet
  3. Open the store's subscription list and the order history

Expected result

Only the Yearly subscription is active. Monthly is replaced as specified (prorated credit or switch at renewal). No double charge for the same period.

TC-IAP-008Prices follow the store account country

Preconditions

  • Test account in Serbia (RSD), device language English

Steps

  1. Open the paywall
  2. Compare the prices with the store sheet

Expected result

The paywall shows the same price and currency as the store sheet, e.g. "RSD 599.00 / month". No hard-coded dollars.

TC-IAP-009Purchase completed while the app was killed

Preconditions

  • Slow test card (approves after a few minutes) or Ask to Buy

Steps

  1. Start a purchase
  2. Force-quit the app while it is pending
  3. Wait for approval
  4. Open the app again

Expected result

Premium is unlocked exactly once. The purchase is acknowledged/finished, so it is not refunded and not delivered again on the next launch.

Tip: click a test case to expand it.

iOS vs Android

  • StoreKit Testing in Xcode is the fastest start: create a .storekit file with your products, run from Xcode, and use the transaction manager to refund, expire, fail renewals, approve Ask to Buy or trigger interrupted purchases. It runs entirely on your Mac, so it doesn’t prove that App Store Connect is set up correctly.
  • Sandbox Apple Accounts test against the real (sandbox) App Store. Create them in App Store Connect, then sign in on the device in the sandbox account section of the settings, not as the device’s main Apple ID. Each new account is a “new customer” again, perfect for trial eligibility.
  • TestFlight purchases are free, so beta testers never pay (see Beta distribution). Subscriptions there renew once a day (up to 6 times), so use a sandbox account or StoreKit Testing for fast lifecycle tests.
  • Restore purchases must exist in the UI. App Review checks it.
  • Ask to Buy creates a deferred transaction: test the “Waiting for approval” state and the later approval or decline.
  • Manage subscriptions: the system subscription screen (in Settings or the App Store) is where users cancel and change plans. Test that links from the app open it.
  • Refunds: users request them from Apple; the app can also offer an in-app refund request sheet. Your server learns about refunds through App Store Server Notifications.
  • License testers (Play Console › Settings › License testing) buy with test cards and are never charged. The app must be installed from Google Play (an internal or closed testing track), with the tester’s Google account.
  • Test cards include “always approves”, “always declines” and slow cards that approve or decline after a few minutes, which is perfect for pending states.
  • Acknowledge within three days: if the app never acknowledges a purchase, Google refunds it automatically. Test by checking the purchase status in the Play Console a few days after a test purchase, or ask developers how it’s handled.
  • Plan changes use a replacement mode chosen by the app (immediate with proration, deferred, and so on). Check that the right one is used and that only one subscription remains.
  • Account hold and pause: Google can put a subscription on hold after failed payments, and users may be able to pause it. The app must lock Premium during hold/pause and unlock it when it resumes.
  • Pending purchases: cash or other slow payment methods. Purchases arrive later, often when the app is closed.
  • Multiple Google accounts on one device are common. Check which account the purchase sheet uses and which account the app thinks is subscribed.

Checklist

In-app purchases checklist

0 of 15 checked

Check your understanding

Quick quiz

0/6 answered · 0 correct

  1. 1.A user removed ads for $2.99 and installs the app on a new phone. What type of product is this, and what should happen?

  2. 2.A user cancels their monthly subscription on day 10 of 30. What should the app do?

  3. 3.How do testers buy subscriptions without paying real money?

  4. 4.The paywall shows $4.99, but the Google Play sheet for a Serbian test account shows RSD 599.00. Which one is wrong?

  5. 5.After a refund, the user still has Premium. What is the most likely cause?

  6. 6.Why should the server, not the phone, decide whether a user is Premium?

Key takeaways

  • Know the product types: consumables are used up, non-consumables and subscriptions must be restorable on every device.
  • The store handles payment; the app gets a transaction, the server verifies it and keeps the entitlement. The store’s own sheet is your test oracle.
  • A subscription is a state machine: trial, active, canceled, grace period, billing retry, expired, refunded. Test what happens after the purchase, not just the purchase.
  • Use sandbox accounts, StoreKit Testing and license testers with accelerated renewals. Never your own card.
  • Classic bugs: Restore does nothing, paid but locked, Premium after refund, trial offered twice, hard-coded prices, double charge on plan change.
  • Test interruptions: pending purchases, killed app, lost network, double taps, and purchases made on another device or the web.

Finished this page?Nice work, this page is complete!

Mark it as done to track your progress. It's saved in this browser, no account needed.

Its review cards are now in Bites ⚡
Esc

Type to search the whole site.