Common In-App Purchases Bugs and How to Catch Them

Common In-App Purchases Bugs and How to Catch Them

January 21, 2026 · 19 min read · Common Issues

Common In-App Purchases Bugs and How to Catch Them

In‑app purchases (IAP) are a critical revenue path for mobile apps, yet they are also one of the most error‑prone areas. A single missed validation step or a mis‑handled edge case can turn a paying user into a frustrated one, trigger chargebacks, or even get the app removed from stores. This guide walks through the most common IAP bug patterns, explains why they appear, shows how they manifest to users, and gives concrete steps to reproduce, detect, fix, and prevent each issue. You’ll also find a test matrix that compares manual and automated techniques, a bug/symptom/fix table for quick reference, and a short checklist you can bookmark before every release.

---

Common In-App Purchases Bugs and How to Catch Them: Overview of the IAP Ecosystem

Why IAP Is Fragile

IAP sits at the intersection of client‑side code, platform‑specific billing libraries (StoreKit on iOS, Google Play Billing on Android), and a backend server that validates receipts. Each layer introduces its own failure modes:

Because the flow spans multiple processes and often involves real money, bugs that only surface under specific conditions (e.g., poor network, a particular locale, or a subscription renewal) are easy to miss with scripted tests that follow a single happy‑path.

What This Guide Covers

We will examine eight recurring bug categories, each broken down into:

  1. Root cause – why the defect originates.
  2. User symptom – what the user sees or experiences.
  3. Reproduction steps – how to trigger the bug reliably in a test environment.
  4. Detection techniques – manual checks, automated tests, and tooling hints.
  5. Fix & prevention – code changes, architectural safeguards, and test additions.

At the end of each major section you’ll find a short “Key Takeaways” bullet list.

---

Common In-App Purchases Bugs and How to Catch Them: Receipt Validation and Server Trust Issues

1.1 Missing or Incorrect Signature Verification

Root cause – The app accepts a receipt from StoreKit or Play Billing and forwards it to the backend, but the backend either skips signature verification or uses an outdated public key.

User symptom – A user completes a purchase, sees the confirmation dialog, but the app does not unlock the purchased content. In some cases the user is charged but receives nothing, leading to support tickets and possible refunds.

Reproduction

  1. Use a sandbox/test account to make a purchase.
  2. Capture the raw receipt (via SKPaymentTransaction.transactionReceipt on iOS or Purchase.getOriginalJson() on Android).
  3. Send a tampered receipt (e.g., flip a byte in the signature) to your backend.
  4. Observe whether the backend accepts it.

Detection

Fix & prevention

Key Takeaways

1.2 Replay Attacks Using Old Receipts

Root cause – The backend validates a receipt but does not check whether it has already been consumed, allowing a user to reuse an old receipt to unlock content repeatedly.

User symptom – A power user discovers they can re‑purchase a consumable item without being charged again, effectively getting free currency or items.

Reproduction

  1. Make a purchase and store the receipt.
  2. Restart the app, clear any local purchase state, and resend the stored receipt to the backend.
  3. Verify whether the backend grants the item a second time.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Product Identification and Localization Errors

2.1 Missing Product Identifiers in Store Response

Root cause – The app requests product details from StoreKit/Play Billing using an incorrect or outdated product ID list, causing the platform to return an empty product array.

User symptom – The store screen shows empty placeholders or “Item unavailable” messages, leading users to abandon the purchase flow.

Reproduction

  1. Change one product ID in your code to a non‑existent string.
  2. Launch the store UI and observe that the product does not appear.
  3. Check the logs for SKProductsRequest failure or BillingClient response with BILLING_UNAVAILABLE.

Detection

Fix & prevention

Key Takeaways

2.2 Localized Price Formatting Errors

Root cause – The app formats the price string manually (e.g., concatenating a currency symbol) instead of using the locale‑aware priceLocale provided by StoreKit or the priceAmountMicros/priceCurrencyCode from Play Billing.

User symptom – Users in certain regions see prices like “$9,99” (incorrect decimal separator) or the wrong currency symbol, causing confusion and abandoned checkouts.

Reproduction

  1. Set the device locale to a region that uses a different decimal separator (e.g., French France).
  2. Navigate to the IAP screen and observe the price display.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Purchase Flow Interruptions and Network Failures

3.1 User Cancels Mid‑Flow

Root cause – The app assumes that once the payment sheet is presented, the transaction will either succeed or fail with an error, ignoring the case where the user taps “Cancel” or closes the sheet.

User symptom – After canceling, the app still shows a loading spinner or incorrectly marks the purchase as pending, leaving the UI in an inconsistent state.

Reproduction

  1. Trigger the purchase UI.
  2. Tap the cancel button (iOS) or back button (Android) before confirming payment.
  3. Observe whether the app returns to the previous screen and clears any loading indicators.

Detection

Fix & prevention

Key Takeaways

3.2 Network Loss After Payment Authorization

