#kevin-webhook-signature

1 messages ยท Page 1 of 1 (latest)

sleek yokeBOT
tepid remnant
#

kevin-webhook-signature

#

Hey @noble wagon ! Usual disclaimer: please don't share pictures of code here ๐Ÿ™‚

noble wagon
#

Hey @tepid remnant got it

tepid remnant
#

what the error means is that request.body is likely empty in your code. The first step is to debug this and log it to a file or your error handler or something to make sure it's not null

#

webhook signature verification with node can be really finicky depending on your environment

#

but yeah really the error right now seems to be that request.body is null/empty in that code you shared a picture of so that's the first thing to debug

noble wagon
#

Thanks, that's the testing code from my webhook setup page, can I clarify if this testing flow is correct?

  1. I use stripe CLI forward the webhook request to my localhost with command : stripe listen --forward-to localhost:3000/api/stripeWebhook
  2. In CLI, trigger an event: stripe trigger payment_intent.succeeded
  3. at this point, my local webhook should have a request coming in, that's the request body I need to debug?
#

When I triggered the event, I didn't even receive any request at all...

tepid remnant
#

yep

#

are you familiar with curl/the terminal?

noble wagon
#

terminal yeah

#

do I need to use sudo ?

#

[ 2:42PM] stripe trigger payment_intent.succeeded
Setting up fixture for: payment_intent
Running fixture for: payment_intent
Trigger succeeded! Check dashboard for event details.

#

I says trigger succeeded but there is no event in the log on stripe's website, and no request coming in

tepid remnant
#

no sudo needed

#

give me a sec I'll give you a command

noble wagon
#

I'm using Next JS btw, it's the native Next JS parser

#

I tried console log request, nothing shows up

#

oh, wait a sec

tepid remnant
#

yeah I know nothing about that unfortunately but I want to show you something simple

noble wagon
#

it logs out the quest

tepid remnant
#

Basically your server-side code is running a request. It's not something you see in a browser or anything

#

What you can do is ignore the CLI and Stripe for a sec and just hit your server first

#

curl --header "Content-Type: application/json" --request POST --data '{id: "evt_123", object: "event", type: "customer.created"}' http://localhost:3000/api/stripeWebhook

#

if you do ^ it's going to send a POST request to that endpoint you have with the JSON body I put inside --data

#

what you can do then is before you look at the signature or anything, log a clear "I am in this code" and then log the req.body

#

and then debug that first. Once that works you can go back to trying the CLI

noble wagon
#

Got it, thanks @tepid remnant !!

#

let me test it

tepid remnant
#

that's why I didn't want a picture earlier, because I could edit your code :p

noble wagon
#

Not sure how you can, but got it ๐Ÿ˜„

tepid remnant
#

