Skip to content
~/sph.sh

Mobile IAP & Paywall Strategies - App Store, Play Store, RevenueCat

A practical guide to mobile in-app purchase rules, paywall patterns, and RevenueCat integration with server-side receipt validation and event-driven architecture.

Mobile monetization looks straightforward until you hit the first App Store rejection or discover your receipt validation has a gap that lets jailbroken devices access premium content for free. The app store payment rules, commission structures, and technical requirements form a complex landscape that every mobile developer needs to navigate carefully.

This post covers the practical side of mobile IAP: what both stores require, which paywall patterns work for different business models, why RevenueCat simplifies cross-platform subscription management, and how to build a reliable receipt validation pipeline with event-driven architecture.

Note: For choosing the right web payment provider when IAP is not required, see the companion post on payment providers and compliance. For how validated purchases propagate across platforms, see the post on omnichannel entitlement sync.

App Store and Play Store Payment Rules

Both Apple and Google mandate their own billing systems for digital goods consumed within apps. The rules differ in nuance, and regulatory pressure is reshaping them.

Commission Rates at a Glance

AspectApple App StoreGoogle Play Store
Standard commission30%30%
Small developer rate15% (under $1M/year)15% (first $1M/year)
Subscriptions (year 1)30%15%
Subscriptions (year 2+)15% (after 12 months retention)15%
Media (ebooks, streaming)Standard rates10% (if qualifying)
EU alternative billing17% or 10% (reduced)27% (3% reduction)

What Requires IAP

Digital goods consumed within the app must use the store's billing system. This includes:

  • Subscriptions unlocking premium features
  • Virtual currencies, coins, gems
  • Additional content (levels, filters, stickers)
  • Premium app functionality

What does not require IAP:

  • Physical goods and real-world services (Uber, Amazon shopping)
  • Person-to-person services (tutoring, consulting)
  • Content consumed outside the app (cloud storage, email services)

Reader App Rule (Apple)

Apple classifies certain apps as "reader" apps -- those providing previously purchased content like music, video, news, books, and magazines. These apps can:

  • Include a single external link to their website for account creation or management
  • Allow users to access content purchased elsewhere
  • Skip IAP for subscriptions managed on the web

Netflix, Spotify, and Kindle use this classification. If your app fits this pattern, you can direct users to web-based sign-up and avoid the 30% commission entirely.

EU Digital Markets Act (DMA)

Since March 2024, the EU designated Apple as a "gatekeeper" under the DMA. This means:

  • Alternative payment providers: Apps distributed in the EU/EEA can use third-party payment processors on iOS 17.4+
  • Reduced commission: Apple charges 17% (standard) or 10% (small business) when using alternative payment -- down from 30%/15%
  • Core Technology Fee: Apple charges EUR 0.50 per first annual install above 1 million installs (controversial and subject to ongoing legal challenges)
  • Google's approach: Offers a 3% service fee reduction when developers use alternative billing in the EEA

For most indie developers and startups, the standard IAP path remains simpler. The alternative billing route primarily benefits apps with significant EU user bases and high revenue where the commission savings outweigh the implementation complexity.

Paywall Patterns That Work

Choosing the right paywall pattern depends on your content type, user base, and business model. Here are the main approaches:

Hard Paywall

Everything behind the purchase wall. Works best for established brands where users already understand the value -- think professional tools or specialized databases. The risk: high friction drives away users who have not experienced the product yet.

Freemium

The most common mobile pattern. Offer a genuinely useful free tier, then gate advanced features behind a subscription. The key tension: the free tier must be good enough to retain users but limited enough to motivate upgrades.

Metered Paywall

Users get a fixed number of free uses per period, then must subscribe. The New York Times popularized this for news apps. It works well for content-heavy apps where users need to sample before committing.

Hybrid

Combines elements from multiple approaches. A typical hybrid might offer a free trial period, transition to freemium, and apply metered limits on specific premium content. Increasingly common for SaaS-style mobile apps that need flexible monetization.

Trial Periods

Both Apple and Google support free trial periods natively. Trials convert to paid subscriptions automatically unless cancelled. A practical note: clearly communicate trial terms in the paywall UI. Unclear trial communication leads to chargebacks, bad reviews, and potential store policy violations.

Why RevenueCat Over Direct Implementation

Implementing IAP directly with StoreKit 2 (iOS) and Google Play Billing Library v7 (Android) is doable but painful at scale. Each platform has its own API, receipt format, and subscription lifecycle semantics.

The Cross-Platform Challenge

ConcernDirect ImplementationRevenueCat
Receipt validationBuild for each store separatelyHandled server-side
Subscription stateTrack across platforms yourselfUnified customer view
Grace periods & retriesCustom logic per platformAutomatic
Paywall configurationRequires app updateRemote config
Analytics (MRR, churn)Build or integrate third-partyBuilt-in dashboard
A/B testing paywallsCustom infrastructureNative support

