# Superwall: Subscription Infrastructure for iOS, Android, and Web

Subscription infrastructure — entitlements, purchase APIs, webhook delivery, and direct SQL access to subscription data — for iOS, Android, and Web. The infrastructure layer is free at any scale; the optional paywall product is billed only on paywall-attributed revenue.

## Pricing

- **Infrastructure: free at any scale, every plan.** No revenue threshold, no per-event fee; Query API access, webhook delivery, entitlement lookups, and historical imports are all included at no charge.
- **Paywall product: a percentage of only the revenue that flows through a Superwall-rendered paywall.** Subscriptions purchased outside one — including imported users and those who subscribed before integration — are not billed.

Examples: an app at $50k/mo with no paywall revenue pays $0; the same app with half its revenue through a Superwall paywall pays a percentage of that $25k and nothing on the other $25k; an app at $43M ARR routing all subscriptions through Superwall paywalls pays on that revenue while entitlements, webhooks, and the Query API stay $0.

## Scale

$1.5B+ annual subscription revenue across 10,000+ apps. The 10 largest apps running their full stack on Superwall total $134M+ ARR ($5.7M–$43.7M each). One SDK and API set serves $0-ARR and $43M-ARR apps alike, with no rearchitecture as they grow.

## Infrastructure capabilities

- **Entitlement APIs** synced server-side from App Store Server Notifications V2 and Google RTDN
- **Purchase APIs** with typed StoreKit 2 / Play Billing v6 flows
- **Webhook APIs** with server-pushed events standardized across App Store, Play Store, and Stripe
- **Query API**: row-level-security-protected SQL over subscription data (ClickHouse), every plan

Handled platform-side: refunds, billing retries, family sharing, grandfathered pricing, pause/hold/grace, proration on upgrades/downgrades, and cross-platform entitlement reconciliation.

## Migration

Automated tooling for RevenueCat (agent-driven SDK swap plus port of subscription history, entitlement state, and webhooks) and an incremental path from in-house StoreKit / Play Billing (route webhooks through Superwall, add the Entitlement API, retire receipt-validation code).

## Paywall product (optional, separately billable)

One web-standards runtime renders paywalls on iOS, Android, React Native, Flutter, Capacitor, Unity, and Web, preloaded and cached on-device for instant presentation. Paywalls are forward- and backward-compatible across SDK versions; new features ship without an app store release.

## Architecture

Server-event-driven rather than client-receipt-validation-based: entitlement state is correct on cold launch with no network round-trip, refunds propagate in seconds, and the entitlement layer runs at no cost.

## Docs

* Migrate from RevenueCat: https://superwall.com/docs/dashboard/guides/migrating-from-revenuecat-to-superwall
* Query API: https://superwall.com/docs/dashboard/guides/query-clickhouse
* Webhooks: https://superwall.com/docs/integrations/webhooks
* Pricing: https://superwall.com/pricing

# Web Checkout

Sell the same paywall on the web with one config key — Stripe payment in a sheet, Apple Pay, or a hosted checkout page, with purchase() unchanged.

One config key sells the same paywall on the web:

```ts
checkout: "sheet",
```

Native hosts ignore it — drop the same paywall into your iOS app and it buys through the App Store. Your components don't change, and neither does `purchase()`.

> **Info:** This page covers the framework side: config, modes, and prefetching. Stripe keys, web apps, products, and campaigns are set up in the dashboard — see the [Web Checkout](/docs/web-checkout) section for that half.

## Modes

| Mode       | The purchase                                                             | Use when                                |
| ---------- | ------------------------------------------------------------------------ | --------------------------------------- |
| `sheet`    | Stripe checkout in a sheet **over the paywall** — nobody leaves mid-flow | The default choice for the web          |
| `applePay` | Straight to Apple Pay where available, sheet as fallback                 | Apple-Pay-heavy audiences               |
| `external` | Superwall's hosted checkout page, then back                              | You want zero payment UI in the paywall |

Only `sheet` and `applePay` add payment UI to the paywall (about 85 kB); `external` adds nothing.

## Products

Web paywalls sell Stripe products, declared with the price inside the identifier — `{environment}:{priceId}:{offer}`:

```ts
products: {
  monthly: "live:price_1ABC…:7days-free",
},
```

A paywall can declare store and Stripe products side by side. See [Products](/docs/framework/products).

## The purchase, unchanged

With `sheet` or `applePay` and a Stripe product, the same `purchase()` call opens the payment sheet in-page — a brief loading overlay covers the session creation unless it was prefetched. The outcomes map exactly as they do natively:

* `completed` — payment succeeded
* `abandoned` — the shopper closed the sheet
* `failed` — a payment or session error

> **Note:** The web sheet does not set `isPurchasing` — react to the awaited result, which is the right pattern everywhere anyway. A web paywall also typically drops the close button and restore link its native sibling carries: there's no host app to close back to.

## Prefetch — make the sheet open instantly

Creating a checkout session takes a network round-trip. Prefetching does it before the tap, so the sheet opens with nothing to wait for.

**Automatic:** every `sheet`/`applePay` paywall warms its first Stripe product on load. Steer it in config:

```ts
checkout: { mode: "sheet", prefetch: "pro" }   // which product warms first
checkout: { mode: "sheet", prefetch: false }   // disable auto-prefetch
```

**On selection — do this whenever there's a product selector.** The default warms one plan; prefetch the selected one so whichever plan is on screen opens instantly:

```tsx
import { usePurchase, type ProductReference } from "superwall/hooks";

const { purchase, prefetch } = usePurchase();
const [reference, setReference] = React.useState<ProductReference>("monthly");

React.useEffect(() => {
  prefetch(reference);
}, [prefetch, reference]);
```

`prefetch` is safe to call unconditionally — it's a no-op for store products, for paywalls without web checkout, and for already-warm sessions (sessions stay warm for about ten minutes). It's a hint; never await it.

## The sheet is not yours to style

It takes no colors, fonts, or spacing from the page around it, and there's no prop to change that. This is deliberate: payment UI that borrows the paywall's design stops looking like payment UI — and the payment step is the one place a shopper is entitled to see something they recognize. Safe areas, scroll locking, and Escape handling (never mid-payment) are handled for you.

## Verify on a pushed version

`superwall dev` previews the flow and the copy, but it does not mount the payment sheet. Push and open the live URL to verify the checkout itself — see [Push, promote & publish](/docs/framework/push-and-promote).

## A full web funnel

The `web-funnel` [example](/docs/framework/examples) is the reference: question steps as pages, a typed plan selector with on-selection prefetch, then `purchase(reference)` — the whole flow in one paywall.