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
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: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 charactero
, 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:Alternatively, as suggested from the Stripe documentation, you can dynamically load the middleware as well:
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
withapp.use(bodyParser.json());
) is that Stripe’s signature verification depends on theraw body string
in the calculation. Parsing the JSON body causes that raw string to be unavailable and the signature comparison to fail to match.