Browse the documentation

In-app purchases with RevenueCat

2026-11-01· 5 min read· View as Markdown

If your app sells digital content, Google Play generally requires its own billing for sales made in the app. SaasToStore connects your site to Google Play subscriptions through RevenueCat. Your website code starts the purchase and receives the result: no native code.

This works on Android today. It isn't available in iOS apps yet, so hide your purchase button there (see iOS).

What you need

  • An Android Build (Native) with native navigation (Shell V2)
  • A RevenueCat project, with its public Android API key (it starts with goog_)
  • An Entitlement ID in RevenueCat, such as premium

Set it up

  1. Turn on In-App Subscriptions

    In your project's plugin library, add the plugin and fill in the API key and the Entitlement ID.

  2. Add the code to your site

    Use the example below, or the prompt for AI builders.

  3. Launch a new build

    The plugin is part of the native app, so it needs a build.

  4. Test with a test account

    Purchases only work in an app installed from a Google Play test track, signed in with a tester's account.

The four calls

billing.identify(userId);     // after sign-in, before any purchase
billing.getStatus();          // is the subscription active?
billing.purchase(productId);  // opens the Google Play purchase sheet
billing.restore();            // restore purchases

Results don't come back from the call. They arrive as an s2s:billing event on window, with a type in event.detail:

identify_result  { success, userId, error? }
purchase_result  { success, productId, isEntitled?, error? }
restore_result   { success, isEntitled, error? }
status           { isEntitled, entitlementId, expiresAt? }

Full example

const billing = await whenSaasToStore("billing");

window.addEventListener("s2s:billing", (e) => {
  const d = e.detail;
  if (d.type === "identify_result" && d.success) billing.getStatus();
  if (d.type === "status") setPremium(d.isEntitled);
  if (d.type === "purchase_result" && d.success) setPremium(d.isEntitled);
});

if (billing?.isAvailable) billing.identify(currentUser.id); // after sign-in

// your "Upgrade" button, inside the Android app:
billing.purchase("premium_monthly");

whenSaasToStore waits for the bridge to load. Its code is in the JavaScript bridge reference.

Keep the purchase state on your server too. RevenueCat can notify your server by webhook, so someone who subscribed in the app stays premium on your website.

iOS

In-app purchases aren't available in iOS apps yet. Apple doesn't allow a digital purchase through your own web checkout in most cases, so don't leave that path visible. Use the marker to hide your purchase button on iPhone:

const ua = navigator.userAgent;
const inApp = ua.includes("SaasToStoreApp");
const isIos = /iPhone|iPad|iPod/.test(ua);
const canSell = !inApp || !isIos;

What about physical goods?

Physical products and real-world services don't use store billing. A shop can keep its normal checkout: Stripe, Shop Pay or another provider. If checkout runs on another domain, it opens in the system browser, unless you add it to Extra domains.

Frequently asked questions

Where do I create my subscriptions?+

In the Google Play Console, under Monetize, then link them in RevenueCat. Your app needs to be uploaded to a test track before Google lets you create them.

Is the RevenueCat API key safe in the app?+

Yes. It's the public key, made to be embedded in apps. Never paste a secret key here.

Does RevenueCat cost extra?+

RevenueCat has its own pricing, with a free tier at the time of writing. Check their site for current terms. SaasToStore adds no commission.

Can I keep Stripe on Android?+

For digital goods, Google Play's billing rules generally apply, with exceptions that depend on the country. Check the current policy for your market before you submit.

See it with your own site

Paste your URL and get a free Android test build on your phone in about 3 minutes.

Publish now

Keep reading