diff --git a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee index 76bfd78a57..d6c7fe272d 100644 --- a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee +++ b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee @@ -16,6 +16,7 @@ module.exports = RecurlyWrapper = "Content-Type" : "application/xml; charset=utf-8" request options, (error, response, body) -> unless error? or response.statusCode == 200 or response.statusCode == 201 or response.statusCode == 204 + logger.err err:error, options:options, "error returned from recurly" error = "Recurly API returned with status code: #{response.statusCode}" callback(error, response, body) @@ -72,8 +73,6 @@ module.exports = RecurlyWrapper = @getAccount accountId, (error, account) -> return callback(error) if error? recurlySubscription.account = account - console.log recurlySubscription - callback null, recurlySubscription else @@ -126,16 +125,19 @@ module.exports = RecurlyWrapper = redeemCoupon: (account_code, coupon_code, callback)-> requestBody = """ - + #{account_code} USD - + """ + logger.log account_code:account_code, coupon_code:coupon_code, requestBody:requestBody, "redeeming coupon for user" @apiRequest({ url : "coupons/#{coupon_code}/redeem" method : "post" body : requestBody }, (error, response, responseBody) => + if error? + logger.err err:error, account_code:account_code, coupon_code:coupon_code, "error redeeming coupon" callback(error) ) diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee index a0e4ee4d4a..a28557e63a 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee @@ -160,23 +160,34 @@ module.exports = SubscriptionController = else res.send 200 - renderUpgradeToAnnualPlanPage: (req, res)-> SecurityManager.getCurrentUser req, (error, user) -> - - LimitationsManager.userHasSubscription user, (err, hasSubscription)-> + LimitationsManager.userHasSubscription user, (err, hasSubscription, subscription)-> + planCode = subscription?.planCode.toLowerCase() + if planCode?.indexOf("student") != -1 + planName = "student" + else if planCode?.indexOf("collaborator") != -1 + planName = "collaborator" if !hasSubscription return res.redirect("/user/subscription/plans") + logger.log planName:planName, user_id:user._id, "rendering upgrade to annual page" res.render "subscriptions/upgradeToAnnual", title: "Upgrade to annual" - planName: req.query.planName - + planName: planName processUpgradeToAnnualPlan: (req, res)-> SecurityManager.getCurrentUser req, (error, user) -> - {plan_code, coupon_code} = req.body - SubscriptionHandler.updateSubscription user, plan_code, coupon_code, -> - res.send 200 + {planName} = req.body + coupon_code = Settings.coupon_codes.upgradeToAnnualPromo[planName] + annualPlanName = "#{planName}-annual" + logger.log user_id:user._id, planName:annualPlanName, "user is upgrading to annual billing with discount" + SubscriptionHandler.updateSubscription user, annualPlanName, coupon_code, (err)-> + if err? + logger.err err:err, user_id:user._id, "error updating subscription" + res.send 500 + else + res.send 200 + recurlyNotificationParser: (req, res, next) -> xml = "" diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionHandler.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionHandler.coffee index 804c8b0064..c74e20a39e 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionHandler.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionHandler.coffee @@ -18,7 +18,7 @@ module.exports = callback() updateSubscription: (user, plan_code, coupon_code, callback)-> - logger.log user:user, plan_code:plan_code, "updating subscription" + logger.log user:user, plan_code:plan_code, coupon_code:coupon_code, "updating subscription" LimitationsManager.userHasSubscription user, (err, hasSubscription, subscription)-> if !hasSubscription return callback() @@ -26,6 +26,7 @@ module.exports = async.series [ (cb)-> return cb() if !coupon_code? + logger.log user_id:user._id, plan_code:plan_code, coupon_code:coupon_code, "updating subscription with coupon code applied first" RecurlyWrapper.getSubscription subscription.recurlySubscription_id, includeAccount: true, (err, usersSubscription)-> return callback(err) if err? account_code = usersSubscription.account.account_code diff --git a/services/web/app/views/subscriptions/upgradeToAnnual.jade b/services/web/app/views/subscriptions/upgradeToAnnual.jade index 0cd2b0a656..17c13f4304 100644 --- a/services/web/app/views/subscriptions/upgradeToAnnual.jade +++ b/services/web/app/views/subscriptions/upgradeToAnnual.jade @@ -3,23 +3,24 @@ extends ../layout block content .content.content-alt - .container - .row + .container(ng-controller="AnnualUpgradeController") + .row(ng-cloak) .col-md-6.col-md-offset-3 - .card + .card(ng-init="planName = #{JSON.stringify(planName)}") .page-header h1.text-centered Move to Annual Billing + div(ng-hide="upgradeComplete") + span(ng-show="planName == 'student'") + | Upgarde from Student to Student Annual and save 20% equivilent to $19.2 - if planName.indexOf("student") != -1 - | Upgarde from Student to Student Annual and save 20% equivilent to $19.2 - - if planName.indexOf("collaborator") != -1 - | Upgarde from Collaborator to Collaborator Annual and save 20% equivilent to $36 + span(ng-show="planName == 'collaborator'") + | Upgarde from Collaborator to Collaborator Annual and save 20% equivilent to $36 - form(action="/user/subscription/upgrade-annual", method="post") - input(name="_csrf", type="hidden", value=csrfToken) - input(name="planName", type="hidden", value=planName) - input.btn.btn-success(type="submit", value="Move to annual billing now") + button.btn.btn-success(ng-click="completeAnnualUpgrade()", ng-disabled="inflight") + span(ng-show="inflight") #{translate("processing")} + span(ng-hide="inflight") Move to annual billing now + div(ng-show="upgradeComplete") + span Upgrade to annual Complete diff --git a/services/web/public/coffee/main.coffee b/services/web/public/coffee/main.coffee index 6757f4ae83..fa71db56fc 100644 --- a/services/web/public/coffee/main.coffee +++ b/services/web/public/coffee/main.coffee @@ -11,6 +11,7 @@ define [ "main/system-messages" "main/translations" "main/subscription-dashboard" + "main/annual-upgrade" "directives/asyncForm" "directives/stopPropagation" "directives/focus" diff --git a/services/web/public/coffee/main/annual-upgrade.coffee b/services/web/public/coffee/main/annual-upgrade.coffee new file mode 100644 index 0000000000..e468b6cc24 --- /dev/null +++ b/services/web/public/coffee/main/annual-upgrade.coffee @@ -0,0 +1,22 @@ +define [ + "base" +], (App) -> + App.controller "AnnualUpgradeController", ($scope, $http, $modal) -> + + MESSAGES_URL = "/user/subscription/upgrade-annual" + + $scope.upgradeComplete = false + + $scope.completeAnnualUpgrade = -> + body = + planName: $scope.planName + _csrf : window.csrfToken + + $scope.inflight = true + + + $http.post(MESSAGES_URL, body) + .success -> + $scope.upgradeComplete = true + .error -> + console.log "something went wrong changing plan" \ No newline at end of file diff --git a/services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee b/services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee index 2ddf0bbc3b..6ca0600c97 100644 --- a/services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Subscription/SubscriptionControllerTests.coffee @@ -18,7 +18,7 @@ mockSubscriptions = account: account_code: "user-123" -describe "Subscription controller sanboxed", -> +describe "SubscriptionController sanboxed", -> beforeEach -> @user = {} @@ -47,6 +47,15 @@ describe "Subscription controller sanboxed", -> @SubscriptionViewModelBuilder = buildUsersSubscriptionViewModel:sinon.stub().callsArgWith(1, null, @activeRecurlySubscription) buildViewModel: sinon.stub() + @settings = + coupon_codes: + upgradeToAnnualPromo: + student:"STUDENTCODEHERE" + collaborator:"COLLABORATORCODEHERE" + apis: + recurly: + subdomain:"sl.recurly.com" + siteUrl: "http://www.sharelatex.dev:3000" @SubscriptionController = SandboxedModule.require modulePath, requires: '../../managers/SecurityManager': @SecurityManager @@ -56,6 +65,7 @@ describe "Subscription controller sanboxed", -> "./LimitationsManager": @LimitationsManager './RecurlyWrapper': @RecurlyWrapper "logger-sharelatex": log:-> + "settings-sharelatex": @settings @res = new MockResponse() @@ -297,18 +307,48 @@ describe "Subscription controller sanboxed", -> @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res + it "should pass the plan code to the view - student", (done)-> + + @LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"Student free trial 14 days"}) + @res.render = (view, opts)-> + view.should.equal "subscriptions/upgradeToAnnual" + opts.planName.should.equal "student" + done() + @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res + + it "should pass the plan code to the view - collaborator", (done)-> + + @LimitationsManager.userHasSubscription.callsArgWith(1, null, true, {planCode:"free trial for Collaborator free trial 14 days"}) + @res.render = (view, opts)-> + opts.planName.should.equal "collaborator" + done() + @SubscriptionController.renderUpgradeToAnnualPlanPage @req, @res + describe "processUpgradeToAnnualPlan", -> beforeEach -> - @req.body = - coupon_code:"1234" - plan_code:"student-annual" - - @res = {} - + it "should tell the subscription handler to update the subscription with the annual plan and apply a coupon code", (done)-> - @res.send = ()=> - @SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "1234").should.equal true + @req.body = + planName:"student" + + @res.redirect = ()=> + @SubscriptionHandler.updateSubscription.calledWith(@user, "student-annual", "STUDENTCODEHERE").should.equal true done() - @SubscriptionController.processUpgradeToAnnualPlan @req, @res \ No newline at end of file + @SubscriptionController.processUpgradeToAnnualPlan @req, @res + + it "should get the collaborator coupon code", (done)-> + + @req.body = + planName:"collaborator" + + @res.redirect = (url)=> + @SubscriptionHandler.updateSubscription.calledWith(@user, "collaborator-annual", "COLLABORATORCODEHERE").should.equal true + url.should.equal "/user/subscription/thank-you" + done() + + @SubscriptionController.processUpgradeToAnnualPlan @req, @res + + +