HBForge/Examples
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.

forge/serverNodeBunDenoCF Workers
Code
stripe-webhook.js
const { WebApp } = require(class="tk-str">'@hyperbridge/forge/server');
const crypto    = require(class="tk-str">'crypto');
const app       = new WebApp();

app.post(class="tk-str">'/webhook/stripe', async (c) => {
  const sig   = c.req.header(class="tk-str">'stripe-signature');
  const raw   = await c.req.text();
  const parts = Object.fromEntries(
    sig.split(class="tk-str">',').map(p => p.split(class="tk-str">'='))
  );
  const expected = crypto.createHmac(class="tk-str">'sha256', process.env.STRIPE_SECRET)
    .update(class="tk-str">`${parts.t}.${raw}`)
    .digest(class="tk-str">'hex');

  if (parts.v1 !== expected) return c.text(class="tk-str">'bad signature', 400);

  const event = JSON.parse(raw);
  console.log(class="tk-str">'stripe event:', 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