SyncTrix logoSyncTrix
All articles
Experience11 min read

In-app purchases and subscriptions: the parts that go wrong

Receipt validation, restore, and the gap between a platform charging a user and your backend knowing about it. Where revenue actually leaks.

By Marcus Hale
In-app purchases and subscriptions: the parts that go wrong

In-app purchase looks like a client feature and behaves like a distributed transaction between the platform, your backend and a device that may disconnect at any point. The failures cost real money in both directions: users charged without receiving access, and users with access nobody is billing.

01Never grant entitlement on the client

The client knows a purchase appeared to succeed. It does not know whether the payment settled, whether the receipt is genuine, or whether the same receipt was already used on another account.

Entitlement must be decided server-side after validating the receipt with the platform. A client that unlocks features because the purchase callback fired is trivially bypassed and will also grant access for purchases that later fail.

This is the single most common implementation error, and it is usually discovered through revenue that does not reconcile rather than through a bug report.

02The states you have to handle

Purchase is not a single event. It is a state machine with several paths that all occur in production.

StateWhat it meansIf unhandled
DeferredAwaiting approval, e.g. family consentUser sees a failure, may purchase twice
InterruptedApp closed mid-transactionCharge with no entitlement
Pending validationReceipt not yet verifiedAccess granted or denied incorrectly
RestoredReinstall or new deviceUser re-charged for owned content
RefundedPlatform issued a refundAccess continues indefinitely
Renewal failedBilling retry in progressAccess cut before grace period ends
Purchase states that need handling

03Restore is not optional

Users reinstall, change devices and use multiple devices on one account. Without a working restore path they are charged twice and open a support ticket, and both stores treat a missing restore as grounds for rejection.

Restore has to reconcile platform entitlements with your own account model, which is where it gets awkward - a platform purchase belongs to a store account, and your subscription belongs to your user. When those two do not correspond, you need a defined rule for what happens.

Decide that rule explicitly: whether one store account can fund multiple users, and what happens when someone signs into a different account on a device with an active subscription. Leaving it undefined produces sharing behaviour you did not intend.

04Server notifications are the source of truth

Both platforms send server-to-server notifications for renewals, cancellations, refunds and billing problems. These, not the client, are how your backend should learn about subscription state changes.

Treat them like any webhook: verify authenticity, make handling idempotent because they can arrive more than once, and reconcile periodically against the platform's API in case you missed some.

Without that reconciliation job, small delivery gaps accumulate into a subscriber list that quietly diverges from reality - and the divergence is invisible until someone compares the two.

Topics

in app purchase implementationmobile subscriptionsreceipt validationapp store billingsubscription management mobile

Marcus Hale

Lead Architect · SyncTrix

Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.

Building something like this?

SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.

Talk to an engineer