#cberube-webhook-signature
1 messages ยท Page 1 of 1 (latest)
Hello! We'll be with you shortly. Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.
- cberube, 35 minutes ago, 31 messages
Hello
Hi
Have you added logs in your webhook handler to see the error and why it is failing?
Not sure on how to do it. But, when I debug the body, signature and secret are available. Would there be an issue with the body payload?
Yep definitely could be
You need to ensure you are using the raw request body
If you share your webhook handler code we can look at that together as well
post('/webhook', bodyParser.raw({type: 'application/json'}), (req: CustomRequest, res: Response) => {
let event: Stripe.Event;
try {
// Retrieve the event by verifying the signature using the raw body and secret.
const signature = req.headers['stripe-signature'] ?? '';
event = stripe.webhooks.constructEvent(
req.body,
signature,
process.env.STRIPE_WEBHOOK_SECRET ?? ''
);
console.log(event);
} catch (err: unknown) {
console.log('โ ๏ธ Webhook signature verification failed.');
return res.sendStatus(400);
}
Do you use express.json() up above in your code?
yes: app.use(express.json());
$ stripe trigger customer.created
Setting up fixture for: customer
Running fixture for: customer
Trigger succeeded! Check dashboard for event details.
evt_1OmKdgJ36fJ52FffCNXDqgaE fails with bad request
Yeah so that is going to manipulate the raw body
Try doing something like: app.use((req, res, next) => { if (req.originalUrl === '/webhook') { 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. } });
You mean like that bodyParser.json()
or
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()
}
});
In place of your app.use
Like the one I shown
replaced bodyParser.json()(req, res, next); for express.json()
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()
}
});
Can you share your entire Webhook handler code
Up above I'd assume you are doing app.use somewhere
And setting express.json
router.post('/webhook', bodyParser.raw({type: 'application/json'}), (req: CustomRequest, res: Response) => {
let event: Stripe.Event;
try {
// Retrieve the event by verifying the signature using the raw body and secret.
const signature = req.headers['stripe-signature'] ?? '';
event = stripe.webhooks.constructEvent(
req.body,
signature,
process.env.STRIPE_WEBHOOK_SECRET ?? ''
);
console.log(event);
} catch (err: unknown) {
console.log('โ ๏ธ Webhook signature verification failed.');
return res.sendStatus(400);
}
// Extract the data from the event.
const data: Stripe.Event.Data = event.data;
const eventType: string = event.type;
// Cast the event into a PaymentIntent to make use of the types.
const pi: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
if (eventType === 'payment_intent.creeated') {
console.log(`๐ Webhook received: ${pi.object} ${pi.status}!`);
} else if (eventType === 'payment_intent.succeeded') {
// Funds have been captured
// Fulfill any orders, e-mail receipts, etc
// To cancel the payment after capture you will need to issue a Refund
// (https://stripe.com/docs/api/refunds).
console.log(`๐ Webhook received: ${pi.object} ${pi.status}!`);
console.log('๐ฐ Payment captured!');
} else if (eventType === 'payment_intent.payment_failed') {
// Cast the event into a PaymentIntent to make use of the types.
const pi: Stripe.PaymentIntent = data.object as Stripe.PaymentIntent;
console.log(`๐ Webhook received: ${pi.object} ${pi.status}!`);
console.log('โ Payment failed.');
}
return res.json({received: true});
}
);
No. Node and Express.
in the server.js
const app: Application = express();
app.use('/', shoppingCartRoutes);
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
bodyParser.json()(req, res, next);
}
});
Yep try that
That is what I have already on the live server already.
What happens when you log out your req.body within your /webhook endpoint?
I am not able to debug locally since I added a listener with the cli. But I was able to do it earlier this morning. So I don't see any console.log happening so far.
Not sure what you mean by that really. You mean you are just using stripe listen and you aren't forwarding to a local endpoint nor using an HTTPS endpoint you have set up?
I use listen , then thirgger a customer.created event. But, now webhooks event are received locally.
Are you using the --forward-to command? like we show here: https://docs.stripe.com/webhooks?lang=node#local-listener
Will do
it breaks here when I do stripe trigger customer.create
stripe.webhooks.constructEvent(req.body, signature, secret);
Do i need to provide data?
Have you logged out req.body like I mentioned above?
{
id: 'evt_1OmM3KJ36fJ52FffLR8IxAkb',
object: 'event',
api_version: '2023-10-16',
created: 1708545962,
data: {
object: {
id: 'cus_PbZBTZC2Y0Nihr',
object: 'customer',
address: null,
balance: 0,
created: 1708545962,
currency: null,
default_source: null,
delinquent: false,
description: '(created by Stripe CLI)',
discount: null,
email: null,
invoice_prefix: '7FD2CBA9',
invoice_settings: [Object],
livemode: false,
metadata: {},
name: null,
next_invoice_sequence: 1,
phone: null,
preferred_locales: [],
shipping: null,
tax_exempt: 'none',
test_clock: null
}
},
livemode: false,
pending_webhooks: 3,
request: {
id: 'req_gAboclHDaFMuWX',
idempotency_key: 'ab95b42a-29bf-42cd-990f-0754bdeba98f'
},
type: 'customer.created'
}
Yeah so that is the JSON response
That is the issue
If it was the raw request body then it would look like binary
But your framework is currently manipulating the raw body
Which is causing the signature verification to fail
I have these:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
bodyParser.json()(req, res, next);
}
});
post('/webhook', bodyParser.raw({type: 'application/json'})
You need to take out the app.use(bodyParser.json()); at the top there
Since it looks like you have it twice so everything is running through it
app.use(express.json());
// app.use(bodyParser.json());
Yep you can't have express.json() either for your webhook endpoint
is that ok, or do I remove the first one as well.
Both of those are going to mess with the raw request body
It is passing now. But, it is still a bad request in the dashbord.
The response you see in the Dashboard is coming from your code
So somewhere you are sending back a 400 in that case?
Really you just should add logs throughout your webhook handler so you can narrow down exactly what is happening
cberube-webhook-signature
Looking into it.
I am receiving customer.created and returning
res.json({ received: true });
Do you have a specific question?
customer request webhook is 400
{
"id": "evt_1OmN2LJ36fJ52Fff1nJuePwo",
"object": "event",
"api_version": "2023-10-16",
"created": 1708549745,
"data": {
"object": {
"id": "cus_PbaCK6x1fdb0Hb",
"object": "customer",
"address": null,
"balance": 0,
"created": 1708549745,
"currency": null,
"default_source": null,
"delinquent": false,
"description": "(created by Stripe CLI)",
"discount": null,
"email": null,
"invoice_prefix": "C7219ABA",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null,
"rendering_options": null
},
"livemode": false,
"metadata": {
},
"name": null,
"next_invoice_sequence": 1,
"phone": null,
"preferred_locales": [
],
"shipping": null,
"tax_exempt": "none",
"test_clock": null
}
},
"livemode": false,
"pending_webhooks": 3,
"request": {
"id": "req_2bGL9jye9AtCZM",
"idempotency_key": "788aa1c7-eb80-4af1-9d53-a17d689091c2"
},
"type": "customer.created"
}
in the dahboard
My colleague explained you have to debug this first so that's the first step. Add clear logs to your code to figure out what returns a 400
the webhook endpoint is returning res.json({ received: true }) as suggested in the doc.
But it's not since it returns a 400, that's what you need to debug
I was not able to trigger any other routes= and this si what is returned from the webhook route.
res.json({received: true});
statusCode:
200
statusMessage:
'OK'
Hello! I'm taking over and catching up...
Right now there is not much, they say they return 200, we get a 400 and we need them to debug way further
Yep, your server is returning an HTTP 400 response and Bad Request in the body. You need to figure out why your server is responding like that.
Hi there is an issue with webhook. It returns this:
res.json({received: true});
statusCode:
200
statusMessage:
'OK'
but, in the dashboard , the status it is 400.
Your server is not returning a 200, and it is not returning JSON in the response.
Your server is returning a 400 with the text Bad Request in the response.
You need to figure out why your server is doing that.
I know that looking at your code makes it look like it should be returning a 200 with JSON, but it isn't.
I am debugging it locally and that's not what is returned.
You're only going to get so far debugging it locally. Add more logging to your code, then trigger an Event from Stripe, then look at your logs to see exactly what's happening.
it is indeed returning 400 when I remove all other routes except the ones for the shopping cart.
Were you able to find out why your server is returning a 400?
Did you add more logging?
Their could be an issue with one of the parsers.
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json();
bodyParser.urlencoded({ extended: true });
}
});
What about regrouping these?
What about it? Have you tried it?
Can any of these interfere with the stripe API
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
Yes, those can prevent the raw body of the request from reaching the function that validates the signature.
It is successsfull has it does not fail webhooks.constructEvent
You need to make sure the raw, unaltered, unparsed body of the request is passed into the function that constructs the Event, otherwise signature validation will fail.
Can you reword that sentence? I don't know what you mean.
Okay, so something else is wrong then. You really should add a lot more logging to your code so you know exactly where things are going wrong.
Is there a reason you're reluctant to add more logging?
I have all logging I need in debugger...
But your server is returning a 400 response and you don't know why, so it sounds like you need more information beyond what the debugger is currently giving you.
Adding more logging to the code would provide you with the missing information, wouldn't it?
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Use JSON parser for all non-webhook routes
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
bodyParser.json()(req, res, next);
bodyParser.urlencoded({ extended: true });
}
});
Would it make sense to replace the former with the later.
Just to clarify, you pasting random bits of your code like that isn't really helpful.
Possibly? There's no way to know on my end. Have you tried it?
If the constructEvent function is succeeding the parsing is not the issue though, right?
Ye,
It returns:
res.json({received: true});
statusCode:
200
statusMessage:
'OK'
I will try to push it live to see if the resend will work.
One thing I want to clarify is that just because your Node code sets the status code to 200 or you call res.json that doesn't mean that's what your server is actually returning. There could be a step after that which is failing, or something at the web server level.
Did not find anything like that so far.
I want to push you one more time to add additional logging throughout your code so you know exactly what's working and what isn't, how far things get in the process when you receive an Event, etc. Beyond that I don't know what other help I can offer. Happy to answer specific questions if you have any though!
So far, I don't see what could send a 400 except the live server. The parsing error seems resolved.
<Buffer 7b 0a 20 20 22 69 64 22 3a 20 22 65 76 74 5f 33 4f 6d 4f 63 39 4a 33 36 66 4a 35 32 46 66 66 31 65 57 33 61 72 45
46 22 2c 0a 20 20 22 6f 62 6a 65 63 ... 1989 more bytes>
๐ Webhook received: payment_intent requires_payment_method!
Live server logging: ๐ Webhook received: customer cus_Pbb30OeXuioOad!
All green now. So the dashboard request where not redirected locally;
Thanks for helping out, I was postponing this webhook debugging session for a while.
Cheer, will need some more support on idempotency later on.