10+ AI SaaS templates for web & mobile
home

RevenueCat vs Stripe: which to use for Expo and web apps

·20 min read

RevenueCat vs Stripe for a SaaS with web and Expo apps: App Store rules, US link-outs, real fees, and one shared entitlement across platforms.

RevenueCat vs Stripe is usually the wrong framing, and that is why the question keeps showing up on Reddit and in support forums. If you run a SaaS with a Next.js web app on Stripe and you are adding an Expo (React Native) app, you are not picking one of them. You are deciding which one takes money on which surface, whether Stripe is allowed inside your app at all, and how a user who paid on the web gets the same access on their phone (and the other way around).

This guide answers all three with the rules as they stand on 15 October 2026, verified against Apple, Google, Stripe, and RevenueCat's own docs. Several of these rules changed in 2025 and again in 2026, so most older answers are out of date.

Short answer

Use both. Use Stripe on the web for checkout, subscriptions, B2B seats, and invoices. Use RevenueCat in the Expo app to sell subscriptions through Apple In-App Purchase and Google Play Billing, because digital features unlocked inside an app must use the store's billing in most of the world. Keep one entitlement in your own database, keyed by your user or organization ID, and feed it from Stripe webhooks and RevenueCat webhooks. In the US App Store, you may also link out to a Stripe checkout, and Apple currently charges no commission on those purchases. Treat that as an add-on next to in-app purchase, not a replacement.

What RevenueCat and Stripe actually are

RevenueCat is a subscription backend, not a payment processor. It wraps StoreKit (Apple) and Google Play Billing behind one SDK, validates receipts, tracks renewals, and turns purchases into entitlements (for example pro) that your app checks. On the App Store and Google Play, Apple and Google still process the payment and take their commission. RevenueCat can also sell on the web through its RevenueCat Web features, using one of three billing engines: RevenueCat Billing (its own engine, with Stripe as the payment gateway), Stripe Billing (your Stripe catalog), or Paddle Billing.

Stripe is a payment processor and billing platform. It charges cards and wallets, runs recurring subscriptions with Stripe Billing, hosts Checkout and the Customer Portal, and sends webhooks. It has no access to StoreKit or Play Billing, so it cannot sell an App Store subscription. Inside a mobile app, Stripe can only take payment where the store rules allow a non-store payment method.

So the real comparison is "store billing through RevenueCat" versus "your own checkout through Stripe", per platform and per country.

RevenueCat vs Stripe at a glance

DimensionRevenueCatStripe
RoleSubscription and entitlement layer over App Store, Google Play, and web billing enginesPayment processor plus Stripe Billing for subscriptions
Who processes the paymentApple or Google for in-app purchases; Stripe (or Paddle) for RevenueCat WebStripe
PricingFree up to $2,500 monthly tracked revenue, then 1% of tracked revenue (pricing)2.9% + 30¢ per US card charge (pricing) plus 0.7% Billing volume (Billing)
Store fees on topYes: Apple 15% to 30%, Google Play 10% + 5% billing fee on US/EEA/UK subscriptionsOnly on mobile link-outs where a store charges for them (Google Play US: 10% on subscriptions)
App Store complianceBuilt for it: uses native in-app purchaseAllowed in-app only in specific cases (US link-outs, physical goods, reader apps, and similar)
EntitlementsFirst-class: products map to entitlements, checked via SDK or REST APINo entitlement concept; you derive access from subscription status
WebhooksYes, with an authorization header and optional HMAC signature (docs)Yes, signed events like checkout.session.completed and customer.subscription.updated
Web supportWeb SDK, hosted Web Purchase Links, web paywalls; RevenueCat Billing does not support B2B sellingFull: Checkout, Customer Portal, invoices, quantities, usage-based billing
Tax handlingApple collects and remits tax on App Store sales in many territories; on the web, depends on the billing engineYou are merchant of record by default; Stripe Tax or Managed Payments optional
Best atMobile subscriptions, paywall experiments, cross-store subscriber stateWeb SaaS billing, B2B, seats, invoicing, anything outside the app stores

