#orangesidny
1 messages · Page 1 of 1 (latest)
Hi there
Hi
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
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);
Yes
Are you setting express.json() when initializing your express instance?
app.use(express.json()); yes
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. } });
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?
Did you remove payload = JSON.stringify(payload); as well?
Yes
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?
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
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)
json object
{
id: 'evt_3MnoXSCyHatXttBL0ciiAekD',
object: 'event',
api_version: '2022-11-15',
...
}
Yep okay so that is still the source of the issue
No I do not see buffer
Ah you are using router.post
Can you change that to app.post?
Or are you initializing router somewhere else?
const router = express.Router();
Should I try router.use...
its more so I am able to organise my routes
Oh right right
I tried router.use that did not work, I am going to see if putting in the main file will work
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
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
Where is key in your code?
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
})
})
Hi there 👋 taking over as my teammate needs to step away. Taking a look through your code.
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
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.
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
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.
I added the code same error
Just to make sure we're aligned, can you share the error (want to make sure I'm thinking of the right one)?
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
Hm, I'm wondering if all of the different parser approaches are conflicting with each other currently.
Would you be willing to try downloading our sample, and see if that alone works?
https://stripe.com/docs/webhooks/quickstart?lang=node
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
Interesting
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
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).
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
Can you reshare the current state of the project, want to make sure I'm looking at the right code?
Should I send everything related to stripe?
Related to the processing of the webhook events.
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
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)
Thank you toby, roadrunner and bismarck that did manage to work
I tried doing that earlier for the request.rawBody, but I that was without the app.use where it sets the value for the request.rawBody
🥳 wooh, glad to hear that worked!
But again thanks that is probably the fastest and most helpful response team I have ever had
We're always happy to help, I'll make sure your thanks are passed along to the team.