18 · Integrations
Stripe webhook signature verification
Verify the stripe-signature header against the raw body. c.req.text() caches the body, so later .json() calls don't throw.
Code
stripe-webhook.js
const { WebApp } = require(class="tk-str">39;@hyperbridge/forge/server39;); const crypto = require(class="tk-str">39;crypto39;); const app = new WebApp(); app.post(class="tk-str">39;/webhook/stripe39;, async (c) => { const sig = c.req.header(class="tk-str">39;stripe-signature39;); const raw = await c.req.text(); const parts = Object.fromEntries( sig.split(class="tk-str">39;,39;).map(p => p.split(class="tk-str">39;=39;)) ); const expected = crypto.createHmac(class="tk-str">39;sha25639;, process.env.STRIPE_SECRET) .update(class="tk-str">`${parts.t}.${raw}`) .digest(class="tk-str">39;hex39;); if (parts.v1 !== expected) return c.text(class="tk-str">39;bad signature39;, 400); const event = JSON.parse(raw); console.log(class="tk-str">39;stripe event:39;, event.type); return c.json({ received: true }); }); app.listen(3000);
How it works
Stripe signatures hash the raw request body, so the body must be read as text before parsing. WebApp's body cache makes this trivial.
On Cloudflare Workers, swap crypto for the Web Crypto API — crypto.subtle.importKey(...) then sign(...).
Same signature-verification pattern works for GitHub webhooks (X-Hub-Signature-256), Shopify (X-Shopify-Hmac-SHA256), and most webhook providers.
Try it
Quickstart
stripe listen --forward-to http://localhost:3000/webhook/stripe
Related modules
This is HBForge's port of Hono's example. Read the original at hono.dev/examples/stripe-webhook.