skip to Main Content

I’m stuck on this, could someone help me?
I put it in postman: https://my-server/api/webhook

and i receive this:

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.

Learn more about webhook signing and explore webhook integration
examples for various frameworks at
https://github.com/stripe/stripe-node#webhook-signing

i’m using ReactJS and Node

index.js

const express = require('express');
const Stripe = require('stripe');
const bodyParser = require('body-parser');
const cors = require('cors');
const webhookRoutes = require('./routes/webhook');

const app = express();
const stripe = Stripe(
  'sk_test',
);

app.use(cors());
app.use(express.json());
app.use('/webhook', express.raw({ type: 'application/json' }));
app.use('/api', webhookRoutes);

app.post('/create-checkout-session', async (req, res) => {
  try {
    console.log('Received request to create checkout session');

    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: {
              name: 'Nome do Produto',
            },
            unit_amount: 2000,
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: 'http://localhost:5173/success',
      cancel_url: 'http://localhost:5173/cancel',
    });

    console.log('Checkout session created successfully:', session);
    res.json({ id: session.id });
  } catch (error) {
    console.error('Error creating checkout session:', error.message);
    res.status(500).json({
      error: 'Failed to create checkout session',
    });
  }
});

const PORT = process.env.PORT || 4242;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

webhook.js

const express = require('express');
const Stripe = require('stripe');
const router = express.Router();
const stripe = Stripe('sk_test');

router.post('/webhook', async (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_');
  } catch (err) {
    console.log(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;

    console.log('Pagamento confirmado:', session);
  }

  res.json({ received: true });
});

module.exports = router;

2

Answers


  1. The problem is…

    app.use(express.json());
    

    This parses all application/json request bodies into a JavaScript data structure (using JSON.parse()) whereas Stripe wants you to forward the raw request body.

    You should simply register your webhook route handler before express.json(). You’ll also need the raw() middleware set for those webhook routes as well.

    app.use(cors());
    
    // 1st - register your webhook routes
    app.use('/api', express.raw({ type: 'application/json' }), webhookRoutes);
    
    // 2nd - then register other routes and middleware
    app.use(express.json());
    app.post('/create-checkout-session', async (req, res) => {
      // ...
    });
    

    See also https://docs.stripe.com/identity/handle-verification-outcomes#create-webhook

    Login or Signup to reply.
  2. It’s because of app.use(express.json()); code.

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

    Or You can try this.

    index.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 express = require('express');
    const Stripe = require('stripe');
    const router = express.Router();
    const stripe = Stripe('sk_test');
    
    router.post('/webhook', async (req, res) => {
      const sig = req.headers['stripe-signature'];
    
      let event;
    
      try {
        // HERE YOU CAN USE req.rawBody
        event = stripe.webhooks.constructEvent(req.rawBody, sig, 'whsec_');
      } catch (err) {
        console.log(`Webhook Error: ${err.message}`);
        return res.status(400).send(`Webhook Error: ${err.message}`);
      }
    
      if (event.type === 'checkout.session.completed') {
        const session = event.data.object;
    
        console.log('Pagamento confirmado:', session);
      }
    
      res.json({ received: true });
    });
    
    module.exports = router;
    

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

    router

    app.use(cors());
    app.use(express.json());
    app.use('/webhook', webhookRoutes);
    

    Note: I’ve modified the api path for webhook

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search