#max!-webhooks

1 messages · Page 1 of 1 (latest)

clever socket
#

Where is endpointSecret defined?

open hill
#

just above the router.get

clever socket
clever socket
#

Is it the correct signing secret from your Dashboard/CLI?

open hill
#

It is indeed

#

I have logged everything, all looks good

#

I have also tried JSON.stringify on the payload

open hill
#
{
  id: 'evt_1LaEtpCC4tqBCblrQfVLchfH',
  object: 'event',
  api_version: '2020-08-27',
  created: 1661329521,
  data: {
    object: {
      id: 'cs_test_a1A30IXfRBCpLBJJQzSioGqj8h43JAbLMH2iz859JLUuQNJ8x0XvQ5RzLr',
      object: 'checkout.session',
      after_expiration: null,
      allow_promotion_codes: null,
      amount_subtotal: 3000,
      amount_total: 3000,
      automatic_tax: [Object],
      billing_address_collection: null,
      cancel_url: 'https://httpbin.org/post',
      client_reference_id: null,
      consent: null,
      consent_collection: null,
      currency: 'usd',
      customer: 'cus_MIqa4TXFfppArB',
      customer_creation: 'always',
      customer_details: [Object],
      customer_email: null,
      expires_at: 1661415917,
      livemode: false,
      locale: null,
      metadata: {},
      mode: 'payment',
      payment_intent: 'pi_3LaEtlCC4tqBCblr01PAaYyq',
      payment_link: null,
      payment_method_collection: 'always',
      payment_method_options: {},
      payment_method_types: [Array],
      payment_status: 'paid',
      phone_number_collection: [Object],
      recovered_from: null,
      setup_intent: null,
      shipping: null,
      shipping_address_collection: null,
      shipping_options: [],
      shipping_rate: null,
      status: 'complete',
      submit_type: null,
      subscription: null,
      success_url: 'https://httpbin.org/post',
      total_details: [Object],
      url: null
    }
  },
  livemode: false,
  pending_webhooks: 2,
  request: { id: null, idempotency_key: null },
  type: 'checkout.session.completed'
}``` this is the response
clever socket
#

You're using Express, right?

open hill
#

Yes

clever socket
#

Can I recommend you try express.raw instead of bodyParser.raw:

router.post('/purchase/webhook', express.raw({type: 'application/json'}), async (request, response) => {})
open hill
#

alrighty

#

ill see if that helped

#

Payload:```json
{
id: 'evt_1LaExACC4tqBCblrtlv96FWT',
object: 'event',
api_version: '2020-08-27',
created: 1661329728,
data: {
object: {
id: 'cs_test_a1YHGVM0ctGetE8P1YLzlkLVRyRcO4d2pyfwyhbFoGsepuzPYVtPHrGFi8',
object: 'checkout.session',
after_expiration: null,
allow_promotion_codes: null,
amount_subtotal: 3000,
amount_total: 3000,
automatic_tax: [Object],
billing_address_collection: null,
cancel_url: 'https://httpbin.org/post',
client_reference_id: null,
consent: null,
consent_collection: null,
currency: 'usd',
customer: 'cus_MIqeLZxAKTb6dd',
customer_creation: 'always',
customer_details: [Object],
customer_email: null,
expires_at: 1661416124,
livemode: false,
locale: null,
metadata: {},
mode: 'payment',
payment_intent: 'pi_3LaEx6CC4tqBCblr0NRo2FMH',
payment_link: null,
payment_method_collection: 'always',
payment_method_options: {},
payment_method_types: [Array],
payment_status: 'paid',
phone_number_collection: [Object],
recovered_from: null,
setup_intent: null,
shipping: null,
shipping_address_collection: null,
shipping_options: [],
shipping_rate: null,
status: 'complete',
submit_type: null,
subscription: null,
success_url: 'https://httpbin.org/post',
total_details: [Object],
url: null
}
},
livemode: false,
pending_webhooks: 2,
request: { id: null, idempotency_key: null },
type: 'checkout.session.completed'
}

