I'm currently testing the Stripe Webhook Endpoints for the Checkout Process.
I'm confused because Stripe shows two different snippets how to set up the Webhook endpoint in their docs.
In the Checkout Doc they show this code snippet:
const stripe = require('stripe')('sk_test_...');
// Find your endpoint's secret in your Dashboard's webhook settings
const endpointSecret = 'whsec_...';
// Using Express
const app = require('express')();
// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
// Fulfill the purchase...
handleCheckoutSession(session);
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(8000, () => console.log('Running on port 8000'));
And in their Webhook Docs they are showing this snippet:
const app = require('express')();
// Use body-parser to retrieve the raw body as a buffer
const bodyParser = require('body-parser');
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
let event;
try {
event = JSON.parse(request.body);
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
// Then define and call a method to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
break;
// ... handle other event types
default:
// Unexpected event type
return response.status(400).end();
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(8000, () => console.log('Running on port 8000'));
I tried both snippets but nothing seems to work for me.
The first one gives me the StripeSignatureVerificationError when I try to constructEvent(...)
and the second one is telling me that the Object event is undefined.
Does someone know why both of these endpoints are not working for me ?
Before using JSON Body parser configure to receive RAW body
app.use(bodyParser.raw({type: "*/*"})) <-- This line need to be added
app.use(bodyParser.json())
More discussion https://github.com/stripe/stripe-node/issues/331
Related
I have this error when I run stripe listen --forward-to localhost:3000/webhook on cmd and server on hyperterminal.
[stripe listen --forward-to localhost:3000/webhook][1]
I basically copied and pasted from the document so I'm not sure what is the problem
[1]: https://i.stack.imgur.com/iosZa.png
[2]: https://i.stack.imgur.com/Wk0xY.png
const stripe = require("stripe")(process.env.STRIPE_PRIVATE_KEY);
const bodyParser = require("body-parser");
const app = express();
app.post('/webhook', bodyParser.raw({type: 'application/json'}), (request, response) => {
const payload = request.body;
console.log("Got payload: " + payload);
response.status(200);
});
The issue is that response.status(200) does not actually send the response. For that you need to use sendStatus(200) (docs) or follow your status(200) with a send() (see docs).
Also this previous answer, related: https://stackoverflow.com/a/38621009/12474862
I have been following a tutorial on setting up REST APIs in Node, using Express for an app that accesses an existing MariaDB database. My version only needs to read data and I have the DB co-located with the Node application (same host).
My goal for this entry-level example is to just access the data, using static SQL, so I can see it rendered in the web page by the JSON pritifier.
[Next, I want to present the data in a table (EJS?). Later, when I can get that to work, I'll add form controls (React?) to let a user specify start and end date bounds for the SQL query. Finally I'll aim to render the data as a line graph (D3js).]
The tutorial runs the web server successfully (it returns 'OK' on the base URL), but when I go to URL/solarData it tries an async function to getMultiple rows from the DB, it responds:
Bind parameters must not contain undefined. To pass SQL NULL specify JS null TypeError: Bind parameters must not contain undefined. To pass SQL NULL specify JS null
at /SunnyData/solarViz/node_modules/mysql2/lib/connection.js:628:17
at Array.forEach (<anonymous>)
at Connection.execute (/SunnyData/solarViz/node_modules/mysql2/lib/connection.js:620:22)
at /SunnyData/solarViz/node_modules/mysql2/promise.js:120:11
at new Promise (<anonymous>)
at PromiseConnection.execute (/SunnyData/solarViz/node_modules/mysql2/promise.js:117:12)
at Object.query (/SunnyData/solarViz/services/db.js:6:40)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Object.getMultiple (/SunnyData/solarViz/services/solarData.js:7:16)
at async /SunnyData/solarViz/routes/solarData.js:8:14
app.js:61
./app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3800;
const solarDataRouter = require('./routes/solarData');
app.use(express.json());
app.use(
express.urlencoded({
extended: true,
})
);
app.get('/', (req, res) => {
res.json({'message': 'ok'});
})
app.use('/solarData', solarDataRouter);
/* Error handler middleware */
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
console.error(err.message, err.stack);
res.status(statusCode).json({'message': err.message});
return;
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
});
./routes/solarData.js
const express = require('express');
const router = express.Router();
const solarData = require('../services/solarData');
/* GET solar data. */
router.get('/', async function(req, res, next) {
try {
res.json(await solarData.getMultiple(req.query.page));
} catch (err) {
console.error(`Error while getting solar data `, err.message);
next(err);
}
});
module.exports = router;
./config.js
const env = process.env;
const config = {
db: {
host: env.SUNNY_HOST,
user: env.SUNNY_USER,
password: env.SUNNY_PW,
database: env.SUNNY_DB,
},
listPerPage: env.LIST_PER_PAGE,
};
module.exports = config;
./services/solarData.js
const db = require('./db');
const helper = require('../helper');
const config = require('../config');
async function getMultiple(page = 1){
const offset = helper.getOffset(page, config.listPerPage);
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
const data = helper.emptyOrRows(rows);
const meta = {page};
return {
data,
meta
}
}
module.exports.getMultiple = getMultiple;
./services/db.js
const mysql = require('mysql2/promise');
const config = require('../config');
async function query(sql, params) {
const connection = await mysql.createConnection(config.db);
const [results, ] = await connection.execute(sql, params);
return results;
}
module.exports = {
query
}
I've left out the ./helper.js
Everything runs fine until I direct the webpage to /solarData. At that point I get the Debug Console (vscode) mentioned up-front
Searching seems to point at a mysql2 shortcoming/bug but not at a practical solution
If you respond, please describe the 'bind' mechanism, as I'm not sure what's going on.
Hope I've put enough info in. Please ask if I need to add anything else.
The error says
Bind parameters must not contain undefined.
It means that in the file ./services/solarData.js on the line
const rows = await db.query(
`SELECT * FROM DTP LIMIT ?,?`, [offset, config.listPerPage]
);
Some of the 2 variables is undefined, you need to check offset and config.listPerPage to be defined.
Just use
console.log('offset: ' + offset)
console.log('listPerPage: ' + config.listPerPage)
and you will find out what is undefined in your case
I'm making a post request using axios and passing in a body like so:
export const uploadFeatured = (userId, uploadInfo) => async dispatch => {
////console.log("uploading", uploadInfo.mediaName, uploadInfo.video, uploadInfo.description);
const res = await axios.post(domain + '/api/uploadFeatured',
{mediaName: uploadInfo.mediaName,
video: uploadInfo.video,
description: uploadInfo.description});
console.log("response to upload", res)
}
However, at the server, I'm getting an undefined when accessing req.body.
app.post("/api/uploadFeatured", async (req, res) => {
try {
//////// req.body returning undefined.
console.log("upload featured is ", req.body)
const data = {name:"Name"}
const newFeatured = new Featured(data).save();
const client = algoliasearch('YD', '055b10');
const index = client.initIndex('Humboi');
index.saveObjects([data], {
autoGenerateObjectIDIfNotExist: true
}).then(({ objectIDs }) => {
console.log(objectIDs);
});
console.log("new featured is ", newFeatured);
} catch (e) {
console.log("error ", e)
}
});
What am I doing that's causing the body to be undefined in the node.js server rather than to be the map that's passed in axios?
Please install body-parser add following code in your js file after const path:
npm install body-parser
const bodyParser = require('body-parser')
app.use(bodyParser);
The bodyParser object exposes various factories to create middlewares. All middlewares will populate the req.body property with the parsed body when the Content-Type request header matches the type option, or an empty object ({}) if there was no body to parse, the Content-Type was not matched, or an error occurred.
The code below should send the message "TwilioQuest rules", via twilio webhook using ngrok so that it can be accessed via the internet https://765ec92dsf.ngrok.io/sms to send messages. But when the code is executed it always displays response 404. And I don't know where the error lies, because the console.log doesn't display anything.
My Code
const http = require('http');
const express = require('express');
const { urlencoded } = require('body-parser');
const ngrok = require('ngrok');
const twilio = require('twilio');
// Set up our express web application
const PORT = 8767;
const app = express();
app.use(urlencoded({ extended: false }));
// Create a route to handle incoming SMS messages
// This is where the magic happens!
app.post('/sms', (request, response) => {
console.log(
`Incoming message from ${request.body.From}: ${request.body.Body}`
);
response.type('text/xml');
response.send(`
<Response>
<Message>TwilioQuest rules</Message>
</Response>
`);
});
// Create and run an HTTP server which can handle incoming requests
const server = http.createServer(app);
server.listen(PORT, () =>
console.log(`Express server listening on localhost:${PORT}`)
);
// -----------------------------------------------------------------------------
// This code sets up a tool called ngrok to let Twilio talk to the app running
// on your computer. It then uses the Twilio REST API to direct all incoming
// SMS messages to your local app. You should not have to edit any of this
// code below.
// -----------------------------------------------------------------------------
(async function() {
try {
await ngrok.disconnect();
await ngrok.kill();
let url = await ngrok.connect(PORT);
console.log('ngrok forwarding configured - your app is live at ', url);
let client = twilio(
process.env.TQ_TWILIO_ACCOUNT_SID,
process.env.TQ_TWILIO_AUTH_TOKEN
);
let ngrokUrl = `${url}/sms`;
let number = await client
.incomingPhoneNumbers(process.env.TQ_TWILIO_NUMBER_SID)
.update({
smsUrl: ngrokUrl,
smsMethod: 'POST',
});
console.log(
`${number.phoneNumber} configured to send incoming SMS to ${ngrokUrl}`
);
console.log(
`Send a message to ${
number.phoneNumber
} and check the reply you get back!`
);
} catch (e) {
console.log('There was an error configuring incoming SMS:');
console.log(e);
}
})();
Response
Cannot GET /sms
Thanks
How to validate webbooks signature using express.js?
In docs, there is a section about notification signatures but I don't know how to combine it with Express.js
This question is a migrated from official Kentico Cloud Forum, that would be deleted.
In the API reference, there is a sample describing webhook validation in various languages including node.js.
If you want to use express.js you could start with this template code:
const express = require('express');
const crypto = require('crypto');
// Create a new instance of express
const app = express();
// Set up a raw bodyparser to read the webhook post
const bodyParserRaw = require('body-parser').raw({
type: '*/*',
});
function webhookValidator (req, res, next) {
// get the header signature from the webhook request
const givenSignature = req.headers['x-kc-signature'];
// throw error if it's missing
if (!givenSignature) {
console.log('Missing signature');
return res.status(409).json({
error: 'Missing signature'
});
}
// create HMAC from the raw request body
let hmac = crypto.createHmac('sha256', [your-webhook-secret-key]);
hmac.write(req.body);
hmac.end();
// get a base64 hash from HMAC
let hash = hmac.read().toString('base64');
// check validity with timingSafeEqual
let webhookValid = false;
try {
webhookValid = crypto.timingSafeEqual(Buffer.from(givenSignature, 'base64'), Buffer.from(hash, 'base64'));
} catch (e) {
webhookValid = false
}
// return validity
if (webhookValid) {
return next();
} else {
console.log('Invalid signature');
return res.status(409).json({
error: 'Invalid signature'
});
}
}
// create a route and pass through the bodyparser and validator
app.post('/webhook', bodyParserRaw, webhookValidator, ( req, res, next ) => {
// If execution gets here, the HMAC is valid
console.log('webhook is valid');
});
EDIT
You can use the Kontent webhook helper library to quickly verify the webhook notifications and their signatures. The library is available as #kentico/kontent-webhook-helper npm package and helps you avoid common problems when calculating the hash.