Stripe + Angular + Firebase: How to Fix Webhook Signature Errors

Stripe
Frontend
Angular
Firebase
Express.js
Vitalii Rybka photo
By Vitalii Rybka
2025-02-16T17:40:22.829Z

Intro

One of our long-term clients recently approached us with a new request. After integrating Stripe’s JavaScript library, they started encountering cryptic error messages. The surprising part? Stripe had been integrated into the project two to three years ago, and it was working fine in the development environment but failing in production.

The project had been put on hold multiple times, leaving the issue unresolved - until now.

Problem

The error message we encountered was:

StripeSignatureVerificationError: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? Stripe Node.js Webhook Signing Docs

This error indicated an issue with Stripe webhook signature verification, likely caused by how the request body was being processed before reaching Stripe’s validation mechanism.

Tech Stack

The project was built using the following technologies:

Diving into the Investigation

Our first step was to analyze Firebase logs and Stripe dashboard logs while inspecting the responses from the Stripe API. However, the error message was generic and didn’t provide enough context.

While searching through Stripe’s developer documentation, we found the following warning:

Stripe requires the raw body of the request to perform signature verification. If you’re using a framework, make sure it doesn’t manipulate the raw body. Any manipulation to the raw body of the request causes the verification to fail.

This seemed like a probable cause, but we needed confirmation.

Challenges We Faced

  • The project had been maintained by multiple development teams over three years, making it difficult to track changes.
  • The application’s backend ran on Firebase Functions with Express.js middleware, which might be altering the request body.
  • Local debugging was impossible due to dependencies on the production environment.
  • Testing Stripe webhooks required deploying every change to production, causing delays.

Approach to Fixing the Issue

To streamline debugging and avoid production delays, we rebuilt a minimal version of the backend focused on payment processing. The only things that we needed to reimplement were Express.js, middlewares, and Firebase Functions, which were in charge of payment handling. This step helped us to deal with two issues:

  • Couldn’t run the application on the local machine
  • Debug possible root causes

For the next issue (connecting to Stripe webhooks), we decided to use Stripe CLI and stripe-node (Node.js library for the Stripe API). Fortunately, Stripe CLI provides tools to connect to the existing environment, listen to it, and forward responses somewhere else (in our case, it was a local environment with a pre-configured API set - the same as in the client environment).

During debugging, one of our developers suggested that some middleware might be modifying the request body before it reached Stripe’s signature validation.

Here’s the original Express.js middleware stack:


TS

app.use(cors({ origin: true }));
app.use(helmet());
app.use(express.json()); // it's the main root cause
app.use(express.urlencoded({ extended: true }));
app.use(compression());

We decided to start our testing environment without middlewares at all and then add one by one. On each stage we did a testing payment and see if the error is popping up.

We use one of the examples from the Stripe dev team and changed it for our needs. Specifically speaking, we added middlewares and payment events handling.

On the first run we didn’t face any issues, the Stripe API returns success and everything went as expected.

We still didn’t face any issues until we added express.json() middleware. That was the main root cause. To make a long story short, this middleware parses incoming requests with JSON payloads and is based on body-parser. A new body object containing the parsed data is populated on the request object after the middleware (i.e. req.body), or an empty object ({}) if there was no body to parse, the Content-Type was not matched, or an error occurred.

Final Fix

To prevent body parsing for Stripe webhook endpoints, we modified the middleware setup:


TS

app.use(cors({ origin: true }));
app.use(helmet());
app.use((req, res, next) => {
  if (req.url.startsWith("/api/user/payment-success")) {
    next();
  }

  express.json()(req, res, next);
});
app.use(express.urlencoded({ extended: true }));
app.use(compression());

Why this Works

  • Requests to /api/user/payment-success bypass express.json(), ensuring the raw body reaches Stripe.
  • Other routes still use express.json() for standard JSON parsing.

After testing, we deployed the fix. The client’s response?

🎉 it works. So cool. What exactly have you done? What was the issue?

Key Takeaways

  • Use Stripe CLI to test webhooks locally before deploying.
  • Recreate a minimal version of the backend for debugging.
  • Check middleware modifications - avoid using express.json() on webhook endpoints.
  • Review Stripe dev docs and API logs for debugging clues.

Final Thoughts

This issue highlights how a single middleware can break critical functionality. Understanding how frameworks handle request bodies is crucial when integrating third-party APIs like Stripe.

By following structured debugging steps, leveraging developer tools like Stripe CLI, and simplifying our approach, we quickly identified and fixed the problem.

👋 Let's Build a Bold Experience Together
Book a Demo
Contact the Varbintech team, and you will have the answer in less than 24 hour

contact@varbintech.com

Take the next step towards achieving your goals by leveraging our top-notch experience with a proven record