mostly you can just paste text here and wrap it between three ` before and after

#

that renders like my curl example above

noble wagon
#

Got it, will do, thanks

tepid remnant
#

let me know what you find

noble wagon
#

Webhook signature verification issue: No stripe-signature header value was provided.%

#

It says above

tepid remnant
#

yeah that's normal if you use the curl part

#

it's really just a basic step to make sure you server-side code in Node.js gets the request

noble wagon
#

I logged out signature from request.headers["stripe-signature"] , it is undefined

#

after triggered an event

#

Got the request.body, but no request.headers["stripe-signature"]

#

logged out request.headers, and there is no stripe-signature there

tepid remnant
#

can you share your real/exact code?

noble wagon
#

'''javascript

#
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

export default async function handler(request, response) {
  const endpointSecret =
    "whse";

  let event = request.body;
  console.log(event) <---------log out request body

  if (endpointSecret) {
    const sig = request.headers["stripe-signature"];  <------undefined!!!

    try {
      event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
    } catch (err) {
      response
        .status(400)
        .send(`Webhook signature verification issue: ${err.message}`);
      return;
    }
  }
......
tepid remnant
#

(you're not supposed to share that secret)

noble wagon
#

That's a test secret

tepid remnant
#

still

noble wagon
#

ok

tepid remnant
#

that code is quite different from what you had earlier so I'm confused now

#

where do you log the request.body?

noble wagon
#

I removed the console log part as it is too long

tepid remnant
#

Gotcha so you're certain it works?

noble wagon
#

yes, I logged out the request.body as well as request.headers

#

the body is long and looks ok, but there is no "stripe-signature" in the header

tepid remnant
#

Okay so try the curl approach again and add --header "Stripe-Signature: 'my signature'" at the end

#

that should force the header and see if your code gets it

noble wagon
#

Webhook signature verification issue: Unable to extract timestamp and signatures from header%

tepid remnant
#

ignore that sorry, ignore anything Stripe right now

#

You're debugging your route handler right now

#

your code should log req.headers and req.body and confirm you get what you are exactly sending in that curl request

noble wagon
#

~ handler ~ event: { id: 'evt_123', object: 'event', type: 'customer.created' }
~ handler ~ sig: 'my signature'

#

it works

#

if this is the case, I guess the testing mode event trigger didn't send out sig in request.headers ?

tepid remnant
#

that feels weird to me though, it definitely should "just work" with the CLI. Trying to think what it could be

#

ah

#

so you're saying that with the CLI, the whole request.headers is completely empty?

noble wagon
#

it is not empty, just doesn't have signature

#
request.headers: {
  host: 'localhost:3000',
  connection: 'keep-alive',
  'cache-control': 'max-age=0',
  'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
  'sec-ch-ua-mobile': '?0',
  'sec-ch-ua-platform': '"macOS"',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
  'sec-fetch-site': 'same-origin',
  'sec-fetch-mode': 'navigate',
  'sec-fetch-user': '?1',
  'sec-fetch-dest': 'document',
  referer: 'http://localhost:3000/api/stripeWebhook',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'en-US,en;q=0.9',
  cookie: 'next
#

these are all the items in headers

tepid remnant
#

okay haven't tested this specific part in a while, give me a few minutes

noble wagon
#

ok, thanks

tepid remnant
#

okay I just tried locally and I definitely get the signature

#

can you try console.log("request headers: ", JSON.stringify(request.headers)); in case this is truncated?

#

I wonder if it's Stripe-Signature, versus stripe-signature or something

noble wagon
#
request headers:  {"host":"localhost:3000","connection":"keep-alive","cache-control":"max-age=0","sec-ch-ua":"\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"","sec-ch-ua-mobile":"?0","sec-ch-ua-platform":"\"macOS\"","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","sec-fetch-site":"same-origin","sec-fetch-mode":"navigate","sec-fetch-user":"?1","sec-fetch-dest":"document","referer":"http://localhost:3000/api/stripeWebhook","accept-encoding":"gzip, deflate, br","accept-language":"en-US,en;q=0.9","cookie":"csrf-c18b04ec4c5698413395dfb274abb8a930b29d15c42a1c; __stripe_mid=76da3216-658f-4f80-a9a6-43d7feb750a4bc883c"}
#

not there

tepid remnant
#

and that comes from the CLI making that request?

noble wagon
#

I could get the sig from your curl, but not from the CLI

tepid remnant
#

Can you give me an example Event id evt_123?

noble wagon
#

yes,

#
request headers:  {"host":"localhost:3000","user-agent":"curl/7.88.1","accept":"*/*","content-type":"application/json","stripe-signature":"'my signature'","content-length":"64"}
tepid remnant
#

that's just my fake request

#

I mean a real Event you got from the CLI to your endpoint so that I can look at our own network requestand see if a signature was sent

noble wagon
#

Where do I find this Event id

#

It says no event forwarded to me successfully

#

evt_1NOTIAKAjj6I40xpCX1JgNiV

tepid remnant
#

You are running the command stripe trigger payment_intent.succeeded. What this does is run some real requests on your account to cause that Event to happen. All those Events are then sent to the CLI locally where you do stripe listen --forward-to xxxxx and you should see the requests come in straight there ```xxxx:~ $ stripe listen --forward-to 127.0.0.1:8888/webhook.php
A newer version of the Stripe CLI is available, please update to: v1.15.0

Ready! You are using Stripe API Version [2015-02-16]. Your webhook signing secret is whsec_123 (^C to quit)
2023-06-29 15:45:32 --> customer.created [evt_ABCDE]
2023-06-29 15:45:32 <-- [200] POST http://127.0.0.1:8888/webhook.php [evt_ABCDE]```

#

that's what I see locally, that's how I know a real Event is received by the CLI from my stripe trigger and then send to my local code