MTR is measured before store fees

RevenueCat bills on monthly tracked revenue (MTR), which it measures before store commission and taxes (billing settings). If you import your Stripe web revenue into RevenueCat, that revenue counts toward MTR too.

Can you use Stripe inside your Expo app?

Yes, in some places and for some things. The answer depends on what you sell and which storefront the user bought the app from, not where your company is based.

iOS: the rule and the US exception

Apple's App Store Review Guideline 3.1.1 is unchanged at its core: if you unlock features or functionality inside the app (subscriptions, premium content, a full version), you must offer in-app purchase.

What changed is the US storefront. Guidelines 3.1.1(a) and 3.1.3 now say the ban on "buttons, external links, or other calls to action" pointing to other purchase methods does not apply to United States storefront apps, and no entitlement is needed for those links (guidelines last updated 8 June 2026). That is the result of the Epic v. Apple contempt ruling of April 2025.

The fee side is still in court. Status as of 15 October 2026:

  • Apple charges no commission on US linked-out purchases today, and RevenueCat's SDK does not even report US link-out purchases to Apple (RevenueCat docs). In 2025 the Ninth Circuit upheld the contempt finding but said Apple may eventually charge a limited fee tied to its actual costs.
  • In its 13 August 2026 proffer to the district court, Apple proposed link-out commissions of 15% for standard apps, 10% for partner programs and subscription renewals, and 5% for Small Business Program apps. The court has not approved any rate.
  • The Supreme Court granted review of the contempt ruling on 30 June 2026. That case is about contempt, not the rate.

Plan for a fee, ship without one

Build US link-outs assuming the current 0% could become something between 5% and 15% later. If your margin only works at 0%, the link-out is a bet on a court ruling, not a business model.

Outside the US, apps cannot steer users to web checkout unless they hold a specific StoreKit External Purchase Link Entitlement in the storefronts where Apple offers one (the EU has its own terms). For most indie apps, that means in-app purchase everywhere except the US.