#

still same error

clever socket
#

No need to keep pasting the event

#

Can you share the evt_xxx ID you're trying to parse

#

Nevermind, its right there 🤦‍♂️

#

So you're using the CLI, yes? Are you using the whsec_xxx value the CLI shares when you run stripe listen?

open hill
#

I am indeed yes

clever socket
#

Then my guess is Express is malforming the payload somehow. The constructEvent method expects the raw body, not JSON

open hill
#

Are there any other ways to change it from JSON to the raw body?

clever socket
#

No, once it's been parsed the method will fail. You need to figure out what in your code is malforming the body

open hill
#

Could it be where I have put router.use(bodyParser.json()) at the top of the code

forest grail
#

Yes that's exactly it. Since it's middleware the payload is getting JSON formatted before it reaches your webhook code. You should configure your express server to only parse the body as JSON if it's not hitting your webhook endpoint. Here's an example of how to do it: https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/node-express/express.js#L10-L16

GitHub

Node.js library for the Stripe API. . Contribute to stripe/stripe-node development by creating an account on GitHub.

open hill
#

Thanks, I'll see if that works.

#

It is still giving me the same error, and is still JSON.

#

I have replaced it as shown to: ```js
router.use((req, res, next) => {
if (req.originalUrl === '/api/purchase/webhook') {
next();
} else {
express.json()(req, res, next);
}
});

forest grail
#

Do you still have the router.use(bodyParser.json()) line in your code? You'll need to take that out

open hill
#

I do not

forest grail
#

Then it sounds like something else is causing your request body to be parsed as JSON, you might want to add some debug statements to see what's going on

open hill
#

I also have js router.use(bodyParser.urlencoded({ extended: false })) Im not sure if that would do anything.

forest grail
#

it does, that parses the body

#

Stripe webhooks require the body to be raw and unparsed

open hill
#

Should I just remove that?

clever socket
open hill
#

I have and still won't work, I have completely created a new router

#

there is nothing but express.raw on the webhook

merry portal
#

can you share the full code you have right now?

open hill
#
const router = require("express").Router();
const express = require("express")
const Joi = require("joi");
const bcrypt = require("bcryptjs");
const config = require("../../config.json");
const {
    models
} = require("../database");
const userModel = require('../database/models/User')
const songModel = require('../database/models/Song')
const newsModel = require('../database/models/News')
const v2Sessions = require('../database/models/v2Sessions')
const notifModel = require('../database/models/Notification')
const passport = require("passport");
const checkAuth = require("../utils/checkAuth");
const {
    isLoggedIn
} = require("../utils/checkStaffAuth");
const fetch = require("node-fetch");

/* Not used idk why, add cat desc */
const axios = require('axios');
const ip = require('ip')
const database = require("../database");

/* Discord webhook */
const {
    Webhook,
    MessageBuilder
} = require('discord-webhook-node');
const hook = new Webhook("https://ptb.discord.com/api/webhooks/redacted");

/* MomentJS */
var moment = require('moment'); // require

/* Stripe */
const Stripe = require('stripe');
const stripe = Stripe('sk_test_redacted');

/* Cleaning tools <3 */
var sanitize = require("mongo-sanitize");


const model = Joi.object({
    username: Joi.string().max(16).required(),
    password: Joi.string().min(6).required(),
});

router.use(express.raw());
router.get("/purchase/:prod", async (req, res) => {
    const session = await stripe.checkout.sessions.create({
        line_items: [
            {
              // Provide the exact Price ID (for example, pr_1234) of the product you want to sell
              price: req.params.prod,
              quantity: 1,
            },
          ],
          mode: 'payment',
          success_url: `http://${req.hostname}:2005/purchase/success`,
          cancel_url: `http://${req.hostname}:2005/purchase/fail`,
    })
    console.log(session)
    res.redirect(session.url || "https://weareharmony.net")
})

