NewHuxly MCP — Connect Claude, Cursor & Codex.Learn more
How to Build a Subscription Mobile App with RevenueCat
Back to Blog
GuideSep 9, 202612 min read

How to Build a Subscription Mobile App with RevenueCat

Contents

A subscription app needs more than a paywall. It must show the right products, complete purchases through Apple or Google, unlock the correct features, restore access, and react when a subscription renews, expires, or is refunded.

RevenueCat sits between your app and the stores. It gives your mobile client one way to fetch products, make purchases, and check access across iOS and Android. You still create the subscription products in App Store Connect and Google Play Console, but you avoid maintaining two separate entitlement systems inside the app.

Key takeaways:

Create store products first, map them to one RevenueCat entitlement, check that entitlement instead of product IDs, identify signed-in users consistently, support restore purchases, and use webhooks when your backend also needs subscription state.

How the subscription system fits together

Four layers are involved. Keeping them separate makes the setup much easier to reason about.

LayerWhat it controlsExample
App Store and Google PlayPrice, billing period, regional availability, and purchase processingMonthly and annual products
RevenueCat products and packagesA normalized catalog your app can request$rc_monthly and $rc_annual packages
RevenueCat entitlementThe access a paid customer receivespro
Your appPaywall, purchase button, feature gates, and account UIUnlock exports and remove limits

An entitlement should describe access, not a billing plan. For example, both a monthly product and an annual product can unlock the same pro entitlement. Your app then asks one simple question: does this customer have active pro access?

RevenueCat explains this model in its entitlements documentation. It is safer than scattering monthly and annual product IDs throughout the code.

1. Define what the subscription unlocks

Before opening either store dashboard, write down the paid promise. Be specific enough that a customer can understand it and a developer can enforce it.

Weak definition: access to premium features.

Useful definition: unlimited projects, cloud sync, PDF export, and AI summaries up to the plan's monthly allowance.

Decide these details before implementation:

  • Which features remain free?
  • Is there one paid tier or several?
  • Will you offer monthly, annual, or both?
  • Is there a free trial or introductory price?
  • What happens to saved data after access expires?
  • Can one account use the subscription on multiple devices?

A simple first release usually benefits from one entitlement and two billing periods. Multiple tiers can work, but they add upgrade, downgrade, proration, and messaging cases that need testing.

2. Create products in both stores

Create auto-renewable subscriptions in App Store Connect and equivalent subscription products in Google Play Console. Use stable IDs such as:

text
pro_monthly
pro_annual

The IDs can differ between platforms, but matching names reduce confusion. Never encode a price in the ID because prices can change by market.

For Apple, place related subscription products in the same subscription group when customers should hold only one of them at a time. Apple's subscription setup guide covers product creation and review information. Google documents plans, offers, and prepaid options in its Play Billing subscription guide.

Complete the metadata while you are there:

Store fieldWhat to check
Display nameClear plan name that fits small screens
DescriptionConcrete benefits without vague promises
Billing periodMonthly or annual as intended
PriceCorrect base price and regional availability
Trial or offerEligibility and duration are clearly explained
Review detailsScreenshots and instructions help reviewers find the paywall

Products may not appear immediately in development. Store agreements, tax details, product status, and build configuration can all affect availability.

3. Configure RevenueCat

Create a RevenueCat project, add the iOS and Android apps, then connect the store credentials requested by RevenueCat. Import or add the products you created in the stores.

Next, create:

  1. An entitlement named pro.
  2. An offering named default or current.
  3. Monthly and annual packages inside that offering.
  4. A mapping from each package's store product to the pro entitlement.

The offering is the catalog shown by the app. This lets you change which products appear without shipping a new build. RevenueCat's displaying products guide explains how offerings and packages are returned to the SDK.

Keep one active current offering unless you have a deliberate experiment. Several half-configured offerings make missing-product bugs harder to diagnose.

4. Install and configure the SDK