Apple also lists cases where in-app purchase is not required (guideline 3.1.3):

  • Reader apps (magazines, newspapers, books, audio, music, video) can let users access content they bought elsewhere.
  • Multiplatform services can let users access subscriptions bought on your website, provided those items are also available as in-app purchases in the app.
  • Enterprise services sold directly to organizations for their employees or students can skip in-app purchase. Consumer, single-user, or family sales cannot.
  • Real-time person-to-person services (tutoring, consultations) may use other payment methods.
  • Physical goods and services consumed outside the app must use something other than in-app purchase. That is where Stripe's regular mobile SDK belongs.
  • Free companion apps to a paid web tool (Apple's examples: VoIP, cloud storage, email, web hosting) can skip in-app purchase if the app has no purchasing at all.

Android: allowed, but not free

Google Play in the US no longer requires Play Billing (policy update). You can enroll in alternative billing (your own in-app payment UI) or external content links (link to your website). Since 1 October 2026, developers in those US programs must report transactions and pay Google's service fee: 10% on auto-renewing subscriptions through external content links or alternative billing.

Meanwhile, Play Billing itself got cheaper. Since 30 June 2026 in the US, EEA, and UK, auto-renewing subscriptions through Play Billing cost 10% service fee + 5% billing fee (service fees). Australia and Japan switched on 30 September 2026, and Google says the rest of the world follows by 30 September 2027 (announcement); until then, other markets keep a 15% subscription fee.

That makes Stripe on Android a worse deal for subscriptions than most people expect. The worked example below shows why.

What it costs: a $10/month subscription in the US

Per-transaction cost for a $10 monthly subscription sold to a US user, before taxes. RevenueCat's 1% applies only once you pass $2,500 MTR and only to revenue it tracks.

PathStore feeProcessor + billing feesYou keep (before RevenueCat)With RevenueCat 1%
iOS in-app purchase, Small Business Program$1.50 (15%)Included$8.50$8.40
iOS in-app purchase, standard, first year$3.00 (30%)Included$7.00$6.90
iOS in-app purchase, standard, after a year$1.50 (15%)Included$8.50$8.40
Google Play Billing (US)$1.50 (10% + 5%)Included$8.50$8.40
Web checkout on StripeNone$0.59 card + $0.07 Billing = $0.66$9.34$9.24 if imported
iOS US link-out to Stripe checkout (today)$0.00$0.66$9.34$9.24
Android US external link to Stripe checkout$1.00 (10%)$0.66$8.34$8.24

Sources: Apple's Small Business Program (15% for developers under $1M in annual proceeds) and subscription proceeds (70% in the first year, 85% after one year of paid service); Google Play service fees; Stripe card pricing and Billing pricing; RevenueCat pricing.

Three things fall out of this table:

  1. On iOS in the US, a link-out saves real money today, about $0.84 per $10 renewal for a Small Business Program app. If Apple's proposed 5% rate for small developers were approved, the saving would shrink to roughly $0.34.
  2. On Android in the US, Play Billing beats a Stripe link-out for a $10 subscription ($8.50 versus $8.34), because Google's 10% still applies and Stripe's fees come on top. The crossover is around $21 per charge: below it Play Billing is cheaper, above it the link-out wins on fees alone. Play Billing still has the native payment sheet on its side.
  3. Fees are not the only cost. RevenueCat reports that moving iOS users from native in-app purchase to web checkout in an A/B test with Dipsea produced "a clear dip in conversion rates" (RevenueCat blog). A 10% drop in conversions wipes out a 10% fee saving.

International cards, currency conversion, and Stripe Tax add to the Stripe rows. Check Stripe's pricing page for your country.

One entitlement across web and mobile

This is the part most "RevenueCat vs Stripe" threads skip. A user who subscribes on your website must be premium in the Expo app, and a user who subscribes in the app must be premium on the web. There are two working architectures.

Stripe sells on the web, RevenueCat sells in the app, and both send webhooks to your API. Your API writes one billing record per customer, where the customer is your user ID (B2C) or organization ID (B2B). Every client asks your API "does this account have pro?" and never asks the stores directly.

Why this wins for a SaaS:

  • Stripe stays fully Stripe. Per-seat quantities, usage-based billing, invoices, coupons, and the Customer Portal keep working, because RevenueCat never sits in the web path.
  • No extra 1% on web revenue. Only in-app revenue is tracked by RevenueCat.
  • B2B works. RevenueCat Billing does not support selling to businesses (RevenueCat Web overview), and RevenueCat's Stripe integration only sells flat-rate and per-unit prices, not tiered or usage-based ones (Stripe integration).
  • One access check. Feature gates on both clients read the same table.

The one rule you cannot skip: set RevenueCat's App User ID to your own user or organization ID, never an anonymous ID or an email. RevenueCat recommends a non-guessable ID like a UUID and warns that anonymous IDs cannot share subscription status across platforms (identifying customers).

Here is a minimal RevenueCat webhook handler in Hono that verifies the HMAC signature on the raw body, then re-fetches subscriber state from RevenueCat's REST API instead of trusting the event payload:

revenuecat-webhook.ts
import { createHmac, timingSafeEqual } from "node:crypto";
import { Hono } from "hono";

const SIGNING_SECRET = process.env.REVENUECAT_WEBHOOK_SIGNING_SECRET ?? "";
const API_KEY = process.env.REVENUECAT_API_KEY ?? "";
const TOLERANCE_SECONDS = 300;

const isValidSignature = (header: string, rawBody: string) => {
  const parts = new Map(
    header.split(",").map((part) => part.split("=") as [string, string]),
  );
  const timestamp = Number(parts.get("t"));
  const signature = parts.get("v1");

  if (!SIGNING_SECRET || !signature || !Number.isFinite(timestamp))
    return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > TOLERANCE_SECONDS) return false;

  const expected = createHmac("sha256", SIGNING_SECRET)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && timingSafeEqual(a, b);
};

