Getting Started
Install @hooksentinel/core and wire up a verified Stripe webhook handler in Express in under 60 seconds.
Install
npm install @hooksentinel/coreNo peer dependencies are required for the core package. Framework adapters (Express, Fastify, NestJS, Next.js, Hono, Lambda) are exported from subpaths so you only pull in what you use — see Bundle size.
60-second quickstart: Stripe + Express
1. Get your webhook signing secret
From the Stripe dashboard, or via the CLI for local testing:
stripe listen --forward-to localhost:3000/webhooks/stripeStripe prints a whsec_... secret — put it in your environment.
STRIPE_WEBHOOK_SECRET=whsec_...2. Create the handler
import { createWebhookHandler, stripe } from '@hooksentinel/core';
import { memoryStore } from '@hooksentinel/core/stores';
export const stripeWebhook = createWebhookHandler({
provider: stripe({ secret: process.env.STRIPE_WEBHOOK_SECRET! }),
idempotency: memoryStore(),
onEvent: async (event) => {
switch (event.type) {
case 'checkout.session.completed':
await fulfillOrder(event.data.object.id);
break;
case 'invoice.payment_failed':
await notifyDunning(event.data.object.customer as string);
break;
}
},
});
async function fulfillOrder(sessionId: string) {
// your business logic
}
async function notifyDunning(customerId: string) {
// your business logic
}event is typed to Stripe's event union — event.type narrows event.data.object automatically.
3. Mount it in Express
The only thing you must get right yourself: the route needs the raw request body, not JSON-parsed. hooksentinel verifies the signature against the raw bytes.
import express from 'express';
import { toExpressHandler } from '@hooksentinel/core/express';
import { stripeWebhook } from './webhooks/stripe';
const app = express();
// raw body parser scoped to this route only — must run before express.json()
app.post(
'/webhooks/stripe',
express.raw({ type: 'application/json' }),
toExpressHandler(stripeWebhook),
);
// JSON parsing for everything else can go after, safely
app.use(express.json());
app.listen(3000);That's the whole integration. Compare it to what you'd write by hand in the landing page comparison — this is roughly 8 lines instead of 40, and it covers signature verification, timestamp tolerance, and deduplication that hand-rolled versions usually skip.
What just happened
stripe({ secret })configures the Stripe signature scheme (HMAC-SHA256 overtimestamp.body, read from theStripe-Signatureheader).memoryStore()deduplicates by event ID using an in-processMap. Fine for local dev and single-instance deployments; swap forredisStore()orprismaStore()in production — see Idempotency.toExpressHandleradapts the framework-agnostic handler to an Express(req, res)signature, readingreq.bodyas the rawBufferthatexpress.raw()produced.- If verification fails, hooksentinel responds with the correct status code and never calls
onEvent. See Errors for the full list of failure modes and how to handle each one.
Next steps
- Supported providers — swap
stripe()forgithub(),shopify(), or any of the other 9 built-in adapters. - Frameworks — guides for Fastify, NestJS, Next.js, Hono, and Lambda.
- Idempotency — production-grade deduplication with Redis or Prisma.
Last updated on