#max!-webhooks
1 messages · Page 1 of 1 (latest)
just above the router.get
To be clear this is webhook signature signing: https://stripe.com/docs/webhooks/signatures
Can you log it in your /purchase/webhook route?
Is it the correct signing secret from your Dashboard/CLI?
It is indeed
I have logged everything, all looks good
I have also tried JSON.stringify on the payload
I have checked on the dashboard and its the same
{
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
You're using Express, right?
Yes
Can I recommend you try express.raw instead of bodyParser.raw:
router.post('/purchase/webhook', express.raw({type: 'application/json'}), async (request, response) => {})
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
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
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?
I am indeed yes
Then my guess is Express is malforming the payload somehow. The constructEvent method expects the raw body, not JSON
Are there any other ways to change it from JSON to the raw body?
No, once it's been parsed the method will fail. You need to figure out what in your code is malforming the body
Could it be where I have put router.use(bodyParser.json()) at the top of the code
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
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);
}
});
Do you still have the router.use(bodyParser.json()) line in your code? You'll need to take that out
I do not
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
I also have js router.use(bodyParser.urlencoded({ extended: false })) Im not sure if that would do anything.
it does, that parses the body
Stripe webhooks require the body to be raw and unparsed
Should I just remove that?
Just drop bodyParser completely and use express.raw() as per the docs: https://stripe.com/docs/webhooks/signatures#verify-official-libraries
I have and still won't work, I have completely created a new router
there is nothing but express.raw on the webhook
can you share the full code you have right now?
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
thanks, will look later, it's busy right now
no worries
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
{
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```
Can you remove router.use(express.raw()) and only call it in your /webhook endpoint
customer_details: [Object], well that's wrong at least since it shows you don't have raw JSON
I'd start by cloning the example project in our repo which does work (https://github.com/stripe/stripe-node/blob/master/examples/webhook-signing/node-express/), as a baseline and then adapting that to start adding your own code instead of the other way around.
alright
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)
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
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.