Stripe webhooks
Endpoint: POST /v1/webhooks/stripe — raw body required for signature verification (stripe-signature + webhook secret). No session auth on this route.
Handled events
| Event | Behavior |
|---|---|
checkout.session.completed | If payment_status === paid, resolve org + pack from session metadata, grant credits from the server catalogue (creates lot + purchase ledger). Duplicate session id is ignored (idempotent). |
checkout.session.async_payment_succeeded | Same grant path as above for async payment methods that complete later. |
All other event types — acknowledged with success; no credit changes.
Outcomes
- Unpaid or pending session on a handled event → success response, no grant.
- Paid session with missing org/pack metadata → error so Stripe retries.
- Invalid signature → validation error (not a retry loop for bad clients).
- Successful handling (including duplicates and ignored types) → 200 so Stripe stops retrying.
Credits granted use pack size from the catalogue, not client-supplied amounts alone.