POST /api/v1/webhooks/stripe.
Handled events
Idempotency
Every handler checks current state before writing. Running the same handler twice with the same event produces the same result:checkout.session.completed— checks ifStripeSubscriptionIdalready matches before creating a new recordcustomer.subscription.updated— compares incoming values to current values; if identical, no database writecustomer.subscription.deleted— if status is alreadyCanceled, no-opinvoice.payment_failed— if status is alreadyPastDue, skips the email and notification to avoid duplicate sendsinvoice.payment_succeeded— if status is alreadyActive, no-op
AspFox returns
200 OK for all webhook events, including events it does not handle. Stripe requires a 2xx response to consider delivery successful. Returning non-2xx for unhandled events causes Stripe to retry indefinitely and eventually disable the endpoint.Webhook signature verification
Every request to the webhook endpoint is verified using theStripe-Signature header and STRIPE_WEBHOOK_SECRET. Requests with an invalid or missing signature are rejected with 400 Bad Request before any processing occurs.
Do not disable signature verification. The webhook endpoint is public — without signature verification, anyone could send fake webhook events to manipulate subscription state.
Webhook delivery timing
Stripe delivers webhooks asynchronously after the triggering event. The delay is typically under 30 seconds but can be longer during Stripe incidents. AspFox does not poll Stripe on every request — local state may lag behind Stripe by up to 30 seconds after an event, or up to 2 hours in the case of a missed webhook (corrected bySubscriptionSyncJob).