#mike7189-webhooks

1 messages · Page 1 of 1 (latest)

vagrant perch
#

mike7189-webhooks

#

@broken copper what do you need help with?

broken copper
#

I am not understanding this stuff

#

here's the code of webhook, but I can't see anything like that in the console

#

./stripe listen --forward-to localhost:3001/api/webhook

#

seems alright, but I don't get it

#

okay I got the problem: it was /webhooks and not /webhook and I forgot break; in switch/case/default

#

so now this problem

vagrant perch
#

okay you're making progress, now you get the event but you're apparently not parsing it properly. Did the constructEvent work? Because my guess is that you get an error. Log the event output first

broken copper
#

nothing

#

event: undefined

#

this code gave undefined

#

server.use("/api/webhooks", express.raw({ type: "*/*" }), webhooksRouter); it's raw

vagrant perch
#

not sure what "it's raw" means. You paste pictures of code and I'm not really following. Can you add logs? Like log the req.body, log the event after parsing it, confirm if you are getting in the catch, etc.

broken copper
#

yep... it's not raw body

#

and if I remove express.raw({ type: "*/*" }), middlware - I am having body: undefined

vagrant perch
broken copper
#

hold on a minute

#

if I don't use middlware it says body is undefined

#

if I use it shows me buffer

vagrant perch
#

That's purely an Express question though, not really something Stripe specific

broken copper
#

I am having rawParser middlware, body shown as Buffer, but signature verification fails anyway

#

is everythign alright over here?

vagrant perch
#