export const revenuecatWebhook = new Hono().post("/", async (c) => {
  const rawBody = await c.req.text();
  const header = c.req.header("X-RevenueCat-Webhook-Signature") ?? "";

  if (!isValidSignature(header, rawBody)) {
    return c.body(null, 401);
  }

  const { event } = JSON.parse(rawBody) as { event: { app_user_id: string } };

  const res = await fetch(
    `https://api.revenuecat.com/v1/subscribers/${encodeURIComponent(event.app_user_id)}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } },
  );

  if (!res.ok) return c.body(null, 500);

  // Map the subscriber's active entitlements onto your billing table here,
  // keyed by app_user_id (your user or organization ID).

  return c.body(null, 200);
});

RevenueCat retries failed deliveries up to five times with increasing delays and disconnects after 60 seconds, so respond fast and keep the handler idempotent (webhooks). The Stripe side follows the same pattern with stripe.webhooks.constructEvent on the raw body.

Option B: RevenueCat is the source of truth

If your product is mobile-first and the web is just another storefront, you can let RevenueCat own everything. Sell on the web with RevenueCat Billing or connect your Stripe account, or keep your own Stripe Checkout and import those purchases with POST /v1/receipts or Stripe server notifications (track external purchases). Every client then checks customerInfo.entitlements.active.

This fits consumer apps with simple flat-rate plans. The tradeoffs: Stripe revenue you import counts toward RevenueCat's 1%, users cannot restore Stripe purchases from the device the way they can with store purchases, and cancellations on Stripe can take up to two hours to show up in RevenueCat unless you re-post the subscription.

Decision rules

  1. Selling a digital subscription in an iOS app outside the US? Apple in-app purchase, through RevenueCat. No real choice.
  2. iOS app, US storefront? Keep in-app purchase as the default, add a link-out to Stripe checkout as a second button, and A/B test it. RevenueCat paywalls can render a web checkout button, and its SDK skips Apple's external purchase steps for the US storefront (app-to-web purchases). Detect the storefront before showing the link, as Stripe's own iOS digital goods guide recommends.
  3. Android subscriptions in the US? Default to Play Billing through RevenueCat. Below roughly $21 per charge a link-out costs you more, and above it the saving is small, so link out mainly for pricing models Play cannot express.
  4. B2B seats bought by an admin on the web? Stripe on the web, and the app reads the organization's entitlement from your API. If you only sell directly to organizations for their employees or students, guideline 3.1.3(c) likely covers you. If individuals can also subscribe on mobile, offer an in-app purchase for them too.
  5. Reader app (magazines, newspapers, books, audio, music, video)? Guideline 3.1.3(a) lets you skip in-app purchase and offer account management only. Stripe on the web.
  6. Physical goods, bookings, or real-time person-to-person services? Stripe inside the app is not just allowed, it is required for physical goods (guideline 3.1.3(e)).
  7. Want web pricing that the stores cannot model (usage-based, tiered, annual invoices)? Stripe on the web, and keep the entitlement in your database (Option A).

Still choosing a web provider? Our Stripe vs Lemon Squeezy vs Polar vs Creem comparison covers the merchant-of-record tradeoffs; the architecture here works with any of them.

Setting up RevenueCat in an Expo app

The package names are react-native-purchases (core SDK) and react-native-purchases-ui (paywalls and Customer Center), per RevenueCat's Expo guide.

Create products in the stores

Create subscription products in App Store Connect and the Google Play Console first. The stores are the source of truth for prices; RevenueCat can only sell what exists there.

Configure RevenueCat

Create a project, connect both stores, create an entitlement (for example pro), attach your products to it, and group them into an offering. Each RevenueCat project includes a Test Store, so you can test purchases before connecting the real stores.

Install the SDK and use a development build

npx expo install expo-dev-client react-native-purchases react-native-purchases-ui

In Expo Go, react-native-purchases runs in a Preview API Mode with mocked native calls, so real purchases need a development build (EAS Build or a local build).

Configure with your own user ID

billing.ts
import { Platform } from "react-native";
import Purchases from "react-native-purchases";
import RevenueCatUI, { PAYWALL_RESULT } from "react-native-purchases-ui";

export const initBilling = (customerId: string) => {
  const apiKey = Platform.select({
    ios: process.env.EXPO_PUBLIC_REVENUECAT_APPLE_API_KEY,
    android: process.env.EXPO_PUBLIC_REVENUECAT_GOOGLE_API_KEY,
  });

  if (!apiKey) throw new Error("Missing RevenueCat API key");

  // customerId is your user ID (B2C) or organization ID (B2B)
  Purchases.configure({ apiKey, appUserID: customerId });
};

export const requirePro = async () => {
  const result = await RevenueCatUI.presentPaywallIfNeeded({
    requiredEntitlementIdentifier: "pro",
  });

  return result !== PAYWALL_RESULT.CANCELLED && result !== PAYWALL_RESULT.ERROR;
};

If the user signs in after launch, call Purchases.logIn(customerId) instead of configuring twice. The public SDK keys are safe in the app bundle; the secret REST API key belongs only on your server.

Point the webhook at your API

Add a webhook in RevenueCat's Integrations tab, set an authorization header or enable HMAC signing, and handle it as shown above. Then gate features on your API's billing summary, so a Stripe subscriber on the web unlocks the same features in the app.

Mistakes that cost you subscribers

  • Showing a Stripe link to every iOS user. Outside the US storefront that is a guideline 3.1.1 rejection risk.
  • Hiding the in-app purchase when a web option exists. Guideline 3.1.3(b) lets users access web purchases only if the items are also available as in-app purchases.
  • Revoking access the second an expiry timestamp passes. RevenueCat notes that Stripe renewal webhooks can arrive after the new period starts; keep access until an EXPIRATION event or re-check the subscriber first.

How TurboStarter wires this

TurboStarter Core ships Option A. The Expo app uses RevenueCat by default, with Superwall as a drop-in alternative behind the same @workspace/billing-mobile API. The Next.js app uses Stripe by default, with Lemon Squeezy, Polar, and Dodo Payments as alternatives. RevenueCat posts to /api/billing/webhook/revenuecat in the same API that handles Stripe, verified with an authorization header secret, and the server fetches customer data with the RevenueCat secret key on each event.

Billing resolves against the active billing reference (the user or their organization), and both apps read /api/billing/summary for feature gating. Store products sit in the shared billing config with hidden: true, so the web pricing table never shows them while both apps display the right plan (mobile billing overview). For just the mobile half, see the Expo RevenueCat integration kit and React Native RevenueCat boilerplate.

Sounds good?Now let's make it real. In minutes.
Try TurboStarter

Frequently asked questions

The verdict

RevenueCat vs Stripe is a split, not a pick: RevenueCat for store billing in the Expo app, Stripe for everything on the web, and your own database as the one place that decides who has access. Add a US link-out to Stripe on iOS if your tests show it converts, keep in-app purchase next to it, and plan for Apple's link-out fee to change. If you want that split already wired, with webhooks, organization-aware billing, and shared plan config, start from the mobile billing docs or the Expo guide for web developers.

world map
Community

Connect with like-minded people

Join our community to get feedback, support, and grow together with 1,000+ builders on board, let's ship it!

Join us

Ship your startup everywhere. In minutes.

Don't burn tokens on setup and start building features on day one.

Get TurboStarter