I am trying to integrate Stripe Payment Gateway’s Webhook in my Strapi setup. Webhook is working and being called remotely. But Stripe requires the raw body content to construct the event which fails saying that:
Hello, I was trying to integrate with Stripe as well and I was running into the same problem. It seems like nobody answered it and the docs are not detailing this, but you can configure your middlewares.
If your ./config/middlewares.js looks reads the following:
@adrinr’s answer is right. Just wanted to add, that when retrieving the raw body, you don’t necessarily have to import the symbol from koa dependency. This will do as well:
const raw = ctx.request.body[Symbol.for("unparsedBody")];
I am trying to do this in Strapi version 4.12.1, in a custom controller in my plugin, but ctx.request.body[Symbol.for("unparsedBody")]; returns undefined. Here is my function so far:
Having the same issue… I can read the “non-rawbody” but can’t read the raw body…
console.log(“Received Stripe webhook payload:”, unparsedBody); → UNDEFINED
console.log(“Received webhook payload:”, ctx.request.body); → THE STRIPE WEBHOOK CONTENT
OK, had ‘strapi::body’ twice, so above fixed. But I still have error:
Webhook Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? If a webhook request is being forwarded by a third-party tool, ensure that the exact request body, including JSON formatting and new line style, is preserved. Learn more about webhook signing and explore webhook integration examples for various frameworks at GitHub - stripe/stripe-node: Node.js library for the Stripe API.
My code is
import Stripe from 'stripe'
import unparsed from "koa-body/unparsed.js"
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {apiVersion: '2023-10-16'})
export default {
receive: async (ctx, next) => {
let event
const signature = ctx.request.headers['stripe-signature']
console.log(process.env.STRIPE_WEBHOOK_SECRET)
try {
event = stripe.webhooks.constructEvent(
ctx.request.body[unparsed],
signature,
process.env.STRIPE_WEBHOOK_SECRET
)
} catch (err) {
console.error('Webhook Error1', err.message)
try {
event = stripe.webhooks.constructEvent(
ctx.request.body[Symbol.for('unparsedBody')],
signature,
process.env.STRIPE_WEBHOOK_SECRET
)
} catch (err) {
console.error('Webhook Error2', err.message)
ctx.response.status = 400
ctx.body = `Webhook Error: ${err.message}`
return
}
}
console.log('Received event:', event)
// Handle the event
switch (event.type) {
case 'charge.succeeded':
const chargeSucceeded = event.data.object;
console.log('chargeSucceeded', chargeSucceeded)
// Then define and call a function to handle the event charge.succeeded
break;
// ... handle other event types
default:
console.log(`Unhandled event type ${event.type}`);
}
ctx.response.status = 200
}
}
Actually I was fussing around with the config/middleware.js and yes the guys above have the right solution. You have to place the following instead of strapi::body
‘strapi::cors’,
‘strapi::poweredBy’,
‘strapi::logger’,
‘strapi::query’,
// ‘strapi::body’,
// these lines are telling Strapi how to streamline Stripe body.
{
name: ‘strapi::body’,
config: {
includeUnparsed: true,
patchKoa: true,
multipart: true,
},
},