Install RevenueCat's SDK for your framework. Use the official React Native installation guide or the dedicated Expo guide for an Expo app.

Configure the SDK once when the app starts. Use the public RevenueCat SDK key for the current platform. Do not put store private keys, webhook secrets, or backend credentials in the app bundle.

Your initialization flow should be predictable:

text
App starts
  -> authentication state loads
  -> RevenueCat configures
  -> known user is identified
  -> CustomerInfo is fetched
  -> app renders the correct access state

Avoid configuring the SDK from several screens. A single subscription service or provider makes purchase state easier to test and prevents duplicated listeners.

5. Choose a stable customer identity

Anonymous purchases are useful when people can subscribe before creating an account. Signed-in identities are useful when access must follow a user across devices.

If your app has accounts, use the same non-sensitive internal user ID for RevenueCat after login. Do not use an email address as the primary app user ID. Emails can change and expose personal information in logs or integrations.

RevenueCat's customer identification guide explains anonymous IDs, custom IDs, login, logout, and alias behavior. Read it before mixing guest purchases with account creation.

Test these identity transitions:

ScenarioExpected result
Guest opens appAnonymous customer exists
Guest buys, then signs upPurchase follows the new account according to your identity policy
Existing user signs in on another deviceActive entitlement is restored after identity loads
User signs outPrevious account's access is no longer shown
Different account signs inApp fetches fresh CustomerInfo before unlocking features

Do not cache a simple isPro boolean forever. Subscription state can change outside the current device.

6. Fetch the offering and build the paywall

Request the current offering when the paywall opens. Render the available packages from RevenueCat rather than hardcoding localized prices.

The store provides the customer-facing price and billing period. That matters because currencies, taxes, and formatting vary. The paywall should show:

  • The plan benefits.
  • Monthly and annual options that are actually available.
  • Trial terms, if applicable.
  • The recurring billing period.
  • Restore purchases.
  • Links to the privacy policy and terms.
  • A clear way to close the paywall when it is not mandatory.

If no offering is returned, show a useful fallback and log enough context to diagnose it. Do not display an empty card or a purchase button with a guessed price.

RevenueCat also provides configurable paywalls. A custom paywall gives you more layout control, while a remotely configured paywall can make iteration faster. The purchase rules remain the same either way.

7. Handle the purchase as a stateful flow

A purchase is not just a button tap. The UI needs explicit states.

StateUI behavior
ReadyPlan choices and purchase button are enabled
PurchasingDisable repeated taps and show progress
SuccessRefresh CustomerInfo, unlock access, and close or confirm
CancelledReturn to the paywall without an alarming error
PendingExplain that approval or payment confirmation is still required
FailedShow a concise retry message and retain the selected plan

Call the SDK purchase method with the selected package. Treat the returned entitlement state as the source of truth. A successful-looking store sheet is not enough by itself.

RevenueCat's making purchases guide includes the platform methods and error handling patterns.

8. Gate features with CustomerInfo

After initialization, login, purchase, restore, or foreground refresh, inspect CustomerInfo and check whether the pro entitlement is active. RevenueCat documents the available entitlement data in its CustomerInfo guide.

Centralize the check:

text
hasProAccess = CustomerInfo.entitlements.active contains "pro"

Every premium screen should read the same access state. Do not let one screen check a product ID while another checks a locally stored flag.

Think carefully about expiration. In many apps, an expired customer should keep their data but lose paid actions. Deleting user content because billing ended creates an avoidable support problem.

9. Add restore purchases and subscription management

Apple requires a way to restore restorable purchases. Put a visible Restore Purchases action on the paywall or account screen. After restore completes, refresh CustomerInfo and explain the result.

Also include a Manage Subscription action that opens the appropriate store subscription settings. Users should be able to change or cancel a plan without contacting support.

Use calm messages:

  • Restored: "Your subscription is active again on this device."
  • Nothing found: "We could not find an active purchase for this store account."
  • Error: "Restore could not be completed. Check your connection and try again."