Root cause – The app receives a successful payment acknowledgment from the platform but loses network connectivity before it can send the receipt to the backend, causing the app to think the purchase failed while the platform already charged the user.

User symptom – The user sees an error message, but later discovers they were charged and the item never unlocked, prompting a refund request.

Reproduction

  1. Enable airplane mode immediately after confirming the payment but before the app sends the receipt to your server.
  2. Observe the app’s behavior and check server logs for missing receipt.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Subscription Management and Renewal Bugs

4.1 Failure to Handle Subscription Renewal Notifications

Root cause – The app only checks subscription status at launch or after a manual restore, ignoring push notifications or server‑to‑server renewal alerts from Apple/Google.

User symptom – A user whose subscription renews successfully sees the app still showing an expired state until they manually restart the app or tap “Restore Purchases.”

Reproduction

  1. Set up a sandbox subscription with a short renewal period (e.g., 5 minutes).
  2. Let the subscription renew automatically.
  3. Without restarting the app, check whether the subscription status updates.

Detection

Fix & prevention

Key Takeaways

4.2 Incorrect Handling of Subscription Downgrades/Upgrades Mid‑Cycle

Root cause – When a user changes subscription tier in the middle of a billing period, the app incorrectly applies the new price immediately or fails to prorate, resulting in either over‑charging or under‑delivering entitlements.

User symptom – After upgrading, the user either gets charged the full new price right away (feeling overcharged) or continues to see the old tier’s benefits until the next billing date.

Reproduction

  1. Purchase a monthly subscription (Tier A).
  2. Midway through the period, initiate an upgrade to Tier B using the store’s upgrade flow.
  3. Verify whether the app reflects the new entitlements immediately and whether the backend receives a prorated transaction.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Restore Purchases and Promotional Offer Failures

5.1 Restore Purchases Returns Empty List

Root cause – The app calls the restore API but fails to map the returned transactions to local entitlements, often because it expects a specific product ID format that changed after a store update.

User symptom – A user who reinstalls the app or switches devices taps “Restore Purchases” and sees no previously bought items restored, forcing them to repurchase.

Reproduction

  1. Make a non‑consumable purchase on Device A.
  2. Install the app on Device B, sign in with the same store account, and invoke the restore flow.
  3. Check whether the entitlements appear.

Detection

Fix & prevention

Key Takeaways

5.2 Promo Code or Offer Not Applied

Root cause – The app presents a promotional offer (e.g., a discounted introductory price) but fails to send the correct offer identifier or token to the store, so the user pays the full price.

User symptom – The user sees a promo banner, taps “Buy,” but the checkout screen shows the regular price, leading to complaints and abandoned purchases.

Reproduction

  1. Configure an introductory offer in App Store Connect (e.g., free trial for 3 days).
  2. In the app, initiate purchase flow for the product with the offer.
  3. Verify whether the price displayed matches the offer price.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Deferred and Pending Purchase Edge Cases

6.1 Deferred Purchase (Ask to Buy) Not Handled

Root cause – On iOS, when a child initiates a purchase under “Ask to Buy,” StoreKit returns a transaction with state .deferred. Many apps treat this as an error or ignore it, leaving the user with no feedback.

User symptom – The child sees the purchase sheet disappear with no confirmation, and the parent never receives a notification, causing confusion.

Reproduction

  1. Set up a family sharing group with a child account.
  2. Have the child attempt to buy a consumable.
  3. Observe whether the app shows a “Purchase pending approval” message.

Detection

Fix & prevention

Key Takeaways

6.2 Pending Android Purchase (User Needs to Complete Payment)

Root cause – On Google Play, if a user selects a payment method that requires additional steps (e.g., carrier billing that needs SMS verification), the Billing Library returns a Purchase object with purchaseState == 0 (PENDING). Apps that only check for purchaseState == 1 (PURCHASED) treat this as a failure.

User symptom – The user sees an error message, but the purchase is actually awaiting completion; if they later finish the payment outside the app, they never receive the item.

Reproduction

  1. Choose a payment method known to trigger pending state (e.g., select “PayPal” in a sandbox environment that requires manual approval).
  2. Complete the flow and observe whether the app grants the item immediately.

Detection

Fix & prevention

Key Takeaways

---

Common In-App Purchases Bugs and How to Catch Them: Persona‑Driven Autonomous Exploration vs Scripted Tests

Why Scripted Tests Miss IAP Bugs

Traditional automated tests often follow a predefined sequence: launch app → navigate to store → tap “Buy” → assert success. This approach works for the happy path but overlooks variations that real users exhibit:

These behaviors can surface issues such as double‑tap race conditions, missing accessibility labels on purchase buttons, or incorrect handling of interrupted flows—scenarios that a single script never touches.

How Autonomous Exploration Helps

An autonomous QA agent (like the one offered by SUSA) explores the app without pre‑written scripts, guided by a set of persona profiles. Each profile defines a distinct interaction pattern:

