3

I'm trying to test locally webhook stripe event, but it say :

Webhook signature verification failed.

So this is my webhook endpoint :

exports.stripeListenWebhook = (req, res) => {
    let data
    let eventType
    const webhookSecret = 'whsec_VjORamXpVZs1j6mCV0eTyE7B2GI92'

    if (webhookSecret) {
        // Retrieve the event by verifying the signature using the raw body and secret.
        let event
        let signature = req.headers['stripe-signature']

        console.log(req.headers)

        try {
            event = stripe.webhooks.constructEvent(req.body, signature, webhookSecret)
        } catch (err) {
            console.log(`⚠️  Webhook signature verification failed.`)
            return res.sendStatus(400)
        }
        // Extract the object from the event.
        data = event.data
        eventType = event.type
    } else {
        // Webhook signing is recommended, but if the secret is not configured in `config.js`,
        // retrieve the event data directly from the request body.
        data = req.body.data
        eventType = req.body.type
    }

    // Handle the event
    switch (eventType) {
        case 'checkout.session.completed':
            const session = data.object
            console.log(session)
            // Then define and call a function to handle the event checkout.session.completed
            break
        default:
            console.log(`Unhandled event type ${eventType}`)
    }

    res.json({ received: true })
}

My console.log(req.headers) output after execute this command :

stripe trigger checkout.session.completed

{
  host: 'localhost:8000',
  'user-agent': 'Stripe/1.0 (+https://stripe.com/docs/webhooks)',
  'content-length': '1923',
  accept: '*/*; q=0.5, application/xml',
  'cache-control': 'no-cache',
  'content-type': 'application/json; charset=utf-8',
  'stripe-signature': 't=1638211722,v1=08ed8a55af610fdb97d928c4ec068d19badfb82fe0a521aee7d8f8cfbe378d63,v0=aeebf964e3da2a19f9a533743d420804c168395bb25bf4789e04cfcd9f573d52',
  'accept-encoding': 'gzip'
}

I follow the rules in the doc, and I'm using this configuration in my app.js :

const corsOptions = {
    origin: [URLConfig.URL_CLIENT],
    credentials: true,
    methods: 'POST, PUT, OPTIONS, DELETE, GET, PATCH',
    allowedHeaders: 'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization',
}
app.use(cors(corsOptions))
app.use(cookieParser())
app.use(express.json())
app.use(
    express.urlencoded({
        extended: true,
    })
)

Anyone have a issue for my problem ? I see that the body-parser is depracated so I used the express.json() instead

mehdibe
  • 177
  • 2
  • 12
  • As a diagnostic step, can you try using body-parser and configure it to retrieve the raw request body[1]? Stripe's signature verification requires you pass it the raw request body but many libraries modify the body by default which is a common cause of this error. If your code works with body-parser and the raw request body, I would check how to get raw request body with express.json(). [1] https://stackoverflow.com/questions/9920208/expressjs-raw-body – Pompey Nov 29 '21 at 19:52
  • 1
    You right ! I used bodyParser in my endpoint like that : router.post('/', bodyParser.raw({ type: 'application/json' }), WebHooksController.stripeListenWebhook) and it's work. Now I need to find a solution with express.json() to don't broke the others applications endpoint. Any solution ? Thank's man ! – mehdibe Nov 29 '21 at 20:19

3 Answers3

10

I replace it with express.raw({ type: 'application/json' }), basically It's don't work if express.json() is placed before it like that :

app.use(express.json())
app.use(
    express.urlencoded({
        extended: true,
    })
)
app.use(URLConfig.URL_API + '/webhooks-stripe', express.raw({ type: 'application/json' }), WebHooksRoutes) / this need to be placed before

So the solution is :

app.use(URLConfig.URL_API + '/webhooks-stripe', express.raw({ type: 'application/json' }), WebHooksRoutes)
app.use(express.json())
app.use(
    express.urlencoded({
        extended: true,
    })
)
mehdibe
  • 177
  • 2
  • 12
1

Change to this for a more general solution:

const rawBodyBuffer = (
    req: http.IncomingMessage,
    res: http.ServerResponse,
    buffer: Buffer,
    encoding: BufferEncoding,
) => {
    if (!req.headers['stripe-signature']) {
        return;
    }

    if (buffer && buffer.length) {
        req['rawBody'] = buffer.toString(encoding || 'utf8');
    }
};

app.use(urlencoded({ verify: rawBodyBuffer, extended: true }));
Xen_mar
  • 8,330
  • 11
  • 51
  • 74
1

Here is a example

https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/node-express/express.js

Please make sure.

  • You use correct webhook screte

  • Webhook endpoint is not passed through bodyParser.

  • Don't miss express middleware to

    express.raw({type: 'application/json'})

F.E
  • 688
  • 6
  • 10