Properly handle stripe hook with sig verification

master
Mark Moffat 2019-11-07 20:13:38 +10:30
parent e2e0fa2a00
commit bf6c35ea50
6 changed files with 46 additions and 14 deletions

10
app.js
View File

@ -272,7 +272,6 @@ app.enable('trust proxy');
app.use(helmet()); app.use(helmet());
app.set('port', process.env.PORT || 1111); app.set('port', process.env.PORT || 1111);
app.use(logger('dev')); app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser(config.secretCookie)); app.use(cookieParser(config.secretCookie));
app.use(session({ app.use(session({
@ -287,6 +286,15 @@ app.use(session({
store: store store: store
})); }));
app.use(bodyParser.json({
// Only on Stripe URL's which need the rawBody
verify: (req, res, buf) => {
if(req.originalUrl === '/stripe/subscription_update'){
req.rawBody = buf.toString();
}
}
}));
// Set locales from session // Set locales from session
app.use(i18n.init); app.use(i18n.init);

View File

@ -3,5 +3,6 @@
"publicKey": "pk_test_this_is_not_real", "publicKey": "pk_test_this_is_not_real",
"stripeCurrency": "usd", "stripeCurrency": "usd",
"stripeDescription": "expressCart payment", "stripeDescription": "expressCart payment",
"stripeLogoURL": "http://localhost:1111/images/stripelogo.png" "stripeLogoURL": "http://localhost:1111/images/stripelogo.png",
"stripeWebhookSecret": ""
} }

View File

@ -16,6 +16,9 @@
"stripeLogoURL": { "stripeLogoURL": {
"type": "string", "type": "string",
"format": "uri-template" "format": "uri-template"
},
"stripeWebhookSecret": {
"type": "string"
} }
}, },
"required": [ "required": [

20
package-lock.json generated
View File

@ -7614,7 +7614,8 @@
"qs": { "qs": {
"version": "6.5.2", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
"dev": true
}, },
"quick-lru": { "quick-lru": {
"version": "1.1.0", "version": "1.1.0",
@ -8726,13 +8727,18 @@
"dev": true "dev": true
}, },
"stripe": { "stripe": {
"version": "5.10.0", "version": "7.12.0",
"resolved": "https://registry.npmjs.org/stripe/-/stripe-5.10.0.tgz", "resolved": "https://registry.npmjs.org/stripe/-/stripe-7.12.0.tgz",
"integrity": "sha512-AUDmXfNAAY/oOfW87HPO4bDzNWJp8iQd0blVWwwEgPxO1DmEC//foI0C9rhr2ZNsuF6kLypPfNtGB9Uf+RCQzQ==", "integrity": "sha512-h/NMB7E+0WgDuEOdfrS9giYmTfQRvOoKHdYaKzo9V0hxilXopVJd3ZZQ47193rAOHjIhmuCDtQRb3gEEm24gKg==",
"requires": { "requires": {
"lodash.isplainobject": "^4.0.6", "qs": "^6.6.0"
"qs": "~6.5.1", },
"safe-buffer": "^5.1.1" "dependencies": {
"qs": {
"version": "6.9.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz",
"integrity": "sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA=="
}
} }
}, },
"superagent": { "superagent": {

View File

@ -50,7 +50,7 @@
"sanitize-html": "^1.20.1", "sanitize-html": "^1.20.1",
"sitemap": "^1.6.0", "sitemap": "^1.6.0",
"strip-bom": "^3.0.0", "strip-bom": "^3.0.0",
"stripe": "^5.10.0", "stripe": "^7.12.0",
"uglifycss": "0.0.27" "uglifycss": "0.0.27"
}, },
"devDependencies": { "devDependencies": {

View File

@ -115,13 +115,27 @@ router.post('/checkout_action', (req, res, next) => {
// Subscription hook from Stripe // Subscription hook from Stripe
router.all('/subscription_update', async (req, res, next) => { router.all('/subscription_update', async (req, res, next) => {
const db = req.app.db; const db = req.app.db;
const stripeSigSecret = common.getPaymentConfig().stripeWebhookSecret;
const stripeSig = req.headers['stripe-signature'];
if(!req.body.data.object.customer){ let hook;
if(stripeSigSecret){
try{
hook = await stripe.webhooks.constructEvent(req.rawBody, stripeSig, stripeSigSecret);
console.info('Stripe Webhook received');
}catch(err){
return res.status(400).send(`Webhook Error: ${err.message}`);
}
if(!hook.data.object.customer){
return res.status(400).json({ message: 'Customer not found' }); return res.status(400).json({ message: 'Customer not found' });
} }
}else{
hook = req.body;
}
const order = await db.orders.findOne({ const order = await db.orders.findOne({
orderCustomer: req.body.data.object.customer, orderCustomer: hook.data.object.customer,
orderType: 'Subscription' orderType: 'Subscription'
}); });
@ -130,7 +144,7 @@ router.all('/subscription_update', async (req, res, next) => {
} }
let orderStatus = 'Paid'; let orderStatus = 'Paid';
if(req.body.type === 'invoice.payment_failed'){ if(hook.type === 'invoice.payment_failed'){
orderStatus = 'Declined'; orderStatus = 'Declined';
} }