# `purchase_completed`

## Use when / do not use when

Use for the canonical terminal after an initial subscription or one-time purchase flow. Do not use for a generic handoff, receipt, renewal, or pre-purchase summary. Entering this screen does not prove that the backend verified payment.

## Exact metadata contract

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

- Step contract version: `3`
- Contract hash: `d761e91d5ac6ff9e72c6d49c5bcd014270912473a66f99c49d726c65998085cf`
- Type: `purchase_completed`
- Family: `terminal`
- Description: The canonical terminal shown after a verified initial purchase.
- Action owner: `step`
- Analytics role: `purchase-completed`
- Terminal: `true`
- Required kind: `subscription-handoff`
- Allowed kinds: `subscription-handoff`
- Allowed semantic events: `funnel_completed`
- Forbidden capabilities: `choice-write`, `email-capture`, `checkout`, `subscription-management`, `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

- `subscription-handoff` must be owned by `runtime-handoff-helper`.

#### Reserved identities

- `subscription-started`: id=`subscription-started`, name=`subscription-started`; type=`purchase_completed`; kind=`subscription-handoff`; unique=`true`

#### Golden fixture source

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

const terminalCompletionHelper = 'completeFunnel' as const satisfies keyof FunnelContextValue;

const manifestStep = {
  id: 'subscription-started',
  name: 'subscription-started',
  path: '/subscription-started',
  filePath: 'src/steps/step-subscription-started.tsx',
  componentKey: 'stepSubscriptionStarted',
  type: 'purchase_completed',
  kind: 'subscription-handoff',
  title: 'Purchase completed',
} as const satisfies FunnelManifestStep;

const componentMeta = {
  id: 'subscription-started',
  name: 'subscription-started',
  type: 'purchase_completed',
  kind: 'subscription-handoff',
  figmaNodeId: 'golden:purchase-completed',
  title: 'Purchase completed',
  description: 'Complete the funnel after purchase and continue into the app.',
} as const satisfies FunnelStepMeta;

export const goldenFixture = {
  stepType: 'purchase_completed',
  manifestStep,
  componentMeta,
  helper: 'runtime handoff helper',
  semanticHelpers: [terminalCompletionHelper],
  forbiddenBrowserEvents: ['purchase_completed', 'registration_completed'],
} as const;
```
<!-- funnelsgrove:generated:end contract-v3/step/purchase_completed -->

## Interaction and persisted answer shape

The runtime terminal/handoff helper owns completion and destination construction. No choice answer is persisted.

## Automatic lifecycle events

`completeFunnel(stepId)` atomically claims the active terminal visit, emits the normal step completion and one deduplicated `funnel_completed`, then flushes before outbound handoff. Preview/editor still claims the local completion outcome while suppressing persistence and provider delivery.

## Required semantic conversions

The browser may request lifecycle-only funnel completion. The business conversion `purchase_completed` is trusted-server-only after the backend verifies a successful initial subscription or one-time payment. `registration_completed` remains a separate later app-link claim.

## Approved shared helpers

Use `completeFunnel` through the runtime handoff surface. The reserved terminal passes `completionMode="funnel"` to `SubscriptionHandoffScreen`; the shared component defaults to `completionMode="step"` for non-terminal handoffs.

## MUST rules

- Preserve the reserved `subscription-started` identity.
- Complete the funnel once before the app handoff.
- Keep terminal completion mode explicit; only the current `purchase_completed` terminal may call `completeFunnel`.

## NEVER rules

- Never emit `purchase_completed` in browser code.
- Never treat terminal reach, `funnel_completed`, a pending payment, or a renewal as purchase verification.

## Golden template or fixture

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

## Validation command

Run `fgrove validate`.

## Manual QA and completion criteria

Confirm funnel completion is deduplicated, the trusted purchase event requires backend verification, preview is silent, and the app destination works.