@broken copper please avoid sharing pictures of code. You can share the exact code here between three ` on both sides

#

And I can't tell you if everything right, I've never used Express as a framework. This is something you need to debug on your end first since it's not really a Stripe issue just yet, it's about you not getting the raw body

#

also if you're just debugging, you can start without webhook signature verification to make progress and come back to this later

broken copper
#

well I need a webhook now... Everything is pretty much done, I need to notify the website owner upon transaction

#

I kinda figured out what's the problem.
It's middleware. I do have rawParser and it's good, but typeof req.body gives me object, so apparently anonymous middleware is a json parser. God knows how to disable it

#
/* routes */
server.use("/api/webhooks", webhooksRouter);
server.use("/api", router);

weird stuff tho

vagrant perch
#

yeah Express is super strange with this

#

I've seen at least 20 different solutions at this point, all subtly different 😦

broken copper
#

typeof req.body should return Buffer, right?

vagrant perch
#

I don't know sorry, that's an Express question, not related to Stripe's API 😅

broken copper
#

it's not express question. It's about data type of raw body

vagrant perch
#

I'm not sure I follow what you mean

broken copper
#

the webhook has a line console.log(typeof req.body) what shall I expect on the output?

vagrant perch
#

I do not know though

#

I've never played with Express, it's an Express thing, it's unrelated to Stripe, I don't know all the types in every languages I'm sorry

broken copper
#

what would you expect in feathers or any other JavaScript library?

vagrant perch
#

I'm really sorry, I'm not sure how to say it, I've never tried to print the type of that thing before in JS

broken copper
#
body:  {
  id: 'evt_3JrQ7cKuBnFIZzDs1UVxRtd7',
  object: 'event',
  api_version: '2020-08-27',
  created: 1635871564,
  data: {
    object: {
      id: 'pi_3JrQ7cKuBnFIZzDs1x4Fh1Rv',
      object: 'payment_intent',
      amount: 7400,
      amount_capturable: 0,
      amount_received: 0,
      application: null,
      application_fee_amount: null,
      canceled_at: null,
      cancellation_reason: null,
      capture_method: 'automatic',
      charges: [Object],
      client_secret: 'pi_3JrQ7cKuBnFIZzDs1x4Fh1Rv_secret_jeiiYx5FLvO6zsFevJrp4ZGDj',
      confirmation_method: 'automatic',
      created: 1635871564,
      currency: 'eur',
      customer: null,
      description: null,
      invoice: null,
      last_payment_error: null,
      livemode: false,
      metadata: {},
      next_action: null,
      on_behalf_of: null,
      payment_method: null,
      payment_method_options: [Object],
      payment_method_types: [Array],
      receipt_email: null,
      review: null,
      setup_future_usage: null,
      shipping: null,
      source: null,
      statement_descriptor: null,
      statement_descriptor_suffix: null,
      status: 'requires_payment_method',
      transfer_data: null,
      transfer_group: null
    }
  },
  livemode: false,
  pending_webhooks: 2,
  request: {
    id: 'req_Te3lQVnGSepvJq',
    idempotency_key: '64bf9244-0e3c-43c0-8a82-5e5680036498'
  },
  type: 'payment_intent.created'
}
typeof body:  object
error:  No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
event:  undefined
#

so req.body seems to be readable now

vagrant perch
#

yeah the problem is whether you are getting the real raw body

broken copper
#

uhm... are you sure I need raw body and stripe.webhooks.constructEvent()?

#

I just passed it as JSON with const event = req.body and I got this:

event:  {
  id: 'evt_3JrQQHKuBnFIZzDs2RC8vROp',
  object: 'event',
  api_version: '2020-08-27',
  created: 1635872721,
  data: {
    object: {
      id: 'pi_3JrQQHKuBnFIZzDs2aZcXY9l',
      object: 'payment_intent',
      amount: 7400,
      amount_capturable: 0,
      amount_received: 0,
      application: null,
      application_fee_amount: null,
      canceled_at: null,
      cancellation_reason: null,
      capture_method: 'automatic',
      charges: [Object],
      client_secret: 'pi_3JrQQHKuBnFIZzDs2aZcXY9l_secret_elkUTTwQwI5sCOBtLXRetjLkz',
      confirmation_method: 'automatic',
      created: 1635872721,
      currency: 'eur',
      customer: null,
      description: null,
      invoice: null,
      last_payment_error: null,
      livemode: false,
      metadata: {},
      next_action: null,
      on_behalf_of: null,
      payment_method: null,
      payment_method_options: [Object],
      payment_method_types: [Array],
      receipt_email: null,
      review: null,
      setup_future_usage: null,
      shipping: null,
      source: null,
      statement_descriptor: null,
      statement_descriptor_suffix: null,
      status: 'requires_payment_method',
      transfer_data: null,
      transfer_group: null
    }
  },
  livemode: false,
  pending_webhooks: 2,
  request: {
    id: 'req_bF0QtDVtdCoSdx',
    idempotency_key: 'b7ca3af9-e654-4aa3-b86d-33dd45a7f98e'
  },
  type: 'payment_intent.created'
}
#
webhooksRouter.post(
    "/",
    express.json({ type: "application/json" }),
    async (req, res) => {
        const event = req.body;
        try {
            console.log(`body: `, req.body);
            console.log(`headers: `, req.headers);
        } catch (error) {
            console.log(`error: `, error.message);
        } finally {
            console.log("finally:");
            console.log(`event type: `, event.type);
        }
    }
);
#

just like this

vagrant perch
#

that code example is explicitly not verifying signatures though

broken copper
#

few months back I had the same problem, and I think I was talking to you and we had kinda same discussion and I "solved" the issue by adding express.raw({type:"*/*"}) middleware to the route and it was working just fine.
Today I finally got back to my old project, and all of a sudden webhook stopped working

broken copper
#

so... is there anything bad about not verifying signature?

vagrant perch
#

Well it means someone can "attack" your integration and send fake events. What you could do to start is call https://stripe.com/docs/api/events/retrieve to retrieve the Event from the API and make sure it's valid. It's more API Requests but it might help to start

broken copper
#

hm....
So in webhook upon each event I can call retrieve() and

if (event_in_webhook !== event_in_retrieve) throw new Error(403)

right?

vagrant perch
#

ignore the rest of the webhook body

broken copper
#

so:

if (event_in_webhook.id !== event_in_retrieve.id) throw new Error(403)

?

vagrant perch
#

not at all no

#

You get the Event's JSON already. I explained you can't trust it, unless you verify the signature. So instead you ignore the entire JSON other than the event id the evt_123. Then your call does a call to https://stripe.com/docs/api/events/retrieve and then you handle the event based on the response the API gives you, you ignore the rest

broken copper
#

oh.. So all I need is to handle the last event? charge_succeeded or something like that?

#

and if event is malicious, this event wont exist on retrieve(), right?

vagrant perch
#

correct, or the data they will send you will ignore

#

imagine someone finding a way to pay $1 but tell you they paid $222.22 by sending amount: 22222 in the Event, you would ignore it, retrieve it from the API itself and see amount: 100

broken copper
#

damn i am lost

vagrant perch
#

Yeah sorry, I'm just not understanding what part is losing you unfortunately

#

All you have to do is this
1/ Something happens in your account
2/ Stripe creates an Event and sends the JSON to your endpoint/code
3/ Your code gets it, gets the event id (evt_123) and then ignores everything else
4/ Your code calls https://stripe.com/docs/api/events/retrieve and then handles the event, the same way as if you used the payload/JSON we sent you

broken copper
#

Alright. I’ll try to verify normal way, if I won’t manage it I’ll go with events stuff

vagrant perch
#

sounds good

broken copper
#
event:  {
  id: 'evt_3JrSTHKuBnFIZzDs1nD3x3kX',
  object: 'event',
  api_version: '2020-08-27',
  created: 1635880596,
  data: {
    object: {
      id: 'pi_3JrSTHKuBnFIZzDs1luoc5pQ',
      object: 'payment_intent',
      amount: 7400,
      amount_capturable: 0,
      amount_received: 0,
      application: null,
      application_fee_amount: null,
      canceled_at: null,
      cancellation_reason: null,
      capture_method: 'automatic',
      charges: [Object],
      client_secret: 'pi_3JrSTHKuBnFIZzDs1luoc5pQ_secret_l2RvQLif1gcatDKRgJC6uqPMy',
      confirmation_method: 'automatic',
      created: 1635880595,
      currency: 'eur',
      customer: null,
      description: null,
      invoice: null,
      last_payment_error: null,
      livemode: false,
      metadata: {},
      next_action: null,
      on_behalf_of: null,
      payment_method: null,
      payment_method_options: [Object],
      payment_method_types: [Array],
      receipt_email: null,
      review: null,
      setup_future_usage: null,
      shipping: null,
      source: null,
      statement_descriptor: null,
      statement_descriptor_suffix: null,
      status: 'requires_payment_method',
      transfer_data: null,
      transfer_group: null
    }
  },
  livemode: false,
  pending_webhooks: 2,
  request: {
    id: 'req_zMieJIWMvNaHbN',
    idempotency_key: '11edd702-edac-4bd4-adc8-fe5d71c450a0'
  },
  type: 'payment_intent.created'
}


jesus christ I did it

#
server.use("/api/webhooks", express.raw({ type: "*/*" }), webhooksRouter);
server.use("/api", express.json(), router);

raw before json and that's it. It seems. I dunno, I've tried so much stuff, I have no damn clue what I've actually done

vagrant perch
#

damn

#

yeah I told you there are so many weird answers in that issue

#

thanks for taking the time to post it on that link, it will help others, I really appreciate it

#

and congrats, this is no small feat to make this work with node+Express trust me

broken copper
#

As far as I understood the biggest issue is that express.json() making a mess.
Thank you for your time 😇