# Design Mobile In-App Purchase and Subscription Validation

Source: https://www.techinterview.org/post/3233475323/mobile-system-design-iap-subscription-validation/
Updated: 2026-06-30 · techinterview.org

"Design in-app purchases and subscription validation" is the unglamorous mobile-system-design question that pays for many apps. The interview tests whether you understand the full purchase loop: client APIs, receipt validation, server entitlements, restore flow, family sharing, and the failure modes that produce angry one-star reviews.

## Clarify scope

- One-time purchases, consumables, or subscriptions?

- iOS, Android, or both?

- Family sharing supported?

- Free trials and introductory offers?

- Server-side entitlements or client-only?

## The two stores have different APIs

- **iOS:** StoreKit 2 (modern Swift API), StoreKit 1 (legacy callback). Receipts as JWS-signed transactions.

- **Android:** Google Play Billing Library v6+. Purchase tokens validated server-side with the Play Developer API.

Universal pattern: client initiates purchase → store processes → client receives signed receipt → server validates with the store → server grants entitlement.

## Why server validation is non-negotiable

- Client-side validation is bypassable (jailbroken devices, modified APKs)

- Subscriptions need server-tracked state for renewal, cancel, refund

- Cross-device entitlement requires a server

- Refund handling requires the server-to-server notification channel

## The validation flow

- Client purchases via StoreKit/Billing API

- Client receives signed receipt or purchase token

- Client sends receipt to your server

- Server validates with Apple's verifyReceipt endpoint or Google's purchases.subscriptions.get

- Server stores entitlement in your DB tied to user account

- Server responds to client; client unlocks features

- Server subscribes to App Store Server Notifications / Google Real-Time Developer Notifications for renewal, cancellation, refund

## Entitlements model

Server is the source of truth. Schema:

- user_id

- entitlement (string identifier, e.g., "pro_monthly")

- expires_at

- auto_renew

- store_transaction_id (for dedup)

- last_validated_at

Client checks entitlements at app launch and periodically during use. Cache locally; treat as soft state.

## Restore purchases

The user reinstalls or signs into a new device. The "Restore Purchases" button:

- Calls StoreKit's currentEntitlements / Billing's queryPurchasesAsync

- For each restored receipt, sends to your server

- Server attaches to the current user account if not already

- Client receives updated entitlements

Edge case: receipts may be tied to an Apple ID different from your app account. Decide policy (link by Apple ID? require explicit account login?).

## Free trials and intro offers

- Server-side eligibility check before showing trial — Apple/Google enforce one trial per Apple ID/Play account, so don't mislead

- Trial state in your DB: trial_started_at, trial_expires_at

- Renewal at trial end fires through the same notification pipeline

## Subscription state machine

- Pending → Active (initial purchase confirmed)

- Active → Renewing (auto-renew true)

- Active → Expiring (user cancelled, still in paid period)

- Expiring → Expired (period ended)

- Active → InGracePeriod (billing failed; Apple/Google retry)

- Active → Refunded (rare; revoke entitlement)

Server tracks state from notifications. Client trusts the server.

## Family sharing

- Apple: enable "Family Sharable" on the IAP product. Apple distributes the entitlement to family members; your validation receives "isFamilyShared" flag

- Google: similar via Family Library; the purchase token is shared

- Your server creates entitlements for each family member who installs and validates

## Refund handling

- Apple sends REFUND notification

- Server revokes entitlement immediately

- Client checks entitlement on next foreground; loses access without crash

- Track refund history to detect abuse patterns (some apps deny re-trials to repeat refunders)

## Common failure modes

- Network drops between purchase and server-validate — store the unvalidated receipt locally and retry

- User logs out before validation completes — receipt orphaned; surface "Restore Purchases" prompt on next login

- App killed mid-purchase — StoreKit/Billing both replay pending purchases on next launch

- Server-down during validation — degrade gracefully; trust client receipt for a brief grace window

## Compliance considerations

- Per Apple guidelines, digital goods MUST go through StoreKit; physical goods via your own payment

- Per the EU DMA, alternative payment methods are allowed in iOS apps in the EU; design for both flows

- Tax handling — both stores collect VAT/sales tax; your prices include it

- Subscription terms must be disclosed before purchase; both stores enforce this

## What separates senior from staff

Senior candidates discuss server validation and entitlements. Staff candidates discuss the notification-driven state machine, refund handling, family sharing, and the EU DMA alternative-payment flow.

## Frequently Asked Questions

### Can I store entitlements only on the client?

Possible for one-time purchases on a single device, but you lose cross-device, refund handling, and audit trail. For subscriptions, server-side is required.

### What about web-purchased subscriptions?

If you sell subscriptions via your website (Stripe, etc.), grant entitlements server-side and surface them in the app. Apple now allows linking out to the web for purchase in some apps; Google has long allowed it.

### Should I use RevenueCat or Adapty?

Many teams do — they handle server validation, notifications, and entitlements. Trade off: less control, monthly fees. For a small team without IAP infrastructure, the time savings are real.
