mirror of
https://github.com/koodiklinikka/koodiklinikka.fi-api.git
synced 2026-01-26 03:34:03 +00:00
Membership registration & payment API (#4)
* remove newrelic from use in devenv * add endpoint for membership payments * fix some wierd spaces * minor code styling and logging stuff * replace non-breaking spaces with normal ones * remove duplicate function * minor code styling * add functionality for writing new member to google sheets * add config example * update example config, start using config in google credentials * remove var creds from google sheets auth * rename config.example to config.template and fix readme * add async and google-spreadsheet packages * rename workingWithRows to addRow * return missing header from readme * minor code styling * flatten google config structure, add address fields * add request validation to membership endpoint * fix config field names * more error handling, fix indentation
This commit is contained in:
committed by
Riku Rouvila
parent
181a48c0f6
commit
f783a27045
@@ -1,5 +1,32 @@
|
||||
{
|
||||
"all": {
|
||||
"development": {
|
||||
"stripe": {
|
||||
"secretKey": ""
|
||||
},
|
||||
"slack": {
|
||||
"token": "",
|
||||
"privateChannel": "",
|
||||
"publicChannel": ""
|
||||
},
|
||||
"github": {
|
||||
"token": ""
|
||||
},
|
||||
"twitter": {
|
||||
"consumerKey": "",
|
||||
"consumerSecret": "",
|
||||
"token": "",
|
||||
"tokenSecret": ""
|
||||
},
|
||||
"google": {
|
||||
"spreadsheetId": "",
|
||||
"clientEmail": "",
|
||||
"privateKey": ""
|
||||
}
|
||||
},
|
||||
"production": {
|
||||
"stripe": {
|
||||
"secretKey": ""
|
||||
},
|
||||
"slack": {
|
||||
"token": "",
|
||||
"privateChannel": "",
|
||||
@@ -14,5 +41,7 @@
|
||||
"token": "",
|
||||
"tokenSecret": ""
|
||||
}
|
||||
},
|
||||
"all": {
|
||||
}
|
||||
}
|
||||
|
||||
10
index.js
10
index.js
@@ -1,4 +1,3 @@
|
||||
require('newrelic');
|
||||
'use strict';
|
||||
|
||||
var express = require('express');
|
||||
@@ -7,9 +6,13 @@ var cors = require('cors');
|
||||
var bodyParser = require('body-parser');
|
||||
var app = express();
|
||||
|
||||
if(app.get('env') != 'development') {
|
||||
require('newrelic');
|
||||
}
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({extended: true}));
|
||||
app.use(cors());
|
||||
app.use(cors({credentials: true, origin: true}));
|
||||
|
||||
morgan.token('body', function(req) {
|
||||
return JSON.stringify(req.body);
|
||||
@@ -20,8 +23,9 @@ app.use(morgan(':method :url :status :response-time ms - :res[content-length] :b
|
||||
require('./routes/invite')(app);
|
||||
require('./routes/members')(app);
|
||||
require('./routes/feeds')(app);
|
||||
require('./routes/membership')(app);
|
||||
|
||||
app.use(function(err, req, res, next) {
|
||||
app.use(function(err, req, res, next) {
|
||||
/*jshint unused:false*/
|
||||
console.error(err);
|
||||
res.status(500).send('Internal server error');
|
||||
|
||||
@@ -19,14 +19,19 @@
|
||||
"homepage": "https://github.com/koodiklinikka/koodiklinikka.fi-api",
|
||||
"dependencies": {
|
||||
"apicache": "0.0.12",
|
||||
"async": "^2.5.0",
|
||||
"bluebird": "^2.9.3",
|
||||
"body-parser": "^1.10.1",
|
||||
"cors": "^2.7.1",
|
||||
"express": "^4.11.0",
|
||||
"google-spreadsheet": "^2.0.4",
|
||||
"joi": "^10.6.0",
|
||||
"lodash": "^3.10.1",
|
||||
"moment": "^2.18.1",
|
||||
"morgan": "^1.5.1",
|
||||
"newrelic": "^1.18.0",
|
||||
"node-twitter": "0.5.2",
|
||||
"stripe": "^4.23.1",
|
||||
"superagent": "^0.21.0",
|
||||
"validator": "^3.27.0"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use strict';
|
||||
|
||||
var cache = require('apicache').middleware;
|
||||
var cache = require('apicache').middleware;
|
||||
var Promise = require('bluebird');
|
||||
var twitter = require('../services/twitter');
|
||||
var github = require('../services/github');
|
||||
var github = require('../services/github');
|
||||
|
||||
module.exports = function (app) {
|
||||
/*
|
||||
* GET /feeds
|
||||
* Endpoint for fetching different information feeds (Twitter, GitHub etc.)
|
||||
*/
|
||||
app.get('/feeds', cache('10 minutes'), function(req, res, next) {
|
||||
app.get('/feeds', cache('10 minutes'), function(req, res, next) {
|
||||
Promise.props({
|
||||
twitter: twitter.getTweets(40),
|
||||
github: github.getEvents(40)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var validator = require('validator');
|
||||
var slack = require('../services/slack');
|
||||
var github = require('../services/github');
|
||||
var slack = require('../services/slack');
|
||||
var github = require('../services/github');
|
||||
|
||||
module.exports = function (app) {
|
||||
/*
|
||||
@@ -10,13 +10,13 @@ module.exports = function (app) {
|
||||
* Endpoint for sending invitations automatically
|
||||
*/
|
||||
|
||||
app.post('/invites', function(req, res, next) {
|
||||
app.post('/invites', function(req, res, next) {
|
||||
|
||||
if(!validator.isEmail(req.body.email)) {
|
||||
return res.status(400).send('invalid_email');
|
||||
}
|
||||
|
||||
function success() {
|
||||
function success() {
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ module.exports = function (app) {
|
||||
var message = 'User ' + user.login + ' invited to GitHub organization.'
|
||||
slack.createMessage(message);
|
||||
})
|
||||
.catch(function(err) {
|
||||
.catch(function(err) {
|
||||
var message = 'Creating GitHub invitation failed for: ' + req.body.email + ' reason: ' + err;
|
||||
slack.createMessage(message);
|
||||
});
|
||||
|
||||
@@ -8,8 +8,7 @@ module.exports = function (app) {
|
||||
* GET /members
|
||||
* Endpoint for fetching GitHub org public members
|
||||
*/
|
||||
|
||||
app.get('/members', cache('3 hours'), function(req, res, next) {
|
||||
app.get('/members', cache('3 hours'), function(req, res, next) {
|
||||
github.getMembers().then(function(data) {
|
||||
res.status(200).send(data);
|
||||
}, function(error) {
|
||||
@@ -21,8 +20,7 @@ module.exports = function (app) {
|
||||
* Post /members
|
||||
* Endpoint for getting an invite to GitHub organization
|
||||
*/
|
||||
|
||||
app.post('/members', function(req, res, next) {
|
||||
app.post('/members', function(req, res, next) {
|
||||
if(!req.body.username) {
|
||||
return res.status(400).send('invalid_username');
|
||||
}
|
||||
|
||||
103
routes/membership.js
Normal file
103
routes/membership.js
Normal file
@@ -0,0 +1,103 @@
|
||||
'use strict';
|
||||
|
||||
var Promise = require('bluebird');
|
||||
var GoogleSpreadsheet = require('google-spreadsheet');
|
||||
var async = require('async');
|
||||
var moment = require('moment');
|
||||
var Joi = require('joi');
|
||||
|
||||
var slack = require('../services/slack');
|
||||
var config = require('../lib/config');
|
||||
var stripe = require('stripe')(config.stripe.secretKey);
|
||||
var validateRequest = require('../utils/validateRequest');
|
||||
|
||||
function log(message) {
|
||||
console.log(message);
|
||||
slack.createMessage(message);
|
||||
}
|
||||
|
||||
function addNewMemberToSheets(data, callback) {
|
||||
var {name, email, address, postcode, city, handle} = data;
|
||||
var doc = new GoogleSpreadsheet(config.google.spreadsheetId);
|
||||
|
||||
async.waterfall([
|
||||
function setAuth(cb) {
|
||||
console.log('Start Google Spreadsheed auth.');
|
||||
doc.useServiceAccountAuth({
|
||||
client_email: config.google.clientEmail,
|
||||
private_key: config.google.privateKey
|
||||
}, (err) => cb(err));
|
||||
},
|
||||
function getInfoAndWorksheets(cb) {
|
||||
console.log('Start Google Spreadsheet info fetch.');
|
||||
doc.getInfo(function(err, info) {
|
||||
if(err) {
|
||||
cb(err);
|
||||
} else {
|
||||
cb(null, info.worksheets[0]);
|
||||
}
|
||||
});
|
||||
},
|
||||
function addRow(sheet, cb) {
|
||||
console.log('Start Google Spreadsheet row write.');
|
||||
sheet.addRow({
|
||||
'jäsenmaksu': true,
|
||||
'koko nimi': name,
|
||||
'liittymispäivä': moment().format('DD.MM.YYYY'),
|
||||
'lisääjä': 'Koodiklinikka.fi-api',
|
||||
'katuosoite': address,
|
||||
'postinumero': postcode,
|
||||
'paikkakunta': city,
|
||||
'slack': handle,
|
||||
'sähköposti': email
|
||||
}, cb);
|
||||
}
|
||||
], callback);
|
||||
}
|
||||
|
||||
module.exports = function (app) {
|
||||
/*
|
||||
* POST /membership
|
||||
* Endpoint for adding a new member to the association
|
||||
*/
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
userInfo: Joi.object().keys({
|
||||
name: Joi.string().required(),
|
||||
email: Joi.string().email().required(),
|
||||
handle: Joi.string().required(),
|
||||
address: Joi.string().required(),
|
||||
city: Joi.string().required(),
|
||||
postcode: Joi.string().required()
|
||||
}),
|
||||
stripeToken: Joi.string().required()
|
||||
})
|
||||
|
||||
app.post('/membership', validateRequest(schema), function(req, res, next) {
|
||||
|
||||
console.log(`Start membership addition with body: ${JSON.stringify(req.body)}`);
|
||||
|
||||
stripe.charges.create({
|
||||
amount: config.membership.price,
|
||||
card: req.body.stripeToken,
|
||||
currency: 'eur',
|
||||
description: `Koodiklinikka ry jäsenyys: ${req.body.name}`
|
||||
}, function(err, charge) {
|
||||
if (err) {
|
||||
log(`Membership payment FAILED for: ${JSON.stringify(req.body)}. Reason: ${err.message}`);
|
||||
res.status(500).send('payment_error');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`Membership payment SUCCESSFUL for: ${JSON.stringify(req.body)}`);
|
||||
addNewMemberToSheets(req.body.userInfo, (err) => {
|
||||
if(err) {
|
||||
log(`Storing membership info FAILED for: ${JSON.stringify(req.body)}. Reason: ${err.message}`);
|
||||
res.status(500).send('membership_storage_error');
|
||||
return;
|
||||
}
|
||||
res.status(200).send('payment_success');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
12
utils/validateRequest.js
Normal file
12
utils/validateRequest.js
Normal file
@@ -0,0 +1,12 @@
|
||||
var Joi = require('joi');
|
||||
|
||||
module.exports = function validateRequest(schema) {
|
||||
return function handler(req, res, next) {
|
||||
Joi.validate(req.body, schema, function (err, value) {
|
||||
if(err) {
|
||||
return res.status(400).send(err.details)
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user