## Before you start

| Requirement | Why it matters |
| --- | --- |
| Existing Stripe account | read-only OAuth and visible payment ingestion |
| Beacon session authenticated as `OWNER` or `ADMIN` | authenticated API operations with real organization context |
| Public HTTPS webhook URL | secure event ingestion |
| Isolated test environment | safe validation before live |

<Warning title="Recommended environment">Run the full quickstart in `test` before enabling `live`.</Warning>

## End-to-end flow map

1. Connect Stripe.
2. Register and validate webhooks.
3. Create subscription checkout.
4. Confirm final state in dashboard.
5. Validate fiscal and observability signals.

## Step-by-step

<Steps>
  <Step title="1) Connect existing Stripe">Complete read-only OAuth and verify visible ingestion in the dashboard.</Step>
  <Step title="2) Configure webhooks">Register the Beacon endpoints in Stripe, store the environment secrets, and validate signatures.</Step>
  <Step title="3) Create checkout">Call the real checkout endpoint and redirect the user to `redirectUrl`.</Step>
  <Step title="4) Confirm outcome">After the checkout return, confirm the subscription with `sessionId` or the pending checkout state.</Step>
  <Step title="5) Validate operations">Check final state across dashboard, webhook processing, and fiscal/operational signals.</Step>
</Steps>

## API example

<Tabs>
  <Tab label="curl">
```bash
curl -X POST "https://test.beacon.pt/api/billing/subscription/checkout?org=acme&mode=test" \
  -H "Authorization: Bearer <beacon_session_token>" \
  -H "Content-Type: application/json" \
  -d '{"plan":"plus","billingCycle":"monthly"}'
```
  </Tab>
  <Tab label="node">
```ts
const response = await fetch(
  "https://test.beacon.pt/api/billing/subscription/checkout?org=acme&mode=test",
  {
  method: "POST",
  headers: {
    Authorization: "Bearer <beacon_session_token>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    plan: "plus",
    billingCycle: "monthly",
  }),
});
```
  </Tab>
</Tabs>

## Final validation

- Checkout returns `checkoutSessionId` and `redirectUrl`.
- `POST /api/billing/subscription/confirm` returns `subscription` for the correct organization.
- Webhook processing stores `webhookEventId` without unexpected duplicates.
- Dashboard state matches billing and operational timeline.
- No critical alerts in observability.

## Quick troubleshooting

| Symptom | Likely diagnosis | Immediate action |
| --- | --- | --- |
| checkout created but state unchanged | missing confirmation step | call `POST /api/billing/subscription/confirm` and verify events |
| webhook rejected | invalid signature | rotate/revalidate secret and clock drift |
| `409` conflict | pending checkout, active subscription, or domain blocker | inspect the current state before retrying |

<Tip title="Next step">After quickstart, follow the [Go-live checklist](/docs/get-started/go-live-checklist) to close operational criteria.</Tip>
