Get raw request body in custom controller

System Information
  • Strapi Version:
  • Operating System:
  • Database:
  • Node Version:
  • NPM Version:
  • Yarn Version:

Hello Friends,

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:

‘No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? GitHub - stripe/stripe-node: Node.js library for the Stripe API.

I am sure you guys have gone through the issue if implemented the Stripe. Can help on this?
P.S. I am using version 4.0.1

Thanks.

2 Likes

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:

module.exports = [
        'strapi::errors',
        'strapi::security',
        'strapi::poweredBy',
        'strapi::logger',
        'strapi::query',
        { name: 'strapi::body', config: { includeUnparsed: true } },
        'strapi::session',
        'strapi::favicon',
        'strapi::public',
    ]

basically, replacing the default strapi::body for the one with the config set.

Then, from your endpoint you can run:

const unparsed = require('koa-body/unparsed.js');

module.exports = {
    async webhook(ctx) {
        const unparsedBody = ctx.prequest.body[unparsed];
        const signature = ctx.request.headers['stripe-signature'];
        let event;

        try {
            event = stripe.webhooks.constructEvent(
                unparsedBody,
                signature,
                endpointSecret
            );
        } catch (err) {
            return ctx.badRequest(`Webhook Error: ${err.message}`);
        }

        // Return a response to acknowledge receipt of the event
        return { received: true };

I hope it helps!

5 Likes

Thank you for this solution! I’ve looked for hours and this one worked for Strapi v4. Bumping for the search algorithm or any others that are lost.

Thanks you made my day a little easier!

Unfortunately, unparsedBody is undefined for me.

const unparsed = require('koa-body/unparsed.js');
const unparsedBody = ctx.request.body[unparsed];
// console.log
unparsed:  Symbol(unparsedBody)
unparsedBody:  undefined

Using strapi v4.

@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")];
3 Likes

Thanks, simple and works fine !

It worked! Thanks.

In Strapi V4 or in latest version ‘koa-body/unparsed.js’ is undefined and ‘koa-body/lib/unparsed’ does not work.

const unparsed = require('koa-body/unparsed.js');

The latest Working Code for Strapi v4.9.0:

// config/middleware.js
module.exports = [
  'strapi::errors',
  'strapi::security',
	'strapi::cors',
  'strapi::poweredBy',
  'strapi::logger',
  'strapi::query',
  {
    name: 'strapi::body',
    config: {
      patchKoa: true,
      multipart: true,
      includeUnparsed: true,
    },
  },
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

To get raw data:

const raw = ctx.request.body[Symbol.for("unparsedBody")];
1 Like

Thank you so much !

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:

'use strict';

const crypto = require("crypto");

module.exports = ({ strapi }) => ({
  async handle(ctx) {
    const secretKey = process.env.SHOPIFY_API_KEY;
    const hmac = ctx.request.get('x-shopify-hmac-sha256');
    const rawBody = ctx.request.body[Symbol.for("unparsedBody")];
    console.log("RAW BODY:", rawBody); // prints RAW BODY: undefined
}});

I have tried to google it, and searched the forum, but I can not find a way to get this to work. Can anyone help?

Oh no, I added the config to the wrong environment. This works perfectly. Sorry.

Here I needed to add a “.data”:
const raw = ctx.request.body.data[Symbol.for(“unparsedBody”)]