noble wagon
#
Setting up fixture for: payment_intent
Running fixture for: payment_intent
Trigger succeeded! Check dashboard for event details.```
#

this is what I see locally

#
ID
DATE
A Checkout Session expired
evt_1NOTIAKAjj6I40xpCX1JgNiV
6/29/23, 3:26:22 PM
A Checkout Session expired
evt_1NOTEeKAjj6I40xpMVkagJD2
6/29/23, 3:22:44 PM
A Checkout Session expired
evt_1NOT9QKAjj6I40xp9Bsp2RPk
6/29/23, 3:17:20 PM
A Checkout Session expired
evt_1NOT7CKAjj6I40xpDwcJkaz9
6/29/23, 3:15:02 PM
A Checkout Session expired
evt_1NOT6qKAjj6I40xpTpMFT8rc
6/29/23, 3:14:40 PM
A Checkout Session expired
evt_1NOT6iKAjj6I40xpZ7a3TNRS
6/29/23, 3:14:32 PM
A Checkout Session expired
evt_1NOT6YKAjj6I40xpsOHQ6emj
6/29/23, 3:14:22 PM
A Checkout Session expired
evt_1NOT5rKAjj6I40xpNtSALEuB
6/29/23, 3:13:39 PM
A Checkout Session expired
evt_1NOSoRKAjj6I40xp3fgJSeKl
6/29/23, 2:55:39 PM
A Checkout Session expired
evt_1NOSnnKAjj6I40xp9Q6w76cN
6/29/23, 2:54:59 PM```
#

found these from the Developer tab

tepid remnant
#

yeah but that's not what I need

#

I'm sorry, I'm confused how you are doing/using this.

#

Don't you see anything in the terminal where you do stripe listen xxxx?

