How I Built a Stripe Webhook in Node.js (Full Guide)
Building a Stripe webhook in Node.js is essential for handling asynchronous events from Stripe, such as payment success, subscription cancellations, or failed charges. In this guide, I’ll walk you through the process step-by-step, ensuring you understand the technical nuances of implementing a secure and efficient webhook.
Prerequisites
Before diving into the code, ensure you have the following:
- Node.js installed (v16 or later recommended).
- A Stripe account (sign up at stripe.com).
- Basic knowledge of Express.js and JavaScript.
Install the required dependencies:
npm install express stripe body-parser dotenv crypto
Step 1: Set Up Your Stripe Webhook Endpoint
First, configure your Stripe account to point to your webhook endpoint:
- Go to the Stripe Dashboard.
- Navigate to Developers > Webhooks.
- Add a new endpoint (e.g.,
https://yourdomain.com/webhook). - Select the events you want to listen to (e.g.,
payment_intent.succeeded,invoice.payment_failed).
Stripe will generate a signing secret (whsec_...), which you’ll use to verify incoming webhook requests.
Step 2: Create the Express Server
Start by setting up an Express server in Node.js:
const express = require('express');
const bodyParser = require('body-parser');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const crypto = require('crypto');
require('dotenv').config();
const app = express();
// Middleware to parse JSON bodies
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
}
}));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
This server listens for incoming requests and parses the raw body for Stripe’s webhook signature verification.
Step 3: Implement Webhook Verification
Stripe sends a Stripe-Signature header with each webhook request. Use this header to verify the request’s authenticity:
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
} catch (err) {
console.error(`Webhook Error: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
console.log('Payment succeeded!');
break;
case 'invoice.payment_failed':
console.log('Payment failed!');
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
});
The stripe.webhooks.constructEvent method ensures the payload is genuine and originated from Stripe.
Step 4: Handle Specific Events
Stripe sends various event types based on your subscription plan and configuration. Here’s how to handle two common events:
Payment Success (payment_intent.succeeded)
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`PaymentIntent ID: ${paymentIntent.id}`);
console.log(`Amount: ${paymentIntent.amount}`);
break;
Payment Failure (invoice.payment_failed)
case 'invoice.payment_failed':
const invoice = event.data.object;
console.log(`Invoice ID: ${invoice.id}`);
console.log(`Attempt: ${invoice.attempt_count}`);
break;
You can extend this switch statement to handle more events.
Step 5: Test Your Webhook
Use Stripe’s CLI or Dashboard to test your webhook:
- Install the Stripe CLI:
brew install stripe/stripe-cli/stripe
- Start listening for events:
stripe listen --forward-to localhost:3000/webhook
- Trigger a test event:
stripe trigger payment_intent.succeeded
You should see the event logged in your server console.
Step 6: Deploy Your Webhook
Once tested locally, deploy your Node.js app to a production environment (e.g., Heroku, AWS, or Vercel). Update your Stripe webhook endpoint to point to your live URL.
Step 7: Securely Store Secrets
Never hardcode sensitive data like your Stripe secret key or webhook signing secret. Use environment variables:
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
Load these variables using dotenv:
require('dotenv').config();
Step 8: Monitor and Scale
Monitor your webhook endpoint using tools like Sentry or New Relic. Handle errors gracefully and scale your infrastructure to accommodate higher traffic.
Conclusion
Building a Stripe webhook in Node.js is straightforward once you understand the core concepts of endpoint verification and event handling. By following this guide, you’ve created a secure and scalable solution to integrate Stripe into your application.
Full Code Example
const express = require('express');
const bodyParser = require('body-parser');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const crypto = require('crypto');
require('dotenv').config();
const app = express();
app.use(bodyParser.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
}
}));
const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET;
app.post('/webhook', (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret);
} catch (err) {
console.error(`Webhook Error: ${err.message}`);
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'payment_intent.succeeded':
console.log('Payment succeeded!');
break;
case 'invoice.payment_failed':
console.log('Payment failed!');
break;
default:
console.log(`Unhandled event type: ${event.type}`);
}
res.json({ received: true });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Now you’re ready to handle Stripe events like a pro! 🚀
🚀 Stop Writing Boilerplate Prompts
If you want to skip the setup and code 10x faster with complete AI architecture patterns, grab my Senior React Developer AI Cookbook ($19). It includes Server Action prompt libraries, UI component generation loops, and hydration debugging strategies.
Browse all 10+ developer products at the Apollo AI Store | Or snipe Solana tokens free via @ApolloSniper_Bot.
Top comments (0)