Docs / wallet / AppPaywall

AppPaywall

componentsince 0.2.4

Access gate for monetized built apps: renders children when the end-user has access, otherwise shows a paywall overlay with the unlock CTA. The default way to charge users of an app.

import { AppPaywall } from 'flowstack-sdk/paywall'

Wraps app content and surfaces the platform paywall when useAppAccess reports the user is blocked, or immediately when the backend emits an app_paywall SSE event mid-conversation (metered credits running out). It does NOT replace backend enforcement — the backend's AppAccessGate always has the final say (402 / SSE event).

This is the monetization entry point for built apps: the owner sets pricing with setMonetization (platform-level config), the app wraps its content in AppPaywall, done. Do not wire raw Stripe calls to charge app users — the owner-registered Stripe integration is for operating the OWNER'S Stripe account, not for app monetization. 0.3.0 (P0-162): when the builder configures a monthly subscription plan, the paywall renders the plan CTA above the one-time unlock. Since 0.3.3 (P0-167) subscriptions are status-only: a subscriber with an open paid period has full access and never reaches this paywall, so the subscribe CTA shows only for lapsed or never-subscribed users.

Signature

<AppPaywall siteId={config.appScope} builderTenantId?={...} onBlocked?={(reason) => ...}>{children}</AppPaywall>

Parameters

ParamTypeNotesDescription
siteIdstringrequiredSite ID of the built app (typically config.appScope).
builderTenantIdstringBuilder's tenant ID — needed to load app config.
childrenReact.ReactNoderequiredContent to show when the user has access.
onBlocked(reason: string) => voidCalled when the backend returns an app_paywall SSE event.

Examples

Wrap the app
import { AppPaywall } from 'flowstack-sdk/paywall';

export default function App() {
  return (
    <AppPaywall siteId={config.appScope}>
      <YourAppContent />
    </AppPaywall>
  );
}
Note0.3.3 (P0-167): when the backend blocks with reason app_unfunded (the app owner is out of compute credits), the paywall renders a "Temporarily unavailable" state with NO purchase CTAs — the end user can't fix owner funding, so don't offer them a buy button.
WarningImport from flowstack-sdk/paywall (0.3.2+). The flowstack-sdk/wallet entry statically imports the wagmi optional peer dependency (via useWalletAuth/useDeposit/WalletProvider) and fails to resolve in built apps that don't carry the wallet stack. /wallet still re-exports AppPaywall for consumers that do.
CriticalBackend enforcement is authoritative — AppPaywall is the UI surface, never the security boundary.
WarningMonetizing the app = platform paywall (setMonetization + this component). Do NOT use raw Stripe API calls to charge app users.
NoteListens for the app_paywall SSE event dispatched by useAgent, so metered apps gate mid-conversation without a page reload.
NoteSince 0.3.1 (P0-164), the pay-with-AGENT option renders only when payment_mode is 'agent' (token-only apps). In 'both' mode the Stripe CTA is the single visible path — the AGENT rail still charges server-side; the paywall just no longer advertises it.

See also

useAppAccess · setMonetization · getMonetization · PaymentRequired · AppSubscriptionStatus

Source: wallet/AppPaywall.tsx · Also available in llms-full.txt and registry.json.