#orangesidny

1 messages · Page 1 of 1 (latest)

deft bridgeBOT
dim totem
#

Hi there

willow moth
#

Hi

dim totem
#

The payload passed to constructEvent must be the raw request body that Stripe sends you with the webhook. This should be a buffer data type

#

Looks like you are manipulating this via payload = JSON.stringify(payload); currently

#

So that will cause verification to fail

willow moth
#

The payload is passed though in json format but it said turn it to a string, which I did but is not working, (stackoverflow said to try that)

#

I also did this router.post("/paymentmade", bodyParser.raw({type:"application/json"}), createSession.paymentMade);

dim totem
#

It shouldn't be JSON or a string in this case.

#

Are you using Express?

willow moth
#

Yes

dim totem
#

Are you setting express.json() when initializing your express instance?

willow moth
#

app.use(express.json()); yes

dim totem
#

Yeah so that is also going to manipulate the raw body and cause it to fail. Try something like: app.use((req, res, next) => { if (req.originalUrl === '/yourWebhookURL') { next(); // Do nothing with the body because I need it in a raw state. } else { express.json()(req, res, next); // ONLY do express.json() if the received request is NOT a WebHook from Stripe. } });

willow moth
#

It is now throwing this 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

By the way this error was also occurring when I just passed in the body rather than manipulating it to a string

#

Do you want me to send my req.body to show you how it looks?

dim totem
#

Did you remove payload = JSON.stringify(payload); as well?

willow moth
#

Yes

dim totem
#

Can you show me the full code for the endpoint and how you are initializing Express?

#

Like something is still messing with the raw body. The only other possible issue here is that you are using the wrong webhook secret. You are using the one the CLI provided you, correct?

willow moth
#
const app = express();
app.use((req, res, next) => {
    console.log("REQ URL", req.originalUrl)
    if (req.originalUrl === '/api/premium/paymentmade') {
      next(); // Do nothing with the body because I need it in a raw state.
    } else {
      express.json()(req, res, next);  // ONLY do express.json() if the received request is NOT a WebHook from Stripe.
    }
  });
router.post("/paymentmade", express.raw({type:"application/json"}), createSession.paymentMade);
module.exports.paymentMade = async (request, response) => {
    console.log("Payment made")


    let payload = request.body;
    // if (Buffer.isBuffer(payload)) {
    //     payload = payload.toString();
    // }

    //payload = JSON.parse(payload);

    console.log(payload)
    const sig = request.headers["stripe-signature"]
    
    let event
    
    try {
        event = stripeInstance.webhooks.constructEvent(payload, sig, config.webhook)
    } catch (error) {
        console.log("ERROR")
        console.log(error.message)
        response.status(400).json({success: false})
        return
    }

    console.log("SUCCESS PAYMENT")

    // Success
    console.log(event.type)
    switch (event.type) {
        case 'payment_intent.succeeded':
            console.log("PAYMENT IS SUCCESS IN THE INTENT")
            const paymentIntentSucceeded = event.data.object;
            // Then define and call a function to handle the event payment_intent.succeeded 
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }


    
    console.log(event.type)
    console.log(event.data.object)
    console.log(event.data.object.id)
    response.json({
        success: true
    })
}
#

Yes

#

whsec_1...

#

I believe its the default local host one

dim totem
#

What does your console.log(payload) show?

#

Is that a js object?

#

Or do you see a buffer?

#

(Which would look like a bunch of binary)

willow moth
#

json object

{
  id: 'evt_3MnoXSCyHatXttBL0ciiAekD',
  object: 'event',
  api_version: '2022-11-15',
  ...
}
dim totem
#

Yep okay so that is still the source of the issue

willow moth
#

No I do not see buffer

dim totem
#

Ah you are using router.post

#

Can you change that to app.post?

#

Or are you initializing router somewhere else?

willow moth
#

const router = express.Router();

Should I try router.use...

its more so I am able to organise my routes

dim totem
#

Oh right right

willow moth
#

I tried router.use that did not work, I am going to see if putting in the main file will work

dim totem
#

Yeah was going to suggest testing putting it in the same file as where you intialize express

#

Another thing to check is to log the request.body instead of the payload

#

You might have something in your project that is automatically JSONifying if you initiate a variable from a buffer

willow moth
#

I put in the main app.js the payload was still a json object {id....} but it threw this error instead
The "key" argument must be of type string or an instance of ArrayBuffer, Buffer, TypedArray, DataView, KeyObject, or CryptoKey. Received undefined

dim totem
#

Where is key in your code?

willow moth
#

Honestly I don't have any key argument

#
const rawdata = fs.readFileSync("config/stripesecret.json");
const config2 = JSON.parse(rawdata);
const secret = config2.secret;
const stripeInstance = require('stripe')(secret);
app.post("/premium/paymentmade", async (request, response) => {
    console.log("Payment made")


    let payload = request.body;
    // if (Buffer.isBuffer(payload)) {
    //     payload = payload.toString();
    // }

    //payload = JSON.parse(payload);

    console.log(payload)
    const sig = request.headers["stripe-signature"]
    
    let event
    
    try {
        event = stripeInstance.webhooks.constructEvent(payload, sig, config.webhook)
    } catch (error) {
        console.log("ERROR")
        console.log(error.message)
        response.status(400).json({success: false})
        return
    }

    console.log("SUCCESS PAYMENT")

    // Success
    console.log(event.type)
    switch (event.type) {
        case 'payment_intent.succeeded':
            console.log("PAYMENT IS SUCCESS IN THE INTENT")
            const paymentIntentSucceeded = event.data.object;
            // Then define and call a function to handle the event payment_intent.succeeded 
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }


    
    console.log(event.type)
    console.log(event.data.object)
    console.log(event.data.object.id)
    response.json({
        success: true
    })
})

worldly pollen
#

Hi there 👋 taking over as my teammate needs to step away. Taking a look through your code.

willow moth
#

Oh Hi Toby

#

Thanks bismarck btw

dim totem
#

Yep going to pass off to toby but I think the issue is likely in initializing payload try to just pass request.body or at least log out request.body

worldly pollen
#

Trying to see if key is the name of the one of the parameters that constructEvents accepts. While I do, can you log the other two variables being passed into that function (sig and config.webhook) to make sure they are populated as expected? I don't need to see the output, just want to make sure they're set.

willow moth
#

Ok, when I moved the webhook to the main file on trying to test if the router was the cause, the webhook secret was not defined as the folder was not correct, but I am still experiencing the same error from the start:

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
worldly pollen
#

Hm, so it seems like something is still parsing the contents of the request.

This is an approach that I've seen used before that allowed a request to include both a JSON parsed body and a buffer to the raw body:

  verify: (req, res, buf) => {
    req.rawBody = buf
  }
})); ```

Then later I'm about to reference the `rawBody` parameter on the request to get to the buffer.
willow moth
#

I added the code same error

worldly pollen
#

Just to make sure we're aligned, can you share the error (want to make sure I'm thinking of the right one)?

willow moth
#

Should I share all the code I have added or just the webhook request

#
app.use((req, res, next) => {
    console.log("REQ URL", req.originalUrl)
    if (req.originalUrl === '/premium/paymentmade') {
      next(); // Do nothing with the body because I need it in a raw state.
    } else {
      express.json()(req, res, next);  // ONLY do express.json() if the received request is NOT a WebHook from Stripe.
    }
  });

app.use(bodyParser.json({
    verify: (req, res, buf) => {
      req.rawBody = buf
    }
  })); 

router.post("/paymentmade", express.raw({type:"application/json"}), createSession.paymentMade);
module.exports.paymentMade = async (req, res) => {
    console.log("Payment made")


    let payload = req.body;
    // if (Buffer.isBuffer(payload)) {
    //     payload = payload.toString();
    // }

    //payload = JSON.parse(payload);

    console.log(payload)
    const sig = req.headers["stripe-signature"]
    
    let event
    
    try {
        event = stripeInstance.webhooks.constructEvent(payload, sig, config.webhook)
    } catch (error) {
        console.log("ERROR")
        console.log(error.message)
        res.status(400).json({success: false})
        return
    }

    console.log("SUCCESS PAYMENT")

    // Success
    console.log(event.type)
    switch (event.type) {
        case 'payment_intent.succeeded':
            console.log("PAYMENT IS SUCCESS IN THE INTENT")
            const paymentIntentSucceeded = event.data.object;
            // Then define and call a function to handle the event payment_intent.succeeded 
            break;
        // ... handle other event types
        default:
            console.log(`Unhandled event type ${event.type}`);
    }


    
    console.log(event.type)
    console.log(event.data.object)
    console.log(event.data.object.id)
    res.json({
        success: true
    })
}
#

I have also done it in the app.js rather than using the router it gives the same error but I am not sure if you want to see that code as well

worldly pollen
willow moth
#

Before I removed any app.use() statements that I added just for stripe this error

  Webhook signature verification failed. 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 JavaSc
ript 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

Then after I removed the app.use() this new error occured

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
worldly pollen
#

Interesting

willow moth
#

Bismark noticed that when I console.log the request.body it returns a json object so
{id: "...", ....} rather than I belive buffer?

#

or some binary data

worldly pollen
#

I'm not sure offhand what difference would cause that error message to change, but it sounds like something in the framework is parsing your request body (likely trying to be helpful but not realizing we wouldn't like that).

willow moth
#

Is there any fixes that I could do e.g convert it to a buffer, I am not sure what could be changing it as the only app.use() I have is cors and some headers

worldly pollen
#

Can you reshare the current state of the project, want to make sure I'm looking at the right code?

willow moth
#

Should I send everything related to stripe?

worldly pollen
#

Related to the processing of the webhook events.

willow moth
#
app.use(bodyParser.json({
    // Because Stripe needs the raw body, we compute it but only when hitting the Stripe callback URL.
    verify: function(req,res,buf) {
        var url = req.originalUrl;
        if (url.startsWith('/premium/paymentmade')) {
            req.rawBody = buf.toString()
        }
    }}));


const rawdata = fs.readFileSync("config/stripesecret.json");
const config2 = JSON.parse(rawdata);
const secret = config2.secret;
const stripe = require('stripe')(secret);

app.post('/premium/paymentmade', express.raw({type: 'application/json'}), (request, response) => {
    let event = request.body;
    // Only verify the event if you have an endpoint secret defined.
    // Otherwise use the basic event deserialized with JSON.parse
    let endpointSecret = config2.webhook
    if (endpointSecret) {
      // Get the signature sent by Stripe
      const signature = request.headers['stripe-signature'];

      console.log(request.body)
      try {
        event = stripe.webhooks.constructEvent(
          request.body,
          signature,
          endpointSecret
        );
      } catch (err) {
        console.log(`⚠️  Webhook signature verification failed.`, err.message);
        return response.sendStatus(400);
      }
    }
  
    // Handle the event
    switch (event.type) {
      case 'payment_intent.succeeded':
        const paymentIntent = event.data.object;
        console.log(`PaymentIntent for ${paymentIntent.amount} was successful!`);
        // Then define and call a method to handle the successful payment intent.
        // handlePaymentIntentSucceeded(paymentIntent);
        break;
      case 'payment_method.attached':
        const paymentMethod = event.data.object;
        // Then define and call a method to handle the successful attachment of a PaymentMethod.
        // handlePaymentMethodAttached(paymentMethod);
        break;
      default:
        // Unexpected event type
        console.log(`Unhandled event type ${event.type}.`);
    }
  
    // Return a 200 response to acknowledge receipt of the event
    response.send();
  });

#

That should be everything

#

I need to go for about 20-25 mins should I send in a message in this channel when I am back, I am just going to say thanks if you not here in 25 mins

deft bridgeBOT
worldly pollen
#

Thank you, taking a closer look.

#

It looks like you're putting the raw request body in rawBody, but when doing the webhook signature verification you're still referencing body instead.

What happens if you change this part:

      try {
        event = stripe.webhooks.constructEvent(
          request.body,
          signature,
          endpointSecret
        );
      } catch (err) {
        console.log(`⚠️  Webhook signature verification failed.`, err.message);
        return response.sendStatus(400);
      }```

To this:
```console.log(request.rawBody)
      try {
        event = stripe.webhooks.constructEvent(
          request.rawBody,
          signature,
          endpointSecret
        );
      } catch (err) {
        console.log(`⚠️  Webhook signature verification failed.`, err.message);
        return response.sendStatus(400);
      }```
#

(also working on testing your code on my end)

willow moth
willow moth
worldly pollen
#

🥳 wooh, glad to hear that worked!

willow moth
#

But again thanks that is probably the fastest and most helpful response team I have ever had

worldly pollen
#

We're always happy to help, I'll make sure your thanks are passed along to the team.