How Stripe webhooks work

Understand Stripe webhook events, signature checks, retries, and how payment changes become durable application access.

By Antoine RossPublished Facts verified

A Stripe webhook is an HTTPS request Stripe sends to your server when an event occurs in your Stripe account. Your server verifies the request, processes the event, and records the result. This lets your app react to events that happen after the browser leaves, such as a subscription payment or cancellation.

A customer returning from Checkout is useful for showing a confirmation page, but the return URL alone is not payment proof. The server needs verified payment information and a durable record of the access the customer has earned.

Follow one purchase

Imagine a customer buys a subscription. Your app creates a Checkout Session for the intended price and associates it with the right customer or application account. Stripe presents Checkout and later sends events to your configured endpoint. Your endpoint verifies the event, identifies the corresponding account and subscription, and updates your database. When the customer opens a premium page, your server verifies the current user and reads that access state.

Later, a payment can fail or a subscription can end. Your billing policy decides what those states mean for access, and the server updates the durable record as verified information arrives. Make the policy explicit: for example, whether a cancellation takes effect immediately or at the end of a paid period depends on the subscription and your product rules.

Stripe documents receiving webhook events and the subscription lifecycle. Pick the events and authoritative fields for your particular checkout and billing model. Do not assume every checkout.session.completed represents a settled payment for every payment method.

Verify before changing anything

An endpoint is publicly reachable, so receiving JSON that looks like a Stripe event is not enough. Read the original raw request body and the Stripe-Signature header, then use Stripe's official library and the endpoint's signing secret to verify the signature before trusting the event or changing state. Parsing and reserializing the body first can break verification. Stripe explains signature errors and endpoint management.

Keep the signing secret on the server. The secret printed by the local Stripe CLI listener and the secret for a registered hosted endpoint are separate; configure the correct one in each environment. A test event and a live event also belong to different environments and must not change each other's customer records.

Expect retries, duplicates, and different ordering

Stripe may retry a failed delivery, and events can arrive more than once or out of order. Stripe's delivery guidance describes these behaviors. Make processing idempotent: record the event ID and its processing outcome durably, and ensure retrying a completed event cannot grant access twice or send duplicate side effects. A failed or interrupted attempt must be recoverable; recording an ID as completed before its work succeeds can lose the change.

An event ID handles repeated delivery of that event. It does not by itself solve two different events concerning the same subscription. Reconcile the relevant provider state and apply your state transition rules so an older event cannot undo a newer result. Keep the endpoint's response and retry behavior consistent with whether the work was actually accepted or completed, and monitor failures.

Connect the event to the right user

Never grant premium access based on an email or user ID supplied by the browser alone. Create the checkout on your server, bind it to the authenticated account or a deliberately designed guest purchase flow, and validate the relevant customer, product, price and subscription against trusted records. Store the resulting entitlement in your database. The page, API routes and server actions that serve premium data must each check the verified identity and current entitlement.

The Next.js Stripe billing guide covers the implementation path. Authentication in Next.js explains the separate identity check.

Test and operate the path

Use Stripe's test environment and the Stripe CLI forwarding workflow to deliver events to your local endpoint. Complete a test Checkout for a real test customer, inspect the delivery result, and verify that the intended database record and protected page agree. Repeat delivery to check idempotency; then test failed payment and cancellation according to your policy. A synthetic CLI-triggered event is useful to exercise a handler, but does not prove that your own Checkout, customer mapping and access path work together.

For a deployed endpoint, register its public HTTPS URL, configure the corresponding signing secret and subscribed events, and inspect delivery failures. Keep a recovery procedure for missed events and reconcile provider state before restoring access. Never expose signing secrets in logs or client code.

The sold CoreMVP Next.js source includes a Stripe webhook route that verifies the raw body and signature and a billing service that records event processing. Its billing documentation is the place to configure and verify the shipped path for your own account.