router.post('/webhook', express.raw({type: 'application/json'}), async (request, response) => {
    let event;
    const payload = request.body
    const endpointSecret = 'whsec_redacted';
    try {
      event = await stripe.webhooks.constructEvent(payload, request.headers['stripe-signature'], endpointSecret);
    } catch (err) {
      return response.status(400).send(`Webhook Error: ${err.message}`), console.log(err.message)
    }

    if (event.type === 'checkout.session.completed') {
        const session = event.data.object;

        // Fulfill the purchase...
        console.log("Succeeded")
    }

    response.status(200);
});

module.exports = router
merry portal
#

thanks, will look later, it's busy right now

open hill
#

no worries

merry portal
#

in the mean time try to debug it yourself a bit

#

like print out what payload is exactly

#

and make sure you're using the right signing secret

open hill
#
{
  id: 'evt_1LaFmaCC4tqBCblrWyxwRytc',
  object: 'event',
  api_version: '2020-08-27',
  created: 1661332915,
  data: {
    object: {
      id: 'cs_test_a19O6zn5sKQIhX3llSmpPMD8H9603GtVnZVeZQgYYfuMcW31WyDJVTBoLa',
      object: 'checkout.session',
      after_expiration: null,
      allow_promotion_codes: null,
      amount_subtotal: 3000,
      amount_total: 3000,
      automatic_tax: [Object],
      billing_address_collection: null,
      cancel_url: 'https://httpbin.org/post',
      client_reference_id: null,
      consent: null,
      consent_collection: null,
      currency: 'usd',
      customer: 'cus_MIrVZG3uwpQwoP',
      customer_creation: 'always',
      customer_details: [Object],
      customer_email: null,
      expires_at: 1661419311,
      livemode: false,
      locale: null,
      metadata: {},
      mode: 'payment',
      payment_intent: 'pi_3LaFmWCC4tqBCblr1goGk2uG',
      payment_link: null,
      payment_method_collection: 'always',
      payment_method_options: {},
      payment_method_types: [Array],
      payment_status: 'paid',
      phone_number_collection: [Object],
      recovered_from: null,
      setup_intent: null,
      shipping: null,
      shipping_address_collection: null,
      shipping_options: [],
      shipping_rate: null,
      status: 'complete',
      submit_type: null,
      subscription: null,
      success_url: 'https://httpbin.org/post',
      total_details: [Object],
      url: null
    }
  },
  livemode: false,
  pending_webhooks: 2,
  request: { id: null, idempotency_key: null },
  type: 'checkout.session.completed'
}
``` its all json
#

and signing is correct, I have looked

#
t=1661333334,v1=d8b4e66fa0d13256ea94f29390db0c6ba8132a8d488c01253fef5d2a3ea03081,v0=6fa4ba9ac5ad01a8e2be27b45e870f73a62bf1d768a0b2f9478242210ee0121a```
clever socket
#

Can you remove router.use(express.raw()) and only call it in your /webhook endpoint

open hill
#

alright

#

didn't do anything

merry portal
#

customer_details: [Object], well that's wrong at least since it shows you don't have raw JSON

open hill
#

yeah

#

i am having issues with making it raw JSON

merry portal
open hill
#

alright

clever socket
#

I suspect that by assigning req.body to a variable (const payload = request.body) its being parsed there. You should instead try passing req.body directly to constructEvent (instead of the payload variable)

open hill
#

Yeah, I did think that too. I have now put it in constructEvent and it didn't do a single thing. Im gonna keep trying different ways and see how I get on

#

I have tried everything, I cloned the example project and got that working but then when I try and put it all into my project it doesn't work anymore

#

I have found a possible fix

#

give me a sec

#

nevermind, didn't work

merry portal
#

what is the last 6 characters of the whsec_xxx value your code is using? print it out from the code, and print the payload, and share with me those characters and the event ID evt_xxx.

open hill
#

yesssssssss it worksssss

#

i had multiple parsers in place

#

and they were messing everything up