Webhook Handler Generator
Prerequisites & Dependencies
- Node.js 18+ with npm or pnpm
- Mandatory packages:
npm i express webhook-receiver or npm i stripe, npm i @octokit/webhooks for GitHub
- Access to webhook signing secrets: Stripe
webhook secret, GitHub webhook secret, Midtrans server key hash
- Optional:
npm i crypto (built-in Node.js, no install needed), npm i helm for deployment testing
- A secure endpoint URL (HTTPS recommended) and a tunneling tool for local development:
ngrok or cloudflared
Execution Steps
- Identify the webhook source: Stripe, GitHub, Midtrans, or custom; each has a different signing mechanism
- Retrieve the webhook signing secret/key from your provider's dashboard and store it as an environment variable (
WEBHOOK_SECRET, STRIPE_WEBHOOK_SECRET, GITHUB_WEBHOOK_SECRET)
- Set up the receiver endpoint using Express (or your preferred framework):
POST /webhook/:source
- Implement signature verification:
- Stripe:
stripe.webhooks.constructEvent(rawBody, signature, webhookSecret) – verifies Temporal-Signature header
- GitHub:
@octokit/webhooks – validates X-Hub-Signature-256 using the secret
- Midtrans: hash the
notification_id and status_code with SHA-512 using the server key
- Parse the verified event payload: extract the relevant data (payment intent, issue number, order ID)
- Process the business logic: update database, trigger email/SMS, fulfill order, return
200 OK promptly (within 10s)
- Idempotency: store received event IDs and skip reprocessing if already handled; return
200 OK to acknowledge
- Error handling: return
400 for verification failures, 500 for processing errors; avoid infinite retries
// Example: Secure Stripe webhook handler with Express
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const endpointRouter = express.Router();
endpointRouter.post(
'/stripe',
express.raw({ type: 'application/json' }),
(request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(
request.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
console.warn(`⚠️ Webhook signature verification failed: ${err.message}`);
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// TODO: fulfill order, update DB, send confirmation email
console.log(`💳 PaymentIntent ${paymentIntent.id} succeeded`);
break;
case 'payment_intent.payment_failed':
console.log(`❌ PaymentIntent ${event.data.object.id} failed`);
break;
default:
console.log(`✅ Unhandled event type: ${event.type}`);
}
// Respond 200 OK to acknowledge receipt (idempotent)
response.json({ received: true });
}
);
module.exports = endpointRouter;
# Local development with ngrok
ngrok http 4000 # expose local port 4000 to https://<sub>.ngrok.io
# Set webhook URL in Stripe dashboard
# https://<sub>.ngrok.io/webhook/stripe
# Verify locally (optional)
stripe listen --forward-to http://localhost:4000/webhook/stripe
npm i express stripe
1---2name: webhook-handler-generator3description: Draft secure webhook receiver endpoints complete with cryptographic signature verification (Stripe, GitHub, Midtrans).4metadata:5 source: skills/webhook-handler-generator/webhook-handler-generator.md6---78# Webhook Handler Generator910## Prerequisites & Dependencies11- Node.js 18+ with npm or pnpm12- Mandatory packages: `npm i express webhook-receiver` or `npm i stripe`, `npm i @octokit/webhooks` for GitHub13- Access to webhook signing secrets: Stripe `webhook secret`, GitHub `webhook secret`, Midtrans `server key hash`14- Optional: `npm i crypto` (built-in Node.js, no install needed), `npm i helm` for deployment testing15- A secure endpoint URL (HTTPS recommended) and a tunneling tool for local development: `ngrok` or `cloudflared`1617## Execution Steps181. Identify the webhook source: Stripe, GitHub, Midtrans, or custom; each has a different signing mechanism192. Retrieve the webhook signing secret/key from your provider's dashboard and store it as an environment variable (`WEBHOOK_SECRET`, `STRIPE_WEBHOOK_SECRET`, `GITHUB_WEBHOOK_SECRET`)203. Set up the receiver endpoint using Express (or your preferred framework): `POST /webhook/:source`214. Implement signature verification:22 - **Stripe**: `stripe.webhooks.constructEvent(rawBody, signature, webhookSecret)` – verifies `Temporal-Signature` header23 - **GitHub**: `@octokit/webhooks` – validates `X-Hub-Signature-256` using the secret24 - **Midtrans**: hash the `notification_id` and `status_code` with SHA-512 using the server key255. Parse the verified event payload: extract the relevant data (payment intent, issue number, order ID)266. Process the business logic: update database, trigger email/SMS, fulfill order, return `200 OK` promptly (within 10s)277. Idempotency: store received event IDs and skip reprocessing if already handled; return `200 OK` to acknowledge288. Error handling: return `400` for verification failures, `500` for processing errors; avoid infinite retries2930```javascript31// Example: Secure Stripe webhook handler with Express32const express = require('express');33const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);34const endpointRouter = express.Router();3536endpointRouter.post(37 '/stripe',38 express.raw({ type: 'application/json' }),39 (request, response) => {40 const sig = request.headers['stripe-signature'];41 let event;4243 try {44 event = stripe.webhooks.constructEvent(45 request.body,46 sig,47 process.env.STRIPE_WEBHOOK_SECRET48 );49 } catch (err) {50 console.warn(`⚠️ Webhook signature verification failed: ${err.message}`);51 return response.status(400).send(`Webhook Error: ${err.message}`);52 }5354 // Handle the event55 switch (event.type) {56 case 'payment_intent.succeeded':57 const paymentIntent = event.data.object;58 // TODO: fulfill order, update DB, send confirmation email59 console.log(`💳 PaymentIntent ${paymentIntent.id} succeeded`);60 break;61 case 'payment_intent.payment_failed':62 console.log(`❌ PaymentIntent ${event.data.object.id} failed`);63 break;64 default:65 console.log(`✅ Unhandled event type: ${event.type}`);66 }6768 // Respond 200 OK to acknowledge receipt (idempotent)69 response.json({ received: true });70 }71);7273module.exports = endpointRouter;74```7576```bash77# Local development with ngrok78ngrok http 4000 # expose local port 4000 to https://<sub>.ngrok.io7980# Set webhook URL in Stripe dashboard81# https://<sub>.ngrok.io/webhook/stripe8283# Verify locally (optional)84stripe listen --forward-to http://localhost:4000/webhook/stripe85```8687```bash88npm i express stripe89```