AgentFlow Enterprise Docs
Stripe Webhook Events
Stripe lifecycle events are verified from the raw body, persisted in Supabase, and used to update checkout, customer, subscription, billing, audit, email, and analytics records.
Endpoint and Signature Verification
| Path | Behavior |
|---|---|
/api/webhooks/stripe | Primary implemented webhook route. |
/api/stripe/webhook | Compatibility route that re-exports the same POST handler. |
The handler requires STRIPE_WEBHOOK_SECRET, reads the raw body with request.text(), and calls stripe.webhooks.constructEvent(rawBody, signature, webhookSecret). Requests without stripe-signature return 400.
Idempotency and Persistence
| Table | Purpose |
|---|---|
billing_events | Stores stripe_event_id, event type, raw provider payload, processing_status, error_message, and processed_at. Unique event ids provide idempotency. |
checkout_sessions | Checkout route writes created sessions; webhook marks successful sessions as completed. |
customers | Stores organization to Stripe customer mapping. |
subscriptions | Mirrors Stripe subscription id, customer id, price id, plan, status, period dates, cancellation flags, and cancellation timestamp. |
audit_events | Records billing lifecycle events for operator review. |
If an event already exists and has processed_at, the route returns { "received": true, "duplicate": true }. If an existing event has not finished processing, the handler can mark it back to processing and retry. Handler failures mark the billing event as failed with a truncated safe error message and return 500 so Stripe can retry.
Handled Events
| Event | Source | Purpose | Supabase update | Failure and retry behavior |
|---|---|---|---|---|
checkout.session.completed | Stripe Checkout | Activates the paid checkout lifecycle after successful payment. | Resolves checkout_sessions, upserts customers, upserts subscriptions, marks checkout completed, writes audit event, sends confirmation email when SES is configured. | Failure marks billing_events.processing_status = failed and returns 500 for Stripe retry. |
customer.subscription.created | Stripe Billing | Creates subscription state from Stripe. | Upserts subscriptions and related customers by subscription/customer identifiers. | Unknown organization or plan fails when persistence is required. |
customer.subscription.updated | Stripe Billing | Keeps subscription status, price, period, and cancellation flags current. | Upserts subscriptions with current Stripe state. | Duplicate processed events are skipped; failed events are retryable. |
customer.subscription.deleted | Stripe Billing | Reflects cancellation/deletion state. | Upserts subscriptions with Stripe status and canceled_at where available. | Failure is stored on billing_events and retried by Stripe. |
invoice.payment_succeeded | Stripe Billing | Refreshes subscription state after successful renewal/payment. | Retrieves the subscription, upserts subscription/customer, writes billing audit event. | Invoices without subscription ids are skipped with a warning. |
invoice.payment_failed | Stripe Billing | Refreshes subscription state and triggers payment-failure notification path. | Retrieves subscription, upserts state, writes billing audit event, sends payment-failure email when SES is configured, tracks Amplitude event. | Failure marks event failed and returns 500. |
Sanitized Payload Fragments
{
"id": "evt_placeholder_checkout",
"type": "checkout.session.completed",
"data": {
"object": {
"id": "cs_test_placeholder",
"object": "checkout.session",
"mode": "subscription",
"customer": "cus_placeholder",
"subscription": "sub_placeholder",
"client_reference_id": "organization_uuid_placeholder",
"metadata": {
"organization_id": "organization_uuid_placeholder",
"user_id": "user_uuid_placeholder",
"plan_key": "growth",
"provider": "stripe"
}
}
}
}{
"id": "evt_placeholder_subscription",
"type": "customer.subscription.updated",
"data": {
"object": {
"id": "sub_placeholder",
"object": "subscription",
"customer": "cus_placeholder",
"status": "active",
"cancel_at_period_end": false,
"items": {
"data": [
{
"price": {
"id": "price_placeholder_growth"
}
}
]
},
"metadata": {
"organization_id": "organization_uuid_placeholder",
"plan_key": "growth"
}
}
}
}Live-mode verification
Live-mode payment, webhook delivery, subscription persistence, failed-payment handling, email delivery, and dashboard access should be tested before public commercial launch if they have not been proven in the target production environment.