summaryrefslogtreecommitdiff
path: root/server/lib
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2015-01-28 11:49:10 -0500
committerJules Laplace <jules@okfoc.us>2015-01-28 11:49:10 -0500
commite3ff5315f7ea7421431658077253c4d71f0f5731 (patch)
tree62a06e26c77ca9454cc0535f39da698319713eac /server/lib
parent79fee7f24d43873fc35295eab1d2a089d373e133 (diff)
parent3059c3203d2cec4e2e745be8c21c6d3fbddb0c14 (diff)
Merge branch 'subscriptions' of github.com:okfocus/vvalls into subscriptions
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/api/index.js1
-rw-r--r--server/lib/api/profile.js1
-rw-r--r--server/lib/api/subscription.js47
-rw-r--r--server/lib/schemas/Plan.js2
-rw-r--r--server/lib/schemas/Subscription.js15
-rw-r--r--server/lib/schemas/User.js6
-rw-r--r--server/lib/views/index.js1
-rw-r--r--server/lib/views/staff.js102
-rw-r--r--server/lib/webhook/index.js44
-rw-r--r--server/lib/webhook/recurly-config.js6
-rw-r--r--server/lib/webhook/webhook.js154
11 files changed, 368 insertions, 11 deletions
diff --git a/server/lib/api/index.js b/server/lib/api/index.js
index 11e13fc..9478d9b 100644
--- a/server/lib/api/index.js
+++ b/server/lib/api/index.js
@@ -8,6 +8,7 @@ var api = {
projects: require('./projects'),
rooms: require('./rooms'),
collaborator: require('./collaborator'),
+ subscription: require('./subscription'),
}
module.exports = api
diff --git a/server/lib/api/profile.js b/server/lib/api/profile.js
index 996505f..d72a2c3 100644
--- a/server/lib/api/profile.js
+++ b/server/lib/api/profile.js
@@ -32,6 +32,7 @@ var profile = {
delete data.old_password
delete data.new_password
delete data.isStaff
+ delete data.plan_level
data.updated_at = new Date ()
if (req.files.avatar) {
diff --git a/server/lib/api/subscription.js b/server/lib/api/subscription.js
new file mode 100644
index 0000000..83644cf
--- /dev/null
+++ b/server/lib/api/subscription.js
@@ -0,0 +1,47 @@
+/* jshint node: true */
+
+var _ = require('lodash'),
+ util = require('../util'),
+ upload = require('../upload'),
+ config = require('../../../config.json'),
+ User = require('../schemas/User'),
+ Project = require('../schemas/Project'),
+ Layout = require('../schemas/Layout'),
+ Subscription = require('../schemas/Subscription');
+
+var subscription = module.exports = {
+
+/*
+ index: function(req, res){
+ Project.find({ user_id: req.user._id }, function(err, docs){
+ res.json(docs)
+ })
+ },
+*/
+ middleware: {
+ fetchAccount: function(req, res, next){
+ recurly.subscriptions.listByAccount(req.user._id, function(data){
+ })
+ },
+ },
+
+ // synchronise an account with recurly..
+ // useful when testing locally (if webhooks do not fire)
+ sync: function(req, res){
+ // fetch req.user._id
+ },
+
+ show: function(req, res){
+ // fetch from recurly
+ },
+
+ update: function(req, res){
+ // update plan_type on recurly
+ // update add_ons on recurly
+ },
+
+ destroy: function(req, res){
+ // destroy on recurly
+ },
+
+}; \ No newline at end of file
diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js
index 1208672..8a19b99 100644
--- a/server/lib/schemas/Plan.js
+++ b/server/lib/schemas/Plan.js
@@ -10,6 +10,8 @@ var PlanSchema = new mongoose.Schema({
name: { type: String },
slug: { type: String },
+ level: { type: Number },
+
monthly_price: { type: Number },
yearly_price: { type: Number },
diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js
index 8ec557d..b766555 100644
--- a/server/lib/schemas/Subscription.js
+++ b/server/lib/schemas/Subscription.js
@@ -8,13 +8,14 @@ var mongoose = require('mongoose'),
var SubscriptionSchema = new mongoose.Schema({
user_id: { type: mongoose.Schema.ObjectId, index: true },
-
- monthly_total: { type: Number },
- yearly_total: { type: Number },
-
- plans: [{
- tier: { type: String },
- monthly: { type: Boolean },
+
+ plan_type: { type: String, default: "free" },
+ plan_period: { type: String, default: "monthly" },
+
+ subscription_uuid: { type: String },
+ subscription_add_ons: [{
+ name: { type: String },
+ quantity: { type: Number },
}],
history: [{
diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js
index 19b5ede..4b6ff39 100644
--- a/server/lib/schemas/User.js
+++ b/server/lib/schemas/User.js
@@ -54,7 +54,11 @@ var UserSchema = new mongoose.Schema({
type: String,
default: "",
},
-
+
+ plan_level: { type: Number, default: 0 },
+ plan_type: { type: String, default: "free" },
+ last_charged: { type: Date, default: null },
+
location: { type: String, default: "" },
photo: { type: String, default: "" },
bio: { type: String, default: "" },
diff --git a/server/lib/views/index.js b/server/lib/views/index.js
index 2a8f921..0ce0357 100644
--- a/server/lib/views/index.js
+++ b/server/lib/views/index.js
@@ -22,7 +22,6 @@ marked.setOptions({
var views = module.exports = {
staff: require('./staff'),
- subscription: require('./subscription'),
editor_new: function (req, res) {
if (! req.user) {
diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js
index 97ecde6..74dd7cd 100644
--- a/server/lib/views/staff.js
+++ b/server/lib/views/staff.js
@@ -124,7 +124,35 @@ var staff = module.exports = {
next()
})
},
-
+
+ ensureSubscriptions: function(req, res, next){
+ var paginationInfo = res.locals.pagination = {}
+ var criteria = req.criteria || {}
+ var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 )
+ var offset = paginationInfo.offset = Number(req.query.offset) || 0
+ var sort
+ paginationInfo.sort = req.query.sort
+ paginationInfo.sortOptions = ["date", "name"]
+ switch (req.query.sort) {
+ case 'created':
+ sort = {'created_at': -1}
+ break
+ default:
+ case 'date':
+ sort = {'updated_at': -1}
+ break
+ }
+ Subscription.find(criteria)
+ .select(staff.fields.project)
+ .sort(sort)
+ .skip(offset)
+ .limit(limit)
+ .exec(function (err, subscriptions) {
+ res.locals.subscriptions = subscriptions.map(staff.helpers.subscription)
+ next()
+ })
+ },
+
ensurePlans: function(req, res, next){
Plan.find(function (err, plans) {
res.locals.plans = (plans || []).map(staff.helpers.plan)
@@ -150,6 +178,24 @@ var staff = module.exports = {
}
},
+ ensureSubscription: function (req, res, next) {
+ if (req.params.id) {
+ Subscription.findOne({ _id: req.params.id }, function(err, subscription){
+ if (err || ! subscription) {
+ console.error(err)
+ res.redirect("/staff/subscriptions/")
+ }
+ else {
+ req.subscription = subscription
+ next()
+ }
+ })
+ }
+ else {
+ res.redirect("/staff/subscriptions/")
+ }
+ },
+
ensureRecentProjects: function(req, res, next){
var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } }
staff.middleware.ensureProjects(dreq, res, next)
@@ -160,11 +206,23 @@ var staff = module.exports = {
staff.middleware.ensureObjectsUsers(res.locals.projects, next)
},
+ ensureSubscriptionsUsers: function(req, res, next){
+ if (! res.locals.subscriptions || ! res.locals.subscriptions.length) { return next() }
+ staff.middleware.ensureObjectsUsers(res.locals.subscriptions, next)
+ },
+
ensureMediaUsers: function(req, res, next){
if (! res.locals.media || ! res.locals.media.length) { return next() }
staff.middleware.ensureObjectsUsers(res.locals.media, next)
},
+ ensureSubscriptionUser: function(req, res, next){
+ if (! res.locals.subscription) { return next() }
+ staff.middleware.ensureObjectsUsers([ res.locals.subscription ], function(){
+ next()
+ })
+ },
+
ensureMediaUser: function(req, res, next){
if (! res.locals.media) { return next() }
staff.middleware.ensureObjectsUsers([ res.locals.media ], function(){
@@ -338,6 +396,13 @@ var staff = module.exports = {
plan.user = {}
return plan
},
+
+ subscription: function(subscription){
+ subscription = subscription.toObject()
+ subscription.date = moment( subscription.updated_at || subscription.created_at ).format("M/DD/YYYY hh:mm a")
+ subscription.user = {}
+ return subscription
+ },
},
route: function(app){
@@ -500,6 +565,27 @@ var staff = module.exports = {
staff.plans.update
);
+
+ //
+ // subscriptions
+ app.get('/staff/subscriptions',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureSubscriptions,
+ staff.middleware.ensureSubscriptionsUsers,
+
+ staff.subscriptions.index
+ );
+ app.get('/staff/subscriptions/:id',
+ middleware.ensureAuthenticated,
+ middleware.ensureIsStaff,
+
+ staff.middleware.ensureSubscription,
+ staff.middleware.ensureSubscriptionUser,
+
+ staff.subscriptions.show
+ );
},
paginate: function(req, res){
@@ -669,6 +755,18 @@ var staff = module.exports = {
res.redirect("/staff/plans/")
})
},
- }
+ },
+
+ subscriptions: {
+ index: function(req, res){
+ res.locals.pagination.count = res.locals.subscriptions.length
+ res.locals.pagination.max = res.locals.subscriptionCount
+ staff.paginate(req, res)
+ res.render('staff/subscriptions/index')
+ },
+ show: function(req, res){
+ res.render('staff/subscriptions/show')
+ },
+ },
}
diff --git a/server/lib/webhook/index.js b/server/lib/webhook/index.js
new file mode 100644
index 0000000..11419c2
--- /dev/null
+++ b/server/lib/webhook/index.js
@@ -0,0 +1,44 @@
+var config = require('../../../config.json'),
+ http = require('http'),
+ express = require('express'),
+ bodyParser = require('body-parser'),
+ mongoose = require('mongoose');
+
+var http = require('http'),
+ express = require('express'),
+ bodyParser = require('body-parser'),
+ multer = require('multer'),
+ MongoStore = require('connect-mongo')(express),
+ passport = require('passport'),
+ path = require('path'),
+ mongoose = require('mongoose');
+
+var webhook = require('./webhook');
+
+var app = express()
+var server
+var DATABASE_URI = process.env.MONGOLAB_URI || ('mongodb://' + config.databaseHost + '/vvalls')
+
+var site = {}
+
+site.init = function(){
+ mongoose.connect(DATABASE_URI, {}, site.ready);
+}
+
+site.ready = function(){
+ app.set('port', config.webhookPort);
+ app.use(bodyParser());
+ app.use(express.query());
+ app.set('env', config.env.production ? "production" : "development")
+ app.get('env') === 'development' && app.use(express.errorHandler());
+
+ server = http.createServer(app)
+ server.listen(app.get('port'), function () {
+ console.log('Webhook server listening on port ' + app.get('port'));
+ });
+
+ app.get('/', function(req,res){ res.send('HI THERE') })
+ webhook.route(app)
+}
+
+site.init()
diff --git a/server/lib/webhook/recurly-config.js b/server/lib/webhook/recurly-config.js
new file mode 100644
index 0000000..3d7e1c5
--- /dev/null
+++ b/server/lib/webhook/recurly-config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ API_KEY: process.env['VVALLS_RECURLY_SECRET'],
+ SUBDOMAIN: 'vvalls',
+ ENVIRONMENT: 'sandbox',
+ DEBUG: true,
+};
diff --git a/server/lib/webhook/webhook.js b/server/lib/webhook/webhook.js
new file mode 100644
index 0000000..4f23d0b
--- /dev/null
+++ b/server/lib/webhook/webhook.js
@@ -0,0 +1,154 @@
+// // where should this live?
+// app.get('/subscribe/webhook', views.subscription.webhook);
+
+/*
+app.use(express.basicAuth(function(user, pass, callback) {
+ var result = (user === 'testUser' && pass === 'testPass');
+ callback(null, result);
+}));
+*/
+
+
+/* jshint node: true */
+
+var User = require('../schemas/User'),
+ Subscription = require('../schemas/Subscription'),
+ config = require('../../../config'),
+ middleware = require('../middleware'),
+ util = require('../util'),
+ _ = require('lodash'),
+ moment = require('moment'),
+ xml2js = require('xml2js'),
+ Recurly = require('node-recurly'),
+ recurly = new Recurly(require('./recurly-config'));
+
+var parser = new xml2js.Parser();
+
+var subscribe = module.exports = {
+ plan_level: {
+ free: 0,
+ basic: 1,
+ pro: 2,
+ },
+
+ callbacks: {
+/*
+ // accounts
+ new_account_notification: function(data, user){
+ // fires on successful signup
+ },
+ canceled_account_notification: function(data, user){
+ },
+ billing_info_updated_notification: function(data, user){
+ },
+ reactivated_account_notification: function(data, user){
+ },
+
+ // invoices
+ new_invoice_notification: function(data, user){
+ },
+ closed_invoice_notification: function(data, user){
+ },
+ past_due_invoice_notification: function(data, user){
+ },
+*/
+
+ // subscriptions
+ new_subscription_notification: function(data, user){
+ var account = data.account
+ Subscription.findOne({ "uuid": data.subscription.uuid }, function(err, subscription){
+ if (err || subscription) return;
+
+ var plan = data.subscription.plan.plan_code.split("-")
+ var plan_type = plan[0]
+ var plan_period = plan[1]
+
+ user.plan_type = plan_type
+ user.plan_level = subscribe.plan_level[plan_type]
+
+ var subscriber = new Subscription ()
+ subscriber.uuid = data.subscription.uuid
+ subscriber.user_id = user._id
+ subscriber.plan_type = plan_type
+ subscriber.plan_period = plan_period
+ subscriber.plan_level = subscribe.plan_level[plan_type]
+ subscriber.add_ons = subscription.add_ons.map(function(add_on){
+ return {
+ name: add_on.plan,
+ quantity: add_on.quantity,
+ }
+ })
+ subscriber.save(function(err, data){
+ if (err) return;
+ user.save(function(err){
+ // saved!
+ })
+ })
+ })
+ },
+
+/*
+ updated_subscription_notification: function(data, user){
+ },
+ canceled_subscription_notification: function(data, user){
+ },
+ expired_subscription_notification: function(data, user){
+ },
+ renewed_subscription_notification: function(data, user){
+ },
+*/
+ // payments
+ successful_payment_notification: function(data, user){
+ var account = data.account
+ user.last_charged = new Date(data.transaction.date)
+ user.save(function(){
+ })
+ },
+/*
+ failed_payment_notification: function(data, user){
+ },
+ successful_refund_notification: function(data, user){
+ },
+ void_payment_notification: function(data, user){
+ },
+*/
+ },
+
+ execute: function(action, data){
+ User.findOne({ _id: data.account.account_code }, function(err, user){
+ if (err) { return }
+ subscribe.callbacks[action](data, user)
+ })
+ },
+
+ // then calls to get appropriate info from the recurly api
+ handle: function(req, res){
+ res.status(200).end()
+ parser.parseString(data, function (err, result) {
+ console.log(inspect(result, { colors: true, depth: Infinity }));
+ for (var action in result) {
+ if (subscribe.callbacks[action]) {
+ subscribe.execute(action, result[action]);
+ }
+ }
+ });
+ },
+
+ list: function(req, res){
+ recurly.subscriptions.listByAccount(req.params.id, function(data){
+ if (data.data != 404) {
+ res.json(data)
+ return
+ }
+ else {
+ res.json(data)
+ return
+ }
+ })
+ },
+
+ route: function(app){
+ app.post('/subscribe/webhook', subscribe.handle);
+ app.get('/subscribe/list/:id', subscribe.list);
+ },
+}