node.jsstripe-paymentswebhooks

Stripe webhook -Webhook Error: Webhook payload must be provided as a string or a Buffer


I am trying to console a message when payment is success using stripe for that i am using webhooks.

const stripe = require('stripe')('sk_test_'); // Replace with your actual key

const handleWebhook = async (req, res) => {
    let event;
    
    try {
        const sig = req.headers['stripe-signature'];
        // Use req.body directly since it's raw
        event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_code'); 
    } catch (err) {
        console.error(`Webhook Error: ${err.message}`);
        return res.status(400).send(`Webhook Error: ${err.message}`);
    }

    console.log(event.type);
    switch (event.type) {
        case 'checkout.session.completed':
            const session = event.data.object;
            console.log('Checkout session completed:', session.id);
            if (session.payment_status === 'paid') { alert("hii")
                console.log('PaymentIntent was successful!');
                const updatedValues = JSON.parse(session.metadata.updatedValues);
                console.log("updatedValues is", updatedValues);
            }
            break;

And session.payment_status === 'paid' i need to test whether webhook is working or not.so i just alert a message.but i am getting

Webhook Error: Webhook payload must be provided as a string or a Buffer (https://nodejs.org/api/buffer.html) instance representing the raw request body.Payload was provided as a parsed JavaScript object instead. Signature verification is impossible without access to the original signed material.

my app.js

gsrApp.use((req, res, next) => {
  console.log(`Incoming request size: ${JSON.stringify(req.body).length} bytes`);
  next();
});

and router i have

routerV2.post('/webhook', express.raw({ type: 'application/json' }), handleWebhook);

Thanks in advance


Solution

  • It's because of app.use(express.json()); code.

    So the simplest solution would be moving the webhook route before app.use(express.json());

    If you can't do that then try this

    app.js

    app.use(
        express.json({
            verify(req, res, buf, encoding) {
                if (req.path.includes('webhook')){
                    req.rawBody = buf.toString(); // sets raw string in req.rawBody variable
                }
            }
        })
    );
    

    webhook handler

    const handleWebhook = async (req, res) => {
    let event;
    
    try {
        const sig = req.headers['stripe-signature'];
        // USE req.rawBody which is created specifically for this route
        event = stripe.webhooks.constructEvent(req.rawBody, sig, 'whsec_code');  
    } catch (err) {
        console.error(`Webhook Error: ${err.message}`);
        return res.status(400).send(`Webhook Error: ${err.message}`);
    }
    
    console.log(event.type);
    switch (event.type) {
        case 'checkout.session.completed':
            const session = event.data.object;
            console.log('Checkout session completed:', session.id);
            if (session.payment_status === 'paid') { alert("hii")
                console.log('PaymentIntent was successful!');
                const updatedValues = JSON.parse(session.metadata.updatedValues);
                console.log("updatedValues is", updatedValues);
            }
            break;
    

    Finally you don't need express.raw({ type: 'application/json' }), since we are doing that

    router

    routerV2.post('/webhook', handleWebhook);