Stripe sends webhooks to a public HTTPS URL, and your development server only listens on localhost. Expose gives your local server a public URL, shows you every event Stripe sends, and lets you replay an event without creating another test payment.
Start your application, then share the port it listens on:
expose share http://localhost:3000
Expose prints a public URL such as https://abc123.eu-1.sharedwithexpose.com. If you use Laravel Herd or Valet, run expose in the project directory instead.
With Expose Pro you can keep the same URL every time, so you only configure Stripe once:
expose share http://localhost:3000 --subdomain=my-shop
https://my-shop.eu-1.sharedwithexpose.com/stripe/webhook.checkout.session.completed and invoice.paid.whsec_.Put the signing secret in your environment, for example STRIPE_WEBHOOK_SECRET=whsec_..., and let Stripe's library verify the Stripe-Signature header with it. Verification needs the raw request body, so read the body before a JSON parser changes it.
Expose forwards the request body and the Stripe-Signature header unchanged, so signature verification works the same as in production.
Make a test payment, or resend an event from the Stripe Dashboard. Then open the Expose dashboard:
http://127.0.0.1:4040
You see every request with its headers, body and your application's response. The built-in Stripe request plugin labels each request with its event type, so you can find invoice.paid at a glance.
When your handler has a bug, fix the code and click Replay on the request in the dashboard. Expose sends the same event to your local server again, so you don't need another test order. You can also change the request before replaying it.
Stripe's libraries reject signatures that are older than a few minutes (the default tolerance is five minutes). When you replay an older event, raise the tolerance in your local environment or resend the event from the Stripe Dashboard to get a fresh signature.
2xx response quickly. Return the response first and do slow work in a queue.--subdomain with Expose Pro, or update the endpoint in Stripe.expose catch-all to get a URL that accepts anything and shows the payloads in the dashboard. See Catch-All.