noble wagon
#
T7 [ 4:09PM] stripe listen --forward-to localhost:3000/api/stripeWebhook
> Ready! You are using Stripe API Version [2022-11-15]. Your webhook signing secret is whsec_2 (^C to quit)```
#

That's all, it just stops here

tepid remnant
#

It shouldn't, it should show real events on that terminal right as they are being received by the CLI

#

Can you log req.body again in your code to find that evt_123?

noble wagon
#

nothing, it just freezes here

#

the samething happened yesterday when I tried it too

#

I thought this is normal behavor

tepid remnant
#

it isn't no. It's supposed to tell you when it receives events and such

noble wagon
#

oh, then, there must be something wrong with this CLI

#

I also upgraded to v.15 too, even in v.14, it was the same

#

no event body now...

#

yeah, I can't even get the event body now...

#

not sure what happened

tepid remnant
#

maybe reboot and start fresh

noble wagon
#

ok, is there any config prevent Mac shell incoming request btw?

#

the outbound traffic looks good

tepid remnant
#

I don't know what that means, but I'm on a mac and it works totally fine

noble wagon
#

ok, let me restart

#

Here is the event ID evt_3NOUIFKAjj6I40xp108how2p

tepid remnant
#

perfect okay so that Event was sent to your CLI and it has a real signature header on our end at least so I'm a bit baffled

#

which version of the CLI are you using?

noble wagon
#

stripe version 1.15.0

#

can stripe completion setup possibly cause issue? I did the stripe completion setup

#
Detected `zsh`, generating zsh completion file: stripe-completion.zsh

Suggested next steps:
---------------------
1. Move `stripe-completion.zsh` to the correct location:
    mkdir -p ~/.stripe
    mv stripe-completion.zsh ~/.stripe

2. Add the following lines to your `.zshrc` enabling shell completion for Stripe:
    fpath=(~/.stripe $fpath)
    autoload -Uz compinit && compinit -i

3. Source your `.zshrc` or open a new terminal session:
    source ~/.zshrc
tepid remnant
#

no I don't think it could be it

#

I'm sorry I'm running out of ideas here. Have you heard of ngrok before? https://ngrok.com

#

you could try that tool quickly to try and send Events to your local endpoint and see if you get the signature

noble wagon
#

alright, let me try it

#

thanks man

#

btw, if the fake curl post works, it should work on my end right

#

seems just the CLI issue

#

it says it is listening on the website

tepid remnant
#

yeah I think so and ngrok will help with that

#

it's basically like our CLI, it "tunnels" requests to your local code

noble wagon
#

Got you, thank you very much

tepid remnant
#

sure thing

sleek yokeBOT
noble wagon
#

ah, finaly, I made it work

#

but why it is still a bad request with 400 error

rancid hound
#

Hi @noble wagon I'm taking over this thread

#

Can you copy and paste the event ID here?

noble wagon
#

I can log out all request.body, request.signature

#

evt_1NOUqyKAjj6I40xpm2AsrDjQ

#

Hey @rancid hound hope all is well!

rancid hound
noble wagon
#

I got a lot event ID

rancid hound
#

Send me one of them that your endpoint responded with 400.

noble wagon
#

evt_3NOUzPKAjj6I40xp0aSAd32R

#

evt_3NOUzPKAjj6I40xp0bvtFMA8

rancid hound
#
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 saw this error message, can you show me your code?

noble wagon
#
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);

export default async function handler(request, response) {
  const endpointSecret =
    "whsec_";

  let event = request.body;
  console.log(" event:", event);

  if (endpointSecret) {
    const sig = request.headers["stripe-signature"];
    console.log(
      "request.headers:",
      sig
    );

    try {
      event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
    } catch (err) {
      response
        .status(400)
        .send(`Webhook signature verification issue: ${err.message}`);
      return;
    }
  }
#

I'm using Next JS btw

#

it is the native Next JS request parser

rancid hound
#

I'm not familiar with NextJS, but it looks like the request.body is not a raw request body (i.e., string or buffer). Can you check if you can get a raw request body and pass it to constructEvent ?

noble wagon
#

ok, give me a sec

#

evt_3NOVOsKAjj6I40xp1fnA6rj3

#

I remove the default parser and it should be raw body now

#

but still got 400

#

here is the event id

rancid hound
#

Can you log request.body's type and check it's a string or buffer?

noble wagon
#

evt_3NOVbvKAjj6I40xp0drU41sw

#

I got error saying no signature value is provided, but I can log the value out

rancid hound
#

It's the same error, have you checked the type of request.body ?

noble wagon
#

It's object

#
const typeofrequestbody = typeof request;
#

request.body is undefined

#

well, if there is a good request parser from stripe packages, to do raw or buffer, that would be great, now, I need to find out what parser is needed ๐Ÿ˜„

rancid hound
#

Then you might want to check why request.body is undefined, or maybe there's a different property you should use to retrieve the request body.

noble wagon
#

yeah, give me a sec

#

I'm sure that I'm not the first guy who faces this issue, do you have any suggestions btw

#

or directions

#

I'm just here trying to figure out how to get raw request body

#

in Next JS

rancid hound
#

I'll suggest you to consult Next.js document and see how to get a request body and make it raw.

noble wagon
#

I did, but it didn't work

#

in this case

rancid hound
#

As you said earlier, the request.body is undefined, and I think you solve this problem first.

noble wagon
#

I checked the request object, and there is no tag named body there

#

great, it is 200 now

#

evt_3NOWHLKAjj6I40xp02clk1UR

bold smelt
#

Thanks

noble wagon
#

huh?

#

does it need raw request or raw request.body

#

because once the parser is turned off, request.body becomes undefined

rancid hound
#

It needs raw request body.

noble wagon
#

since there is no body defined after disable parser, it has to be the request I guess

#

request body just doesnt work, but request works

#

all the working examples I saw are using request directly

#

but there is an issue, on the front end, it still gives me 400 error saying signature value is not provided

#

is this normal?

#

Webhook signature verification issue: No stripe-signature header value was provided.

#

In console it gives me
Unhandled event type charge.succeeded

#

and in the stripe listener it gives me green 200

#

so why do I still get 400 on the end point?

#

I can console log the signature value and see it too

rancid hound
#

What's the new event ID?

noble wagon
#

evt_3NOWh1KAjj6I40xp1Gp7aWj1

#

evt_3NOWh1KAjj6I40xp1J05XUKN

rancid hound
#

Where do you get this error Webhook signature verification issue: No stripe-signature header value was provided. ?

noble wagon
#

it is when I render localhost:3000/api/stripeWebhook

#

in the browser

rancid hound
#

Why you render localhost:3000/api/stripeWebhook in browser?

noble wagon
#

just to see the responses ๐Ÿ˜„

#

obviously the res from stripe is 400 , so is this normal?

#

It should give me 200 too right?

rancid hound
#

No this is not the way to test.

#

Your browser would send a GET request without Webhook signature, and your endpoint would expect a POST request with Webhook signature