Do not promise a refund from inside the app. Refund decisions and workflows belong to the stores.

10. Sync subscription state to your backend

Client-side entitlement checks are enough for purely local UI access. A backend also needs subscription state when it protects paid API endpoints, grants usage credits, sends lifecycle messages, or runs paid jobs while the app is closed.

Use RevenueCat webhooks to receive events. Your endpoint should authenticate the request, store the event idempotently, and update a subscription record linked to the RevenueCat app user ID.

text
RevenueCat event
  -> verify webhook authorization
  -> reject or ignore duplicate event
  -> update subscription record
  -> recalculate server-side access or allowance

Do not trust a client request that says isPro: true. The backend should check its own synchronized record for paid operations.

Plan for renewals, cancellations, billing issues, expirations, refunds, transfers, and temporary grace periods. RevenueCat's webhook event flows show how lifecycle events relate.

11. Test the complete lifecycle

Test purchases before release with sandbox users, StoreKit configuration where appropriate, TestFlight, and Google Play test tracks. Google provides a dedicated billing testing guide.

Your checklist should cover more than the happy path:

  • Monthly and annual purchase.
  • User cancels the store sheet.
  • Network drops during purchase.
  • Purchase is pending.
  • Subscription renews.
  • Subscription enters a billing issue or grace period.
  • Subscription expires.
  • Refund or revocation removes access.
  • Restore works after reinstalling.
  • Login on a second device shows the same access.
  • Logout does not leak the previous account's entitlement.
  • Webhook delivery is duplicated or arrives late.

Enable verbose RevenueCat logs in development, never as noisy production telemetry. The RevenueCat debugging guide lists common configuration checks.

Common RevenueCat mistakes

MistakeBetter approach
Hardcoding pricesRender the localized store price from the package
Checking product IDs everywhereCheck one named entitlement
Storing isPro permanentlyRefresh CustomerInfo at important lifecycle points
Using email as customer IDUse a stable internal account ID
Unlocking backend work from a client flagVerify access with server-side subscription state
Ignoring restoreAdd and test an obvious restore action
Testing only one successful purchaseTest expiration, refund, pending, and identity changes

For a broader payment overview, see how to add in-app purchases to a mobile app.

Build your subscription app with Huxly

Huxly helps you build the mobile frontend, backend, authentication, database, and subscription flow in one project. Start from your plan rules and paywall design, add RevenueCat for iOS and Android billing, test the complete purchase lifecycle, and prepare the app for TestFlight and Google Play without starting from an empty codebase.

FAQ

Does RevenueCat replace App Store Connect and Google Play Billing?

No. Apple and Google still process purchases and control store products. RevenueCat connects those systems to a shared catalog, entitlement model, customer record, and event layer.

Should I check the product ID or the entitlement?

Check the entitlement for access. Several products, such as monthly and annual plans, can unlock the same entitlement.

Can I use RevenueCat with Expo?

Yes. Follow RevenueCat's Expo installation requirements and use a development build when native purchase functionality is required. Purchase testing cannot be treated like an ordinary web preview.

Do I need a backend for RevenueCat?

Not for a basic client-only entitlement check. You do need backend synchronization when paid access controls server endpoints, usage credits, scheduled work, or other resources outside the app.

How should I handle users without accounts?

RevenueCat can create anonymous customer identities. If those users later sign up, define and test how the anonymous purchase becomes associated with the account.

When should subscription access refresh?

Refresh after app initialization, login, purchase, restore, and relevant foreground transitions. Also listen for customer information updates provided by the SDK.

Conclusion

A reliable subscription app separates store products, RevenueCat configuration, entitlement checks, and backend access control. Configure the catalog carefully, identify customers consistently, render live store data, and make every state from purchase to expiration visible and testable.

The paywall is only the front door. Restore behavior, identity changes, webhook handling, and lifecycle testing are what keep paid access accurate after launch.