What RevenueCat Provides

RevenueCat acts as an abstraction layer over both stores. The SDK handles:

  • Unified purchases: One API for iOS, Android, Amazon, and Stripe web billing
  • Server-side receipt validation: Verifies purchases with Apple and Google servers automatically
  • Subscription lifecycle: Manages trials, grace periods, billing retries, and cancellations
  • Offerings system: Configure products and paywalls remotely without app updates
  • RevenueCat Paywalls: Pre-built, customizable paywall UI templates
  • Webhooks: Real-time subscription events pushed to your backend
  • Analytics: MRR, churn rate, LTV, trial conversion rates out of the box

Pricing Consideration

RevenueCat pricing scales with revenue:

  • Free: Up to $2,500 monthly tracked revenue (MTR)
  • Starter: 1% of MTR above $2,500
  • Pro: $119/month + 0.9% of MTR
  • Enterprise: Custom pricing

At high scale, the percentage fee adds up. For an app generating 100K/month,RevenueCatcostsroughly100K/month, RevenueCat costs roughly 900-$1,000/month on the Pro plan. Whether that is worth it depends on the engineering time saved versus building and maintaining your own subscription infrastructure.

When Direct Implementation Makes Sense

  • Simple one-time purchase apps with no subscription logic
  • Apps where you need full control over receipt validation behavior
  • Very high revenue where RevenueCat fees exceed the cost of a dedicated team

Receipt Validation: Server-Side Is Non-Negotiable

Client-side receipt validation can be bypassed. Modified app binaries, jailbroken devices, and proxy tools can all forge or replay receipts. Server-side validation is the baseline for any serious monetization.

The Validation Flow

Apple App Store Server API

Apple's server-side validation uses the App Store Server API (replacing the deprecated verifyReceipt endpoint). Key points:

  • JWS-signed transactions for cryptographic verification
  • Transaction history endpoint for full purchase history
  • App Store Server Notifications V2 for real-time subscription events (SUBSCRIBED, DID_RENEW, EXPIRED, REFUND)

Google Play Developer API

Google's validation uses the Android Publisher API:

  • purchases.subscriptions.get for subscription status
  • purchases.products.get for one-time purchases
  • Real-time Developer Notifications (RTDN) via Google Cloud Pub/Sub

Server Notifications Are Essential

Both stores push real-time events when subscription states change. Without handling these, your backend will have stale entitlement data:

  • A user cancels but still has access until period end
  • A billing retry succeeds after initial failure
  • A refund is processed and you need to revoke access

Restore Purchases: A Required Feature

Both Apple and Google require apps to provide a mechanism for users to restore previous purchases. Missing this feature risks app rejection and generates support tickets.

Why Users Need It

  • Device upgrades or replacements
  • App reinstallation
  • Factory resets
  • Family Sharing (Apple) -- shared subscriptions across family members

Implementation Approach

With RevenueCat, restore is handled through customer ID synchronization. When a user logs in on a new device, RevenueCat matches the customer and restores entitlements automatically.

For direct implementation:

typescript
// iOS - StoreKit 2// Check for existing entitlements on app launchconst checkEntitlements = async () => {  // StoreKit 2 automatically syncs transactions  // Transaction.currentEntitlements returns all active entitlements  for await (const result of Transaction.currentEntitlements) {    // Process each active entitlement    await grantAccess(result);  }};
// Android - Google Play Billing Library v7// Query existing purchasesconst restorePurchases = async (billingClient: BillingClient) => {  const params = QueryPurchasesParams.newBuilder()    .setProductType(ProductType.SUBS)    .build();
  const purchasesResult = await billingClient.queryPurchasesAsync(params);  // Process each purchase and restore access  for (const purchase of purchasesResult.purchasesList) {    await acknowledgePurchaseIfNeeded(purchase);    await grantAccess(purchase);  }};

Edge Cases to Handle

  • Account holds (Google): A grace period before cancellation where the user temporarily loses access
  • Billing retry: Both stores retry failed payments automatically -- your backend needs to handle state transitions
  • Refunds: When a refund is processed, revoke the entitlement. Apple sends a REFUND notification; Google sends a REVOKED notification
  • Cross-platform restore: A user who purchased on iOS expects access on Android (and vice versa). This requires a backend entitlement system -- see the post on omnichannel entitlement sync

Code Example: RevenueCat Webhook to EventBridge

Here is a practical backend handler that receives RevenueCat webhook events, validates them, and publishes purchase events to AWS EventBridge for downstream processing.

