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
Turn on In-App Subscriptions
In your project's plugin library, add the plugin and fill in the API key and the Entitlement ID.
Add the code to your site
Use the example below, or the prompt for AI builders.
Launch a new build
The plugin is part of the native app, so it needs a build.
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 purchasesResults 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.