DEV Community

Apollo
Apollo

Posted on

How I built a Stripe Webhook in Node.js (Full Guide)

How I Built a Stripe Webhook in Node.js (Full Guide)

Webhooks are essential for modern SaaS applications, enabling real-time communication between services. In this guide, I'll walk you through building a Stripe Webhook in Node.js, ensuring secure, scalable, and robust processing of Stripe events. By the end, you’ll have a fully functional webhook implementation ready for production.


Why Webhooks?

Webhooks allow Stripe to notify your application about events like payment success, subscription cancellations, or charge disputes. Instead of polling Stripe’s API, webhooks push data to your server, making your app more efficient and responsive.


Prerequisites

Before diving in, ensure you have:

  1. Node.js installed (v16+ recommended).
  2. A Stripe account with API keys (test mode is sufficient for development).
  3. An HTTP server framework like Express.
  4. A tool like ngrok to expose your local server for testing.

Step 1: Set Up Your Node.js Project

Start by initializing a Node.js project:

mkdir stripe-webhook
cd stripe-webhook
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install the required dependencies:

npm install express stripe body-parser
Enter fullscreen mode Exit fullscreen mode

Step 2: Create a Basic Express Server

Create an index.js file and set up a basic Express server:

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

app.use(bodyParser.json());

app.get('/', (req, res) => {
  res.send('Stripe Webhook Server');
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Run the server:

node index.js
Enter fullscreen mode Exit fullscreen mode

Step 3: Set Up Stripe Webhook Endpoint

Next, create a /webhook endpoint to handle incoming Stripe events:

const stripe = require('stripe')('your_stripe_secret_key');
const endpointSecret = 'your_webhook_signing_secret';

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.error(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  console.log('Received event:', event.type);

  // Handle the event
  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object;
      console.log('PaymentIntent was successful:', paymentIntent.id);
      break;
    case 'charge.failed':
      const charge = event.data.object;
      console.log('Charge failed:', charge.id);
      break;
    default:
      console.log(`Unhandled event type: ${event.type}`);
  }

  res.json({ received: true });
});
Enter fullscreen mode Exit fullscreen mode

Step 4: Secure Your Webhook

Stripe uses signatures to verify the authenticity of webhook events. The stripe.webhooks.constructEvent function validates the payload using the Stripe-Signature header and your webhook signing secret.

Never expose your Stripe secret key or webhook signing secret in client-side code.


Step 5: Test Your Webhook Locally

Use ngrok to expose your local server:

ngrok http 3000
Enter fullscreen mode Exit fullscreen mode

Update your Stripe webhook settings in the Stripe Dashboard with the ngrok URL (e.g., https://your-ngrok-url.ngrok.io/webhook).

Trigger a test event in Stripe’s Dashboard to verify it works.


Step 6: Handle Common Webhook Scenarios

Here are some common scenarios you might handle:

1. Payment Succeeded

case 'payment_intent.succeeded':
  const paymentIntent = event.data.object;
  console.log('Payment succeeded:', paymentIntent.id);
  break;
Enter fullscreen mode Exit fullscreen mode

2. Subscription Created

case 'customer.subscription.created':
  const subscription = event.data.object;
  console.log('Subscription created:', subscription.id);
  break;
Enter fullscreen mode Exit fullscreen mode

3. Charge Failed

case 'charge.failed':
  const charge = event.data.object;
  console.log('Charge failed:', charge.id);
  break;
Enter fullscreen mode Exit fullscreen mode

Step 7: Deploy Your Webhook

Once tested, deploy your webhook to a production server (e.g., AWS, Heroku, or Vercel). Update your Stripe webhook settings with the production URL.


Best Practices

  1. Idempotency: Ensure your webhook handlers are idempotent to handle duplicate events gracefully.
  2. Logging: Log all incoming events for debugging and auditing.
  3. Rate Limiting: Protect your endpoint from abuse by implementing rate limiting.
  4. Retry Logic: Handle failed events by retrying with exponential backoff.

Conclusion

Building a Stripe webhook in Node.js is straightforward with the stripe-node SDK and Express. By following this guide, you’ve created a secure and scalable webhook endpoint ready to handle Stripe events in real time.

Experiment with different event types and integrate them into your application’s workflow. Happy coding! 🚀


Further Reading:


🚀 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)