The paywall
The paywall
When a feature or metered-limit gate blocks a user, the paywall turns the refusal into a
branded "upgrade to unlock" offer. It reuses the UpgradeGate — the required plan and the
hosted-checkout deep-link are the gate's own output — and only enriches it for display (the human
label of the gated capability and the required plan's price in the org's billing currency). The
upgrade logic is never recomputed here.
Hosted page
Redirect a blocked user to:
GET /paywall?org={org}&feature={feature}
GET /paywall?org={org}&meter={meter}
GET /paywall?org={org}&feature={feature}&return_url={url}
The page is self-contained and seller-branded. It shows the gated capability, the required plan
and its price, an Upgrade CTA that deep-links straight into the hosted checkout for that plan
(the gate mints/reuses an org-scoped session), and a Maybe later link back to return_url
(or the seller's support URL). When the org already has the capability, or no reachable plan
grants it, the page states that honestly rather than inventing an offer.
Because the paywall already knows the org, its CTA links directly to the hosted checkout — unlike a public pricing table, which is pre-customer and can only hand off (see the checkout deep-link contract).
Inline panel partial (console / portal only)
Inside the operator console or the hosted portal — surfaces that already load the shared
cbx-* design-system stylesheet (public/cbox/styles.css) — you can render the panel inline
instead of redirecting, reusing the gate's output:
@include('partials.paywall', [
'upgrade' => $offer, // the UpgradeGate output: {required_plan, checkout_url}
'feature' => 'Single sign-on', // label of the gated capability
'kind' => 'feature', // 'feature' | 'usage'
'price' => 'DKK 1.240,00', // optional formatted price
'per' => '/mo', // optional interval suffix
'returnUrl' => '/back', // optional "maybe later" target
])
Given no upgrade (a null/empty offer), the partial renders nothing — deny-by-default. For a
compact inline nudge rather than a full panel, partials.upgrade-gate is the lighter sibling.
This partial is not self-contained. It depends on the internal
cbx-*design tokens, so it renders unstyled outside those surfaces. To surface a paywall inside your own app, redirect to the self-contained hosted/paywallpage above (or embed the hosted pricing table) — don't reach for this partial there.