Edit on val.town ↗

Host a Stripe webhook receiver

A Stripe webhook receiver is just an HTTPS endpoint, but it has to be deployed somewhere public before Stripe will deliver to it — which makes a plain HTTP val a natural home for one. This template is a ready-made receiver: it verifies the Stripe-Signature header against your webhook signing secret, stores each event in the val's SQLite database, and returns 200 immediately.

If you're starting from the payments side — payment links, Stripe Checkout, and what a fulfillment webhook is for — read the Stripe webhooks guide first. This page is just the hosted-receiver part.

The saveEvent helper in db.ts creates a stripe_events table (id, type, created, full event JSON) and ignores duplicate deliveries of the same event id, so Stripe's at-least-once delivery doesn't create duplicate rows.

  1. Remix this template — click the Remix button in the top right corner.
  2. In the Stripe dashboard, add a webhook endpoint pointing at your val's HTTP URL (shown on the HTTP trigger of main.ts).
  3. Copy that endpoint's signing secret (it starts with whsec_) and add it to your val's environment variables as STRIPE_WEBHOOK_SECRET.

To test it, configure your endpoint in Stripe test mode and run stripe trigger payment_intent.succeeded with the Stripe CLI. Expect {"received":true} and a new row in stripe_events — the val's README also shows how to forge a correctly signed test event with curl. Requests with a missing or bad signature get a 400.

Last updated