Why Webhooks?

Stripe performs actions asynchronously. Just because a charge was created doesn’t mean it succeeded. Webhooks help your server respond to those lifecycle events reliably.

If you’re relying on Stripe for anything critical subscriptions, one-time payments, or dispute notifications—webhooks are a must.

Set Up a Stripe Account and Secret Key

Before anything else, you’ll need a Stripe account and a secret key from your Stripe dashboard. You’ll also generate a webhook secret once you set up the endpoint.

Install Stripe SDK

In your NestJS project, install the Stripe SDK:

npm install stripe

npm install --save-dev @types/stripe

Create a Webhook Controller

Create a dedicated controller to handle incoming Stripe events. Here’s a simplified version:

import { Controller, Post, Headers, Req, Res, HttpCode, HttpStatus } from ’@nestjs/common’;
 import { Request, Response } from ’express’;
 import Stripe from ’stripe’;
const stripe = new Stripe(process.env.STRIPESECRETKEY, { apiVersion: ’2023-10-16’ });
@Controller(’stripe’)
 export class StripeController {
  @Post(’webhook’)
  @HttpCode(HttpStatus.OK)
  async handleWebhook(
    @Req() request: Request,
    @Res() response: Response,
    @Headers(’stripe-signature’) signature: string,
  ) {
    const rawBody = request[’rawBody’];
    let event: Stripe.Event;
   try {
      event = stripe.webhooks.constructEvent(
        rawBody,
        signature,
        process.env.STRIPEWEBHOOKSECRET,
      );
    } catch (err) {
      console.error(’Webhook signature verification failed.’, err.message);
      return response.status(400).send(`Webhook Error: ${err.message}`);
    }
   switch (event.type) {
      case ’payment_intent.succeeded’:
        const paymentIntent = event.data.object as Stripe.PaymentIntent;
        console.log(’PaymentIntent was successful!’, paymentIntent);
        break;
      case ’invoice.paid’:
        const invoice = event.data.object as Stripe.Invoice;
        console.log(’Invoice paid!’, invoice);
        break;
      default:
        console.log(`Unhandled event type ${event.type}`);
    }
   response.send({ received: true });
  }
 }

Handle Raw Body Parsing

This step is crucial. Stripe requires the raw request body to verify signatures. But NestJS (and express.json() middleware) parses the body automatically—breaking signature validation.

Fix it by adding a custom body parser middleware:

import * as express from ’express’;
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(’/stripe/webhook’, express.raw({ type: ’application/json’ }));
  await app.listen(3000);
 }

Add Environment Variables

In your `.env` file:

STRIPESECRETKEY=sktest...
STRIPEWEBHOOKSECRET=whsec_...

Make sure you load these using `ConfigModule` or any other config strategy you use.

Test with Stripe CLI

To simulate webhooks in development, install the Stripe CLI and forward events to your local server:

stripe listen --forward-to localhost:3000/stripe/webhook

You’ll see events in real time and can test against them without deploying.

Secure the Route (Optional)

Avoid global guards or auth middleware on the webhook route. Stripe should be able to hit it directly without authentication. But always double-check that:

- It’s only accessible via HTTPS in production

- Signature verification is solid

Conclusion

Integrating Stripe webhooks in NestJS isn’t overly complex, but getting the raw body and verifying the signature correctly is what trips up most developers (including me the first time). Once that’s handled, it’s just like building any other controller.

Now my NestJS app can respond to real-time Stripe events reliably whether it’s confirming a successful payment or reacting to a failed subscription.

Let me know if you hit any snags I’m happy to help troubleshoot!

Our Trusted
Partner.

Unlock Valuable Cloud and Technology Credits

Imagine reducing your operational costs by up to $100,000 annually without compromising on the technology you rely on. Through our partnerships with leading cloud and technology providers like AWS (Amazon Web Services), Google Cloud Platform (GCP), Microsoft Azure, and Nvidia Inception, we can help you secure up to $25,000 in credits over two years (subject to approval).

These credits can cover essential server fees and offer additional perks, such as:

  • Google Workspace accounts
  • Microsoft accounts
  • Stripe processing fee waivers up to $25,000
  • And many other valuable benefits

Why Choose Our Partnership?

By leveraging these credits, you can significantly optimize your operational expenses. Whether you're a startup or a growing business, the savings from these partnerships ranging from $5,000 to $100,000 annually can make a huge difference in scaling your business efficiently.

The approval process requires company registration and meeting specific requirements, but we provide full support to guide you through every step. Start saving on your cloud infrastructure today and unlock the full potential of your business.

exclusive-partnersexclusive-partners

Let's TALK

Let's TALK and bring your ideas to life! Our experienced team is dedicated to helping your business grow and thrive. Reach out today for personalized support or request your free quote to kickstart your journey to success.

DIGITAL PRODUCTUI/UX DESIGNDIGITAL STUDIOBRANDING DESIGNUI/UX DESIGNEMAIL MARKETINGBRANDING DESIGNUI/UX DESIGNEMAIL MARKETING
DIGITAL PRODUCTUI/UX DESIGNDIGITAL STUDIOBRANDING DESIGNUI/UX DESIGNEMAIL MARKETINGBRANDING DESIGNUI/UX DESIGNEMAIL MARKETING