An Expo app that sells subscriptions has three real options for the paywall layer: RevenueCat, Adapty, and Stripe Checkout. RevenueCat and Adapty wrap StoreKit and Google Play Billing in a native SDK. Stripe Checkout opens a page that Stripe hosts in the system browser. For most apps, pick RevenueCat. Pick Adapty for a no-code paywall builder and A/B tests on placements. Pick Stripe when you sell on the web too, or when what you sell is not digital goods that the stores require in-app purchase for.
The three share one API. Screens call payments.useEntitlements() and payments.presentPaywall?.(), so the code that gates a feature does not change when you switch. What does change is how the money moves, what Expo Go can run, and where the server code lives.
How the purchase happens
RevenueCat and Adapty sell through the stores. When the user taps Buy, StoreKit or Play Billing charges them, and the SDK reports the result. Stripe Checkout sells through a page that Stripe hosts, reached from the app by a redirect. Store billing is not involved.
That difference is what the stores care about. Apple's App Review Guideline 3.1.1 says that if you unlock features or functionality in your app, such as with subscriptions, you must use in-app purchase. Outside the US storefront, it also bars buttons and links to other purchase methods unless an Apple entitlement permits them. Google Play's Payments policy says that Google Play's billing system is required for in-app purchases of digital goods and services. Both policies have region-specific programs, and both change. Read the current text before you ship a Stripe link inside an app.
The comparison in one table
| RevenueCat | Adapty | Stripe Checkout | |
|---|---|---|---|
| Store billing (StoreKit, Play Billing) | Yes | Yes | No, hosted page in the browser |
| Runs in Expo Go | No | No, mock mode only | Yes |
| Native build needed | Yes | Yes | No |
| Paywall | Dashboard paywall, or /paywall | Paywall Builder, or /paywall | /paywall modal |
| Change the paywall without an app update | Yes, dashboard paywalls | Yes, flows and paywalls | Prices in Stripe; the screen is code |
| Server code in your tree | No | No | Yes: four API routes |
| Entitlement | pro | premium (access level) | pro (STRIPE_ENTITLEMENT) |
| Price, as of 12 Nov 2026 | Free to $2,500 monthly tracked revenue, then 1% | Free under $5K a month, then 1% of monthly tracked revenue | Billing 0.7% of billing volume; card fees separate |
RevenueCat: the default
RevenueCat wraps StoreKit and Google Play Billing behind one SDK and a unified subscription backend, and it can run the paywall from its dashboard. Its docs say that paywalls are configured remotely, without code changes or app updates. Run the setup, then add the two public SDK keys to .env:
bun run setup --payments revenuecatEXPO_PUBLIC_REVENUECAT_IOS_KEY takes the App Store key (appl_…), and EXPO_PUBLIC_REVENUECAT_ANDROID_KEY takes the Play Store key (goog_…). Create the subscriptions in App Store Connect and Play Console, add them to RevenueCat under Products, attach them to an entitlement named pro, and mark an offering as current. A product that exists only in RevenueCat never shows a price.
Then gate a feature on the entitlement:
import { payments } from "@/lib/payments";
const { active, loading } = payments.useEntitlements(); // active: ["pro"]
if (!loading && !active.includes("pro")) await payments.presentPaywall?.();presentPaywall() shows the dashboard paywall when you have designed one, and falls back to ReadyNative's /paywall route when you have not. Expo's own in-app purchases guide makes the same point: purchase libraries need custom native code, which Expo Go does not configure. The App Review steps are in RevenueCat subscriptions in an Expo Router app.
Adapty: the no-code paywall builder
Adapty uses the same store billing underneath. Its selling point is the Paywall Builder: you design a flow or paywall visually, attach it to a placement, and test variants with A/B tests. Adapty's SDK docs describe the builder as a no-code interface that renders the UI and handles the purchase for you, and its paywalls change without an app release.
bun run setup --payments adaptyThe one public key, EXPO_PUBLIC_ADAPTY_PUBLIC_KEY, serves both platforms. The placement id defaults to default; set EXPO_PUBLIC_ADAPTY_PLACEMENT_ID if yours has another name. The module also adds expo-build-properties with useFrameworks set to dynamic, which the SDK's Swift Package dependencies need. In Expo Go, the SDK falls back to mock mode: no products and no purchases.
Stripe Checkout: web sales and Expo Go
Stripe is the odd one out. ReadyNative's module has no native payments SDK. The app opens Stripe's hosted Checkout in the system browser, and your own API routes create the session, read entitlements and receive webhooks. Stripe describes Checkout as a fully-featured payment page, either embedded on your site or reached by redirect to a Stripe-hosted page. Because the purchase happens in a browser, Expo Go runs it.
bun run setup --payments stripe --backend api-routes
stripe listen --forward-to localhost:8081/api/stripe/webhookThe module needs the API routes backend, because it sets the web output to server. Keep STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET out of any EXPO_PUBLIC_ variable. The routes charge the user that the server verifies from the session, not the customer id the app sends. The repo's examples/snap-recipe runs Checkout next to RevenueCat with one merged entitlement set.
Stripe fits physical goods and services, and web sales. Apple's guideline 3.1.3(e) says that physical goods and services consumed outside the app must use other purchase methods, such as Apple Pay or traditional credit card entry. Digital unlocks are different: guideline 3.1.1 applies, so check a Stripe-only subscription against the current store rules before you ship it.
What it costs, as of 12 Nov 2026
Prices change, so these figures come from each vendor's pricing page, fetched on 12 Nov 2026. RevenueCat's Pro plan costs nothing up to $2,500 in monthly tracked revenue, then 1% of what you track. Adapty is free while you earn under $5K a month, then pays 1% of monthly tracked revenue. Stripe's pricing page lists Billing, for recurring subscriptions, at 0.7% of billing volume, and prices card payments separately. Check the pages again before you commit.
When each one wins
- RevenueCat: most subscription apps on both stores. Pick it when you want StoreKit and Play Billing handled for you, and paywalls you can change from the dashboard.
- Adapty: you want a no-code builder and A/B tests on placements, and you accept the same native build step.
- Stripe Checkout: you sell on the web as well, or what you sell is not digital goods the stores require in-app purchase for. It is also the one option that runs in Expo Go.
Read next: RevenueCat subscriptions in an Expo Router app, for the dashboard and App Review steps, and Expo Go vs a development build, for why two of these need a build. The option pages are in the payments module docs.
FAQ
What are the best paywall tools for React Native and Expo?
For most Expo apps, pick RevenueCat: it wraps StoreKit and Play Billing, and its paywall can be changed from the dashboard. Adapty suits a no-code Paywall Builder. Stripe Checkout suits web sales and runs in Expo Go.
Is RevenueCat or Adapty better for Expo?
Neither wins for every app. RevenueCat is the recommended default here. Choose Adapty when its Paywall Builder and placement A/B tests matter more. Both need a development build, and both have a free tier below a revenue threshold.
Can I use Stripe for in-app subscriptions in Expo?
You can run Stripe Checkout from an Expo app, and Expo Go works with it. The stores still decide what may be sold inside the app: Apple and Google require in-app purchase for digital goods, so check their current rules first.
More guides
- Unistyles 3 with Expo: a working setup
- Expo environment variables: which are public?
- TanStack Query in Expo: offline and refetch
- Expo zustand: persist a store across restarts
- jest-expo setup, Expo Router tests and fixes
- Expo auth: Clerk vs Supabase vs Better Auth
- AsyncStorage vs MMKV vs expo-sqlite/kv-store
- Expo i18n: i18next or Lingui, compared
- Expo android widgets: expo-widgets on SDK 58
- Tamagui in Expo: setup, styling and themes
- Expo widgets on iOS: a working setup
- RevenueCat subscriptions in an Expo Router app
- Supabase auth in Expo: email, Apple and Google
- Expo push notifications: setup, token, testing
- Expo Go vs development build: limitations
- The best Expo boilerplates, compared