#cberube-webhook-signature

1 messages ยท Page 1 of 1 (latest)

gusty torrentBOT
#

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
kind lynx
#

Hello

pearl pivot
#

Hi

kind lynx
#

Have you added logs in your webhook handler to see the error and why it is failing?

pearl pivot
#

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?

kind lynx
#

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

pearl pivot
#

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);
}

kind lynx
#

Do you use express.json() up above in your code?

pearl pivot
#

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

kind lynx
#

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. } });

pearl pivot
#

You mean like that bodyParser.json()

#

or
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()
}
});

kind lynx
#

In place of your app.use

pearl pivot
#

Like the one I shown

kind lynx
#

You are going to need to provide more details

#

I don't know what you are asking

pearl pivot
#

replaced bodyParser.json()(req, res, next); for express.json()

#

app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()
}
});

kind lynx
#

Can you share your entire Webhook handler code

#

Up above I'd assume you are doing app.use somewhere

#

And setting express.json

pearl pivot
#

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});

}
);

kind lynx
#

Ah you aren't using Express at all?

#

Is this Next.JS?

pearl pivot
#

No. Node and Express.

kind lynx
#

Where is your router set up?

#

Basically where are you initializing express?

pearl pivot
#

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);
}
});

kind lynx
#

Yep try that

pearl pivot
#

That is what I have already on the live server already.

kind lynx
#

What happens when you log out your req.body within your /webhook endpoint?

pearl pivot
#

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.

kind lynx
#

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?

pearl pivot
#

I use listen , then thirgger a customer.created event. But, now webhooks event are received locally.

kind lynx
pearl pivot
#

Will do

pearl pivot
#

it breaks here when I do stripe trigger customer.create
stripe.webhooks.constructEvent(req.body, signature, secret);
Do i need to provide data?

kind lynx
#

Have you logged out req.body like I mentioned above?

pearl pivot
#

{
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'
}

kind lynx
#

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

pearl pivot
#

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'})

kind lynx
#

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

pearl pivot
#

app.use(express.json());
// app.use(bodyParser.json());

kind lynx
#

Yep you can't have express.json() either for your webhook endpoint

pearl pivot
#

is that ok, or do I remove the first one as well.

kind lynx
#

Both of those are going to mess with the raw request body

pearl pivot
#

It is passing now. But, it is still a bad request in the dashbord.

kind lynx
#

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

gusty torrentBOT
civic zealot
#

cberube-webhook-signature

pearl pivot
#

Looking into it.

pearl pivot
#

I am receiving customer.created and returning
res.json({ received: true });

civic zealot
#

Do you have a specific question?

pearl pivot
#

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

civic zealot
#

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

pearl pivot
#

the webhook endpoint is returning res.json({ received: true }) as suggested in the doc.

civic zealot
#

But it's not since it returns a 400, that's what you need to debug

pearl pivot
#

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'

gusty torrentBOT
raven cove
#

Hello! I'm taking over and catching up...

civic zealot
#

Right now there is not much, they say they return 200, we get a 400 and we need them to debug way further

raven cove
#

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.

pearl pivot
#

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.

raven cove
#

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.

pearl pivot
#

I am debugging it locally and that's not what is returned.

raven cove
#

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.

pearl pivot
#

it is indeed returning 400 when I remove all other routes except the ones for the shopping cart.

raven cove
#

Were you able to find out why your server is returning a 400?

pearl pivot
#

No

#

I put break point on all catch blocks

raven cove
#

Did you add more logging?

pearl pivot
#

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?

raven cove
#

What about it? Have you tried it?

pearl pivot
#

Can any of these interfere with the stripe API
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

raven cove
#

Yes, those can prevent the raw body of the request from reaching the function that validates the signature.

pearl pivot
#

It is successsfull has it does not fail webhooks.constructEvent

raven cove
#

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.

pearl pivot
#

signature works

#

but there is still a 400 in the dahboard.

#

What if I test live...

raven cove
#

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?

pearl pivot
#

I have all logging I need in debugger...

raven cove
#

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?

pearl pivot
#

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.

raven cove
#

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?

pearl pivot
#

testing it

#

apparently express.json and bodyparser.json is similar.

raven cove
#

If the constructEvent function is succeeding the parsing is not the issue though, right?

pearl pivot
#

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.

raven cove
#

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.

pearl pivot
#

Did not find anything like that so far.

raven cove
#

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!

pearl pivot
#

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.