Stripe webhook security
The Stripe webhook is unauthenticated by user session and therefore authenticates the provider message cryptographically before trusting its content.
Security · Billing and subscriptions
Validation flow
POST /stripe/webhook:
- reads the original raw request body;
- requires
Stripe__WebhookSecret; - validates
Stripe-Signatureusing the Stripe .NET SDK; - rejects invalid signatures with
InvalidStripeSignaturebefore inserting trusted state; - inserts a unique
StripeEventInboxrow by Stripe event ID; - acknowledges duplicates without reapplying them;
- enqueues
ProcessStripeEventCommandfor replay-safe application.
Request-body logging explicitly excludes the webhook DTO. Keep the endpoint behind HTTPS and do not place a generic proxy/body transformation in front of signature verification.

Tenant correlation
Checkout attaches opaque organization and plan-version metadata. Webhook application resolves the organization from controlled metadata or an existing Stripe Customer mapping. It never trusts a browser-provided subscription status.
Validate that Product, Price, Customer, Subscription, and keys all belong to the same Stripe account and mode. Treat unknown/missing organization mappings as recoverable operational events, not a reason to attach the event to a guessed tenant.
Replay and ordering
Stripe may retry, duplicate, or deliver events out of order. Handlers update the local projection idempotently and reconciliation compares known Stripe subscriptions later. Never produce a second entitlement grant, notification, or usage mutation merely because the same event ran again.
The browser Checkout-success confirmation covers the immediate return path; it does not replace signed webhooks for future renewals, failures, cancellation, and portal changes.
Secret rotation and incident handling
Coordinate webhook endpoint secret rotation with the deployed Stripe__WebhookSecret. Test a signed event after rotation before retiring the previous endpoint/configuration. Do not temporarily disable validation to restore billing.
For suspicious events, preserve the inbox/event ID, request ID, timestamp, signature outcome, and Stripe Dashboard delivery. Reconcile from Stripe's authenticated API only after resolving the cause.
Verify
Test valid signature, tampered body, absent/wrong secret, duplicate ID, retry, out-of-order relevant events, missing tenant mapping, test/live mismatch, failed command recovery, and reconciliation.