Open the event deliveries for your endpoint
In Workbench, select the endpoint under Webhooks and open the Event deliveries tab. Find the checkout.session.completed or invoice.paid event for the affected customer. Note whether it is Delivered, Pending or Failed, and its HTTP status code.
Docs: docs.stripe.com →
Match the status code to Stripe's fix list
Stripe counts redirect responses as failures, so check that the registered URL does not redirect. For a 4xx, make sure the endpoint is publicly accessible and accepts POST. For a 500, read your application logs. For a timeout, return a 2xx before the slow work.
Docs: docs.stripe.com →
Verify signatures against the raw body and the right secret
Verify the Stripe-Signature header using the raw request body and the signing secret of that endpoint. Make sure your framework does not modify the raw body before verification. If you moved from test to live, confirm the handler uses the secret of the live endpoint.
Docs: docs.stripe.com →
Check which events grant access for one-off payments
The handler should call your grant function on checkout.session.completed and on checkout.session.async_payment_succeeded for delayed payment methods. Check the payment_status property of the Checkout Session before granting.
Docs: docs.stripe.com →
Grant subscription renewals from invoice.paid
On invoice.paid, find the customer, retrieve the subscription and confirm its status is active before extending the access expiry date. Revoke access when the status becomes canceled or unpaid.
Docs: docs.stripe.com →
Resend the events your endpoint missed
After fixing the endpoint, resend each missed event from the Dashboard (up to 15 days after creation) or with the CLI (up to 30 days). A manual resend doesn't cancel automatic retries, so the grant must tolerate a repeat.
stripe events resend <event_id> --webhook-endpoint=<endpoint_id>
Docs: docs.stripe.com →
Make the grant safe to repeat and to run out of order
Log the event IDs you have processed and skip repeats. Make the grant function safe to call several times for the same Checkout Session ID, even concurrently. If an event arrives first, retrieve the missing objects through the API instead of assuming an order.
Docs: docs.stripe.com →