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:

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().

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 section for that half.

Modes

ModeThe purchaseUse when
sheetStripe checkout in a sheet over the paywall — nobody leaves mid-flowThe default choice for the web
applePayStraight to Apple Pay where available, sheet as fallbackApple-Pay-heavy audiences
externalSuperwall's hosted checkout page, then backYou 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}:

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

A paywall can declare store and Stripe products side by side. See 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

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:

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:

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.

A full web funnel

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

How is this guide?

On this page