# `paywall_offer`

## Use when / do not use when

Use only for a monetization screen that presents purchasable plans. Do not use for a loading, calculation, reveal, or pre-offer bridge.

## Exact metadata contract

<!-- funnelsgrove:generated:start contract-v3/step/paywall_offer -->
### Generated contract facts — do not edit

- Step contract version: `3`
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
- Type: `paywall_offer`
- Family: `commerce`
- Description: A monetization offer that presents purchasable plans.
- Action owner: `step`
- Analytics role: `paywall`
- Terminal: `false`
- Required kind: `paywall`
- Allowed kinds: `paywall`
- Allowed semantic events: `checkout_started`, `add_payment_info`, `checkout_completed`
- Forbidden capabilities: `choice-write`, `email-capture`, `subscription-management`, `subscription-handoff`, `hardcoded-navigation`, `provider-direct-analytics`, `browser-registration-completed`

#### Choice contract

_Not a choice type._

#### Automatic lifecycle events

`first_step_clicked`, `first_step_viewed`, `funnel_started`, `step_completed`, `step_exited`, `step_started`

#### Approved capability owners

- `checkout` must be owned by `named-payment-helpers`.

#### Reserved identities

- `paywall`: id=`paywall`, name=`paywall`; type=`paywall_offer`; kind=`paywall`; primary=`true`; unique=`true`
- `paywall-variant`: idPattern=`^paywall-[a-z0-9-]+$`, namePattern=`^paywall-[a-z0-9-]+$`; type=`paywall_offer`; kind=`paywall`; primary=`false`; unique=`false`

#### Golden fixture source

```ts
import type { FunnelManifestStep, FunnelStepMeta } from '@funnelsgrove/runtime';

const manifestStep = {
  id: 'paywall',
  name: 'paywall',
  path: '/paywall',
  filePath: 'src/steps/step-paywall.tsx',
  componentKey: 'stepPaywall',
  type: 'paywall_offer',
  kind: 'paywall',
  title: 'Choose your plan',
} as const satisfies FunnelManifestStep;

const componentMeta = {
  id: 'paywall',
  name: 'paywall',
  type: 'paywall_offer',
  kind: 'paywall',
  figmaNodeId: 'golden:paywall-offer',
  title: 'Choose your plan',
  description: 'Present purchasable plans.',
} as const satisfies FunnelStepMeta;

export const goldenFixture = {
  stepType: 'paywall_offer',
  manifestStep,
  componentMeta,
  presentsPurchasablePlans: true,
  semanticHelpers: ['trackCheckoutStarted', 'trackPaymentInfoSubmitted', 'trackCheckoutCompleted'],
} as const;
```
<!-- funnelsgrove:generated:end contract-v3/step/paywall_offer -->

## Interaction and persisted answer shape

Resolve offers and plans through shared payments configuration. Do not persist plan data as a choice answer.

## Automatic lifecycle events

The controller owns view/start/completion/exit lifecycle. Closing or leaving without purchase is an exit, not a completion.

## Required semantic conversions

Pass `checkoutAnalytics` into `SharedStripeCheckoutV2Dialog` and every `StripeSubscriptionWalletSurface` or `StripeOneTimeWalletSurface`. Those shared components emit checkout started, payment info, and verified checkout completed with the real Checkout Session identity. Do not call the analytics SDK methods directly.

## Approved shared helpers

Use shared plan resolution, the checkout hook matching the resolved payment mode for the card dialog, `SharedStripeCheckoutV2Dialog`, and one private-session wallet surface matching that payment mode per visible placement. Call `completeStep` only from `onSuccess`.

## MUST rules

- Keep the primary paywall identity exact; variants use contract-compliant distinct identities.
- Present at least one purchasable plan.
- Read prices, discounts, provider IDs, and payment mode from shared configuration.
- Keep card and wallet Checkout Sessions isolated even when they show the same plan.

## NEVER rules

- Never classify a reveal/interstitial as a paywall.
- Never put secrets, provider IDs, amount math, or provider analytics in JSX.
- Never complete the step on payment-info submission or manually emit checkout semantic events.

## Golden template or fixture

See the [embedded canonical fixture](#golden-fixture-source) generated from `paywall_offer.fixture.ts`.

## Validation command

Run `fgrove validate`.

## Manual QA and completion criteria

Verify plans and discounts, every payment method, decline/retry, checkout event metadata/deduplication, cancel/exit, and manifest routing after success.