typescript
import {  EventBridgeClient,  PutEventsCommand,} from "@aws-sdk/client-eventbridge";
const eventBridge = new EventBridgeClient({});
interface RevenueCatWebhookEvent {  api_version: string;  event: {    type: string;    app_user_id: string;    product_id: string;    period_type: string;    purchased_at_ms: number;    expiration_at_ms: number | null;    store: "APP_STORE" | "PLAY_STORE" | "STRIPE" | "AMAZON";    environment: "PRODUCTION" | "SANDBOX";    price_in_purchased_currency: number;    currency: string;    transaction_id: string;  };}
export const handler = async (event: {  headers: Record<string, string>;  body: string;}) => {  // Validate webhook authenticity  const authHeader = event.headers["authorization"];  if (authHeader !== `Bearer ${process.env.REVENUECAT_WEBHOOK_SECRET}`) {    return { statusCode: 401, body: "Unauthorized" };  }
  const webhook: RevenueCatWebhookEvent = JSON.parse(event.body);  const rcEvent = webhook.event;
  // Skip sandbox events in production  if (    rcEvent.environment === "SANDBOX" &&    process.env.NODE_ENV === "production"  ) {    return { statusCode: 200, body: "Sandbox event ignored" };  }
  // Map RevenueCat event types to domain events  const eventTypeMap: Record<string, string> = {    INITIAL_PURCHASE: "purchase.completed",    RENEWAL: "subscription.renewed",    CANCELLATION: "subscription.cancelled",    EXPIRATION: "subscription.expired",    BILLING_ISSUE: "subscription.billing_failed",    PRODUCT_CHANGE: "subscription.plan_changed",  };
  const domainEventType = eventTypeMap[rcEvent.type];  if (!domainEventType) {    console.log(`Unhandled event type: ${rcEvent.type}`);    return { statusCode: 200, body: "Event type not handled" };  }
  // Publish to EventBridge  await eventBridge.send(    new PutEventsCommand({      Entries: [        {          Source: "payments.mobile-iap",          DetailType: domainEventType,          Detail: JSON.stringify({            customerId: rcEvent.app_user_id,            productId: rcEvent.product_id,            store: rcEvent.store,            transactionId: rcEvent.transaction_id,            periodType: rcEvent.period_type,            price: {              amount: rcEvent.price_in_purchased_currency,              currency: rcEvent.currency,            },            purchasedAt: new Date(rcEvent.purchased_at_ms).toISOString(),            expiresAt: rcEvent.expiration_at_ms              ? new Date(rcEvent.expiration_at_ms).toISOString()              : null,          }),          EventBusName: process.env.EVENT_BUS_NAME || "default",        },      ],    })  );
  return { statusCode: 200, body: "Event processed" };};

This handler provides a clean boundary between RevenueCat's webhook format and your internal event schema. Downstream consumers (entitlement service, analytics, notifications) subscribe to EventBridge rules without knowing about RevenueCat's specifics.

Key Takeaways

  • Start with standard IAP unless you have a specific reason to use alternative billing. The commission rates are converging, and direct IAP is simpler to implement and maintain.
  • Choose your paywall pattern based on your content type, not what competitors do. Freemium works for most apps, but metered works better for content-heavy products.
  • Use RevenueCat for cross-platform apps unless you have very simple purchase flows or revenue high enough to justify a dedicated subscription infrastructure team.
  • Server-side receipt validation is mandatory for any app handling real revenue. Client-side validation alone is a security vulnerability.
  • Handle store notifications (Apple Server Notifications V2, Google RTDN). Without them, your entitlement data will drift from reality.
  • Restore purchases is not optional -- both stores require it, and users expect it. Test it across device transfers, reinstalls, and family sharing scenarios.

References

  1. Apple App Store Review Guidelines - Official guidelines covering IAP requirements, reader app rules, and payment policies
  2. Apple App Store Small Business Program - Details on the 15% reduced commission for developers earning under $1M annually
  3. Google Play Billing Documentation - Official Android billing library documentation with integration guides
  4. RevenueCat Documentation - Comprehensive SDK documentation, webhook reference, and integration guides
  5. EU Digital Markets Act Overview - European Commission page on DMA regulations affecting app store payment rules
  6. Apple StoreKit 2 Documentation - Modern Apple IAP framework with async/await API and JWS-signed transactions
  7. App Store Server API - Server-side purchase verification and transaction history endpoints
  8. Google Play Developer API - Server-side purchase verification for Android subscriptions and products
  9. RevenueCat Webhook Events Reference - Complete list of webhook event types and payload formats
  10. Apple App Store Server Notifications V2 - Real-time subscription lifecycle notifications from Apple
  11. Google Real-time Developer Notifications - Pub/Sub based notification system for Google Play subscription events
  12. RevenueCat Paywalls Documentation - Pre-built paywall templates and remote configuration options

Related Posts