skip to Main Content

This seems to be a issue that has cropped up numerous times, but none of the solutions I’ve found have worked. When the data comes in from stripe, I get the ‘unexpected token o’ error, so I try to stringify and parse it, but I then get a different error: UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON. The console output is what is expected, but making it usable – even when following the exact code Stripe gives you as an example – fails. Below is my current code. Any idea how to fix this?

app.post('/webhook', express.raw({type: 'application/json'}), async (request, response) => {
    
  console.log(request.body);

  let event;

  try {
    event = await stripe.webhooks.constructEvent(
      request.body,
      request.headers['stripe-signature'],
      endpointSecret
    );
    
    const customerSubscriptionUpdated = event.data.object;
    
  } catch (err) {
    response.status(400).send(`Stupid Webhook Error: ${err.message}`);
    return;
  }

  // Return a 200 response to acknowledge receipt of the event
  response.send();
});

2

Answers


  1. The reason is very likely caused by having a JSON parser middleware in your app before your /webhook route is defined, something like this is in your code:

    app.use(express.json()); // or app.use(bodyParser.json()) in your case
    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {});
    

    This caused the JSON data (identified with Content-Type: application/json header) to be parsed first before going into your route. However, stripe API specifically mention that this data has to be raw Buffer, thus it is invalid for the API.

    The reason why I suggest so is because JSON.parse({}) will resulting in the JavaScript trying to first cast the argument into a string (In this case [object Object]), JSON can understand the first character [ (Opening bracket of an array) but starts to complain in the second character o, which results in the error: Uncaught SyntaxError: Unexpected token o in JSON at position 1

    The solution is to move your middleware below your .post('/webhook'), which will cause it to only run for all routes declared after that:

    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {});
    app.use(express.json()); // or app.use(bodyParser.json()) in your case
    

    Alternatively, as suggested from the Stripe documentation, you can dynamically load the middleware as well:

    // Use JSON parser for all non-webhook routes
    app.use((req, res, next) => {
      if (req.originalUrl === '/webhook') {
        next();
      } else {
        express.json()(req, res, next); // or bodyParser.json() in your case
      }
    });
    app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {});
    
    Login or Signup to reply.
  2. Further to @AngYC’s answer above, the reason you must not parse request body json prior to signature verification (as applied when you add body-parser to your app with app.use(bodyParser.json());) is that Stripe’s signature verification depends on the raw body string in the calculation. Parsing the JSON body causes that raw string to be unavailable and the signature comparison to fail to match.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search