From b0eb12bb5fb950330f2c7bc94c35d5d55a8ba587 Mon Sep 17 00:00:00 2001 From: Tyler Renelle Date: Mon, 5 May 2014 23:18:27 -0600 Subject: [PATCH] feat(subscriptions): allow one month after sub start-date before it's terminated. Fixes #3180 --- src/controllers/payments.js | 19 ++++++++++++++--- src/models/user.js | 1 + test/api.mocha.js | 41 +++++++++++++++++++++++++++++++++++-- views/options/settings.jade | 23 +++++++++++---------- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/src/controllers/payments.js b/src/controllers/payments.js index 7956a7db8c..86b45bcbdf 100644 --- a/src/controllers/payments.js +++ b/src/controllers/payments.js @@ -44,7 +44,8 @@ function createSubscription(user, data) { customerId: data.customerId, dateUpdated: new Date, gemsBought: 0, - paymentMethod: data.paymentMethod + paymentMethod: data.paymentMethod, + dateTerminated: null }) .defaults({ // allow non-override if a plan was previously used dateCreated: new Date, @@ -56,9 +57,15 @@ function createSubscription(user, data) { ga.transaction(data.customerId, 5).item(5, 1, data.paymentMethod.toLowerCase() + '-subscription', data.paymentMethod + " > Stripe").send(); } +/** + * Sets their subscription to be cancelled later + */ function cancelSubscription(user, data){ - _.merge(user.purchased.plan, {planId:null, customerId:null, paymentMethod:null}); - user.markModified('purchased.plan'); + var du = user.purchased.plan.dateUpdated, now = moment(); + user.purchased.plan.dateTerminated = + moment( now.format('MM') + '/' + moment(du).format('DD') + '/' + now.format('YYYY') ) + .add('month',1) + .toDate(); ga.event('unsubscribe', 'Stripe').send(); } @@ -69,6 +76,12 @@ function buyGems(user, data) { ga.transaction(data.customerId, 5).item(5, 1, data.paymentMethod.toLowerCase() + "-checkout", "Gems > " + data.paymentMethod).send(); } +// Expose some functions for tests +if (nconf.get('NODE_ENV')==='testing') { + api.cancelSubscription = cancelSubscription; + api.createSubscription = createSubscription; +} + /* Setup Stripe response when posting payment */ diff --git a/src/models/user.js b/src/models/user.js index 3f5b02cc54..4e019af4aa 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -89,6 +89,7 @@ var UserSchema = new Schema({ paymentMethod: String, //enum: ['Paypal','Stripe', '']} customerId: String, dateCreated: Date, + dateTerminated: Date, dateUpdated: Date, gemsBought: {type: Number, 'default': 0}, mysteryItems: {type: Array, 'default': []} diff --git a/test/api.mocha.js b/test/api.mocha.js index 935e9f0ca1..fc98b60f8a 100644 --- a/test/api.mocha.js +++ b/test/api.mocha.js @@ -27,6 +27,7 @@ var Challenge = require('../src/models/challenge').model; var app = require('../src/server'); var shared = require('habitrpg-shared'); +var payments = require('../src/controllers/payments'); // ###### Helpers & Variables ###### var model, uuid, taskPath, @@ -118,6 +119,7 @@ describe('API', function () { }); }); + describe('Todos', function(){ it('Archives old todos',function(done){ request.post(baseURL + "/user/batch-update?_v=999") @@ -506,8 +508,7 @@ describe('API', function () { ],done); - // You see all these freaking })s? This is why I prefer coffeescript!! I keep hitting stupid errors - // where they're mis-matched and I'm counting )'s on my fingers for 10m just to fix them. Gaahhh.. + // See all these })s? This is why CoffeeScript is better. //}) }) }) @@ -517,5 +518,41 @@ describe('API', function () { }); }); + + describe('Subscriptions', function(){ + var user; + before(function(done){ + User.findOne({_id: _id}, function (err, _user) { + expect(err).to.not.be.ok(); + user = _user; + done(); + }); + }) + }) + + it('Handles unsubscription', function(done){ + var cron = function(){ + user.lastCron = moment().subtract('d',1); + user.fns.cron(); + } + expect(user.purchased.plan.customerId).to.not.be.ok(); + payments.createSubscription(user,{customerId:'123',paymentMethod:'Stripe'}); + expect(user.purchased.plan.customerId).to.be.ok(); + shared.wrap(user); + cron(); + expect(user.purchased.plan.customerId).to.be.ok(); + payments.cancelSubscription(user); + cron(); + expect(user.purchased.plan.customerId).to.be.ok(); + expect(user.purchased.plan.dateTerminated).to.be.ok(); + user.purchased.plan.dateTerminated = moment().subtract('d',2); + cron(); + expect(user.purchased.plan.customerId).to.not.be.ok(); + payments.createSubscription(user,{customerId:'123',paymentMethod:'Stripe'}); + expect(user.purchased.plan.dateTerminated).to.not.be.ok(); + done(); + }) + + }); }); diff --git a/views/options/settings.jade b/views/options/settings.jade index 832c08bb03..7e450ec533 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -193,16 +193,17 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template') p small.muted Payment Methods: .btn.btn-primary(ng-click='showStripe(true)') Card - //-small.muted PayPal coming soon. - //a.btn.btn-warning(ng-click='paypalSubscribe()') PayPal + //a.btn.btn-warning(ng-click='paypalSubscribe()') PayPal a.btn.btn-warning(href='/paypal/subscribe?_id={{user._id}}&apiToken={{user.apiToken}}') PayPal div(ng-if='user.purchased.plan.customerId') - .well - p.lead - =env.t('subscribed') - |   - span.glyphicon.glyphicon-ok - div(ng-include="'partials/options.settings.subscription.perks.html'") - .btn.btn-sm.btn-danger(ng-click='cancelSubscription()')=env.t('cancelSub') - p - small.muted Unsubscribing removes subscriber perks (read more here). We're working on persisting subscriber perks through a canceled plan's billing cycle, but for now be sure to open your mystery box before unsubscribing! + p.alert.alert-warning(ng-if='user.purchased.plan.dateTerminated') + i.glyphicon.glyphicon-time + |   + =env.t('subCanceled') + | {{moment(user.purchased.plan.dateTerminated).format('MM/DD/YYYY')}} + p.lead + =env.t('subscribed') + |   + span.glyphicon.glyphicon-ok + div(ng-include="'partials/options.settings.subscription.perks.html'") + .btn.btn-sm.btn-danger(ng-if='!user.purchased.plan.dateTerminated', ng-click='cancelSubscription()')=env.t('cancelSub') \ No newline at end of file