PersonaInteraction TraitsTypical IAP‑Related Findings
CuriousTaps every visible element, explores hidden menusDiscovers unintended purchase triggers, exposed debug purchase buttons
ImpatientRapid repeated taps, quick navigationUncovers race conditions, double‑charge bugs, UI state leaks
NoviceReads prompts slowly, often taps cancel or backFinds unclear error messages, missing guidance during deferred/pending states
AdversarialModifies network traffic, attempts to resend old receiptsDetects insufficient receipt validation, replay‑attack vulnerabilities
ElderlyUses larger touch targets, voice‑over navigationReveals accessibility label missing on IAP buttons, touch‑target too small
AccessibilityRelies on screen readers, high‑contrast modeChecks that price strings and purchase states are announced correctly
Power userUses shortcuts, restores purchases frequentlyFinds restore‑purchase failures, missing entitlement sync after reinstall
Price‑sensitiveAborts when price display looks offCatches localization/formatting errors, misleading promo pricing

The agent automatically records each screen visited, each action taken, and any observable outcome (crash, ANR, toast, UI state change). When it encounters a purchase flow, it:

  1. Collects the raw receipt from the platform library.
  2. Attempts to send it to a mock backend (or the real backend in a staging environment).
  3. Variates the interaction: cancels mid‑flow, drops network, replays receipts, changes locale, triggers promotional offers, etc.

If the backend responds with an error or the UI shows an incorrect state, the agent logs a defect with steps to reproduce. Because the exploration is driven by personas, it produces a broader set of edge cases than a deterministic script.

Practical Example: Detecting a Race Condition with the Impatient Persona

*Scenario*: The app allows users to buy a consumable currency pack. The UI disables the buy button after the first tap, but a bug in the debounce logic enables a second tap before the disabling takes effect, resulting in two transactions being sent.

*Autonomous test*:

  1. The Impatient persona rapidly taps the buy button three times within 200 ms.
  2. The agent captures two successful transaction callbacks from StoreKit.
  3. It forwards both receipts to the backend, which accepts both and grants double the currency.
  4. The agent flags the defect: “Duplicate purchase granted due to insufficient debounce.”

*Manual reproduction*:

*Fix*:

Integrating Autonomous Findings into CI

You can export the agent’s discovery report as a JUnit‑style XML or JSON artifact and fail the build if any high‑severity IAP defect is detected. This gives you the confidence of scripted coverage plus the breadth of exploratory testing, without writing countless persona‑specific scripts.

Key Takeaways

---

Building a Reliable IAP Test Matrix (Manual + Automated)

The following matrix maps each bug category to the most effective detection techniques. Use it to decide where to invest manual effort, where to automate, and where to leverage autonomous exploration.

Bug CategoryManual Test TechniqueAutomated Unit / Integration TestUI‑Test (XCUITest/Espresso)Autonomous Exploration (Persona‑Driven)Recommended Frequency
Receipt signature missingCharles proxy tamperingVerify verification function throws on bad sigN/AAdversarial persona replays tampered receiptEvery commit (unit) + nightly (autonomous)
Replay attackManual resend of old receiptAssert second submission rejectedN/AAdversarial persona re‑uses receiptEvery commit (unit) + nightly
Missing product IDsSpot‑check store IDs vs codeAssert returned set matches source of truthN/ACurious persona browses store screenEach release (manual) + CI (automated)
Price localization errorsLocale switch + screenshotAssert formatted string matches localeAssert price label matches NumberFormatter outputCurious + Accessibility personasEach release (manual) + nightly (UI)
User cancels mid‑flowTap cancel buttonAssert state returns to idle, no entitlementAssert loading spinner disappearsImpatient personaEach release (manual) + nightly
Network loss after authEnable airplane mode post‑authAssert local receipt stored & retry queue processesAssert pending UI shown & eventual success/failureImpatient personaEach release (manual) + nightly
Subscription renewal not handledWait for sandbox renewalAssert subscription model updates on notificationN/APower‑user persona (frequent restores)Each release (manual) + weekly (autonomous)
Subscription upgrade/downgrade prorationMid‑cycle upgrade in sandboxAssert prorated charge & correct entitlement datesN/APower‑user personaEach release (manual) + nightly
Restore purchases emptyFresh install + restore flowAssert entitlements restored from mocked transactionsAssert UI shows restored itemsNovice + Power‑user personasEach release (manual) + nightly
Promo offer not appliedSandbox purchase with offerAssert receipt contains offer fieldsAssert UI price matches offer priceCurious + Price‑sensitive personasEach release (manual) + nightly
Deferred purchase (Ask to Buy)Child account sandboxAssert UI shows pending, no entitlementAssert pending label visibleNovice personaEach release (manual) + nightly
Pending Android paymentSelect pending‑payment methodAssert UI shows waiting state, no entitlementAssert pending indicatorImpatient personaEach release (manual) + nightly

How to Use the Matrix

---

Quick Checklist for Shipping Safe IAP Features

Before you tag a release, run through this list. Mark each item as ✅ or ❌ and address any gaps.

Test Your App Autonomously

Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.

Try SUSA Free