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).4---56# Webhook Handler Generator78## Prerequisites & Dependencies9- Node.js 18+ with npm or pnpm10- Mandatory packages: `npm i express webhook-receiver` or `npm i stripe`, `npm i @octokit/webhooks` for GitHub11- Access to webhook signing secrets: Stripe `webhook secret`, GitHub `webhook secret`, Midtrans `server key hash`12- Optional: `npm i crypto` (built-in Node.js, no install needed), `npm i helm` for deployment testing13- A secure endpoint URL (HTTPS recommended) and a tunneling tool for local development: `ngrok` or `cloudflared`1415## Execution Steps161. Identify the webhook source: Stripe, GitHub, Midtrans, or custom; each has a different signing mechanism172. 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`)183. Set up the receiver endpoint using Express (or your preferred framework): `POST /webhook/:source`194. Implement signature verification:20 - **Stripe**: `stripe.webhooks.constructEvent(rawBody, signature, webhookSecret)` – verifies `Temporal-Signature` header21 - **GitHub**: `@octokit/webhooks` – validates `X-Hub-Signature-256` using the secret22 - **Midtrans**: hash the `notification_id` and `status_code` with SHA-512 using the server key235. Parse the verified event payload: extract the relevant data (payment intent, issue number, order ID)246. Process the business logic: update database, trigger email/SMS, fulfill order, return `200 OK` promptly (within 10s)257. Idempotency: store received event IDs and skip reprocessing if already handled; return `200 OK` to acknowledge268. Error handling: return `400` for verification failures, `500` for processing errors; avoid infinite retries2728```javascript29// Example: Secure Stripe webhook handler with Express30const express = require('express');31const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);32const endpointRouter = express.Router();3334endpointRouter.post(35 '/stripe',36 express.raw({ type: 'application/json' }),37 (request, response) => {38 const sig = request.headers['stripe-signature'];39 let event;4041 try {42 event = stripe.webhooks.constructEvent(43 request.body,44 sig,45 process.env.STRIPE_WEBHOOK_SECRET46 );47 } catch (err) {48 console.warn(`⚠️ Webhook signature verification failed: ${err.message}`);49 return response.status(400).send(`Webhook Error: ${err.message}`);50 }5152 // Handle the event53 switch (event.type) {54 case 'payment_intent.succeeded':55 const paymentIntent = event.data.object;56 // TODO: fulfill order, update DB, send confirmation email57 console.log(`💳 PaymentIntent ${paymentIntent.id} succeeded`);58 break;59 case 'payment_intent.payment_failed':60 console.log(`❌ PaymentIntent ${event.data.object.id} failed`);61 break;62 default:63 console.log(`✅ Unhandled event type: ${event.type}`);64 }6566 // Respond 200 OK to acknowledge receipt (idempotent)67 response.json({ received: true });68 }69);7071module.exports = endpointRouter;72```7374```bash75# Local development with ngrok76ngrok http 4000 # expose local port 4000 to https://<sub>.ngrok.io7778# Set webhook URL in Stripe dashboard79# https://<sub>.ngrok.io/webhook/stripe8081# Verify locally (optional)82stripe listen --forward-to http://localhost:4000/webhook/stripe83```8485```bash86npm i express stripe87```