In-App Purchase Failures: Diagnosis Guide
In-app purchase failures are revenue loss in real time. A broken IAP flow means every click "Buy" fails, users give up, money evaporates. The causes span client code, store sandbox vs production diffe
In-app purchase failures are revenue loss in real time. A broken IAP flow means every click "Buy" fails, users give up, money evaporates. The causes span client code, store sandbox vs production differences, receipt validation, and the many layers of IAP-related bugs. This guide covers root-causing and fixing.
Symptoms
- Purchase button tap does nothing
- Purchase sheet never appears
- Sheet appears, purchase succeeds, but entitlement not granted
- Receipt validation returns error
- Restore purchases shows no history
- Subscription renewal silently fails
- Duplicate charges on retry
Root causes
1. Products not loaded from store
App tries to show purchase sheet before products loaded from Play / App Store. Sheet shows but with no products.
Fix: Load products during splash; verify before enabling purchase buttons.
2. Sandbox / production mismatch
Developer tests in sandbox. Users in production. Different product IDs, different receipts, different validation endpoint.
Fix: Explicit sandbox / production flag. Backend validates against correct endpoint.
3. Account not signed into store
User signed out of Play Store / App Store. Purchase sheet displays auth prompt; user confused.
Fix: Detect account state; prompt "Sign into App Store first."
4. Pending purchase (Play Store)
User purchased via alternate method (cash, gift card). Purchase pending server-side confirmation. Client may show "purchase failed" incorrectly.
Fix: Handle pending state. Show "Awaiting payment confirmation." Grant access after confirmation webhook.
5. Receipt validation fails
Client sends receipt to own server; server validates with Apple / Google. Bug in validation logic or network call; returns error.
Fix: Exhaustive logging. Retry on network. Handle receipt reuse (idempotent).
6. Entitlement granted client-side only
Purchase succeeds, client sets "premium = true" locally. User reinstalls; entitlement lost.
Fix: Server is source of truth. Every launch verifies with server.
7. Restore purchases uses wrong identifier
Restore flow looks up transactions by anonymous device ID; user has signed in with new account. Restore shows empty.
Fix: Account-based subscription management. User ID, not device.
8. Family sharing edge case
Purchaser shares with family; family member launches app; family entitlement not recognized.
Fix: Check family sharing receipts. Respect is_family_shared flag.
9. Subscription downgraded, upgrade not handled
User downgrades from Premium to Basic. App still thinks Premium. Features reserved for Premium no longer available but UI claims they are.
Fix: On launch, always re-verify subscription tier. Respect downgrade immediately.
10. Grace period after failed payment
Subscription payment failed. Store gives 3-day grace. App locks user out on day 1.
Fix: Grace state respected. Show "Payment issue; access continues for X days. Update payment."
11. Receipt forge (jailbreak / root)
Attacker replays a known good receipt. Client accepts without server verification.
Fix: Always verify server-side. Don't trust client.
12. Multiple active subscriptions
User purchased monthly, then yearly. Two active subscriptions. App confused about which to honor.
Fix: Server deduplicates, grants entitlement from highest tier.
Diagnosis
Client-side
Logs at each step: product load, purchase attempt, response received, receipt sent to server, server response.
Server-side
Receipt validation request/response logs. Entitlement state changes logged with timestamp and reason.
Store-side
App Store Connect → Subscriber information. Play Console → Orders. Can see user's transaction history.
Reproduce
- Sandbox tester with known product
- Fresh install
- Specific OS / device
- Specific product (subscription vs one-time)
Fix patterns
- Server source of truth. Never grant entitlement from client alone.
- Receipt verification with retry. Network failures are transient.
- Idempotent transaction handling. Same receipt twice = same outcome, no double-charge.
- Observable state.
isPurchased,subscriptionTier,expiryDate— all from server, all on every launch. - Clear error UX. "Payment declined by bank" vs "Server temporarily unavailable, retry" — user needs signal.
How SUSA tests IAP
SUSA drives purchase flows in sandbox mode (with configured test accounts). Detects sheet appearance, post-purchase entitlement state, restore flow. Reports:
- Purchase flow failures (crash, no sheet, wrong product)
- Entitlement-not-granted-post-purchase
- Restore flow returning empty when it should not
susatest-agent test myapp.apk --iap-test-mode sandbox --iap-account tester@example.com
Prevention
- Per-release manual test of every IAP flow (can't be fully automated)
- Real-money spot check in production monthly
- Analytics: funnel from button-tap to entitlement-granted, alerts on drops
- RevenueCat / equivalent IAP SDK for out-of-box receipt validation
IAP is the revenue surface. Every bug costs real money. Invest accordingly.
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