diff --git a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
index 2e47ea597d..0a179cd81f 100644
--- a/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
+++ b/services/web/app/coffee/Features/Subscription/RecurlyWrapper.coffee
@@ -321,6 +321,27 @@ module.exports = RecurlyWrapper =
RecurlyWrapper._parseAccountXml body, callback
)
+ getAccountActiveCoupons: (accountId, callback) ->
+ RecurlyWrapper.apiRequest({
+ url: "accounts/#{accountId}/redemptions"
+ }, (error, response, body) =>
+ return callback(error) if error?
+ RecurlyWrapper._parseRedemptionsXml body, (error, redemptions) ->
+ return callback(error) if error?
+ activeRedemptions = redemptions.filter (redemption) ->
+ redemption.state == 'active'
+ couponCodes = activeRedemptions.map (redemption) ->
+ redemption.coupon_code
+ Async.map couponCodes, RecurlyWrapper.getCoupon, (error, coupons) ->
+ return callback(error) if error?
+ callback(null, coupons)
+ )
+
+ getCoupon: (couponCode, callback) ->
+ opts = { url: "coupons/#{couponCode}" }
+ RecurlyWrapper.apiRequest opts, (error, response, body) ->
+ RecurlyWrapper._parseCouponXml body, callback
+
getBillingInfo: (accountId, callback)->
RecurlyWrapper.apiRequest({
url: "accounts/#{accountId}/billing_info"
@@ -465,6 +486,12 @@ module.exports = RecurlyWrapper =
_parseBillingInfoXml: (xml, callback) ->
RecurlyWrapper._parseXmlAndGetAttribute xml, "billing_info", callback
+ _parseRedemptionsXml: (xml, callback) ->
+ RecurlyWrapper._parseXmlAndGetAttribute xml, "redemptions", callback
+
+ _parseCouponXml: (xml, callback) ->
+ RecurlyWrapper._parseXmlAndGetAttribute xml, "coupon", callback
+
_parseXmlAndGetAttribute: (xml, attribute, callback) ->
RecurlyWrapper._parseXml xml, (error, data) ->
return callback(error) if error?
@@ -505,6 +532,7 @@ module.exports = RecurlyWrapper =
parser = new xml2js.Parser(
explicitRoot : true
explicitArray : false
+ emptyTag: ''
)
parser.parseString xml, (error, data) ->
return callback(error) if error?
diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
index c393b0c4a4..992f051806 100644
--- a/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
+++ b/services/web/app/coffee/Features/Subscription/SubscriptionController.coffee
@@ -150,7 +150,7 @@ module.exports = SubscriptionController =
return res.redirect '/user/subscription/plans'
res.render "subscriptions/successful_subscription",
title: "thank_you"
- subscription:personalSubscription
+ personalSubscription: personalSubscription
cancelSubscription: (req, res, next) ->
user = AuthenticationController.getSessionUser(req)
diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee
index 2f590bd2df..2c55a1ee8e 100644
--- a/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee
+++ b/services/web/app/coffee/Features/Subscription/SubscriptionViewModelBuilder.coffee
@@ -34,6 +34,11 @@ module.exports =
return cb(null, null)
RecurlyWrapper.getSubscription personalSubscription.recurlySubscription_id, includeAccount: true, cb
]
+ recurlyCoupons: ['recurlySubscription', (cb, {recurlySubscription}) ->
+ return cb(null, null) if !recurlySubscription
+ accountId = recurlySubscription.account.account_code
+ RecurlyWrapper.getAccountActiveCoupons accountId, cb
+ ]
plan: ['personalSubscription', (cb, {personalSubscription}) ->
return cb() if !personalSubscription?
plan = PlansLocator.findLocalPlanInSettings(personalSubscription.planCode)
@@ -65,6 +70,7 @@ module.exports =
managedPublishers,
v1SubscriptionStatus,
recurlySubscription,
+ recurlyCoupons,
plan
} = results
memberGroupSubscriptions ?= []
@@ -72,6 +78,7 @@ module.exports =
confirmedMemberInstitutions ?= []
managedInstitutions ?= []
v1SubscriptionStatus ?= {}
+ recurlyCoupons ?= []
if personalSubscription?.toObject?
@@ -93,6 +100,7 @@ module.exports =
state: recurlySubscription.state
trialEndsAtFormatted: SubscriptionFormatters.formatDate(recurlySubscription?.trial_ends_at)
trial_ends_at: recurlySubscription.trial_ends_at
+ activeCoupons: recurlyCoupons
}
for memberGroupSubscription in memberGroupSubscriptions
diff --git a/services/web/app/views/subscriptions/_price_exceptions.pug b/services/web/app/views/subscriptions/_price_exceptions.pug
new file mode 100644
index 0000000000..be0018db61
--- /dev/null
+++ b/services/web/app/views/subscriptions/_price_exceptions.pug
@@ -0,0 +1,9 @@
+p
+ i * !{translate("subject_to_additional_vat")}
+
+if (personalSubscription.recurly.activeCoupons.length > 0)
+ i * !{translate("coupons_not_included")}:
+ ul
+ each coupon in personalSubscription.recurly.activeCoupons
+ li
+ i= coupon.description || coupon.name
diff --git a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug
index f376bb59bc..645a2e037a 100644
--- a/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug
+++ b/services/web/app/views/subscriptions/dashboard/_personal_subscription_recurly.pug
@@ -13,6 +13,7 @@ div(ng-controller="RecurlySubscriptionController")
-if (personalSubscription.recurly.trialEndsAtFormatted && personalSubscription.recurly.trial_ends_at > Date.now())
p You're on a free trial which ends on #{personalSubscription.recurly.trialEndsAtFormatted}
p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:"" + personalSubscription.recurly.price + "", collectionDate:"" + personalSubscription.recurly.nextPaymentDueAt + ""})}
+ include ./../_price_exceptions
p.pull-right
p
a(href=personalSubscription.recurly.billingDetailsLink, target="_blank").btn.btn-info #{translate("update_your_billing_details")}
diff --git a/services/web/app/views/subscriptions/successful_subscription.pug b/services/web/app/views/subscriptions/successful_subscription.pug
index f8515007ab..ac21595e28 100644
--- a/services/web/app/views/subscriptions/successful_subscription.pug
+++ b/services/web/app/views/subscriptions/successful_subscription.pug
@@ -9,14 +9,15 @@ block content
.page-header
h2 #{translate("thanks_for_subscribing")}
.alert.alert-success
- p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:""+subscription.recurly.price+"", collectionDate:""+subscription.recurly.nextPaymentDueAt+""})}
+ p !{translate("next_payment_of_x_collectected_on_y", {paymentAmmount:""+personalSubscription.recurly.price+"", collectionDate:""+personalSubscription.recurly.nextPaymentDueAt+""})}
+ include ./_price_exceptions
p #{translate("to_modify_your_subscription_go_to")}
a(href="/user/subscription") #{translate("manage_subscription")}.
p
- - if (subscription.groupPlan == true)
+ - if (personalSubscription.groupPlan == true)
a.btn.btn-success.btn-large(href="/subscription/group") #{translate("add_your_first_group_member_now")}
p.letter-from-founders
- p #{translate("thanks_for_subscribing_you_help_sl", {planName:subscription.plan.name})}
+ p #{translate("thanks_for_subscribing_you_help_sl", {planName:personalSubscription.plan.name})}
p #{translate("need_anything_contact_us_at")}
a(href=`mailto:${settings.adminEmail}`, ng-non-bindable) #{settings.adminEmail}
| .
diff --git a/services/web/test/acceptance/coffee/SubscriptionTests.coffee b/services/web/test/acceptance/coffee/SubscriptionTests.coffee
index 8539084536..011af90f35 100644
--- a/services/web/test/acceptance/coffee/SubscriptionTests.coffee
+++ b/services/web/test/acceptance/coffee/SubscriptionTests.coffee
@@ -42,6 +42,11 @@ describe 'Subscriptions', ->
account_id: 'mock-account-id',
trial_ends_at: new Date(2018, 6, 7)
}
+ MockRecurlyApi.coupons = @coupons = {
+ 'test-coupon-1': { description: 'Test Coupon 1' }
+ 'test-coupon-2': { description: 'Test Coupon 2' }
+ 'test-coupon-3': { name: 'TestCoupon3' }
+ }
Subscription.create {
admin_id: @user._id,
manager_ids: [@user._id],
@@ -57,6 +62,8 @@ describe 'Subscriptions', ->
after (done) ->
MockRecurlyApi.accounts = {}
MockRecurlyApi.subscriptions = {}
+ MockRecurlyApi.coupons = {}
+ MockRecurlyApi.redemptions = {}
Subscription.remove {
admin_id: @user._id
}, done
@@ -68,6 +75,7 @@ describe 'Subscriptions', ->
expect(subscription.planCode).to.equal 'collaborator'
expect(subscription.recurly).to.exist
expect(subscription.recurly).to.deep.equal {
+ "activeCoupons": []
"billingDetailsLink": "https://test.recurly.com/account/billing_info/edit?ht=mock-login-token"
"currency": "GBP"
"nextPaymentDueAt": "5th May 2018"
@@ -82,6 +90,30 @@ describe 'Subscriptions', ->
it 'should return no memberGroupSubscriptions', ->
expect(@data.memberGroupSubscriptions).to.deep.equal []
+ it 'should include redeemed coupons', (done) ->
+ MockRecurlyApi.redemptions['mock-account-id'] = [
+ { state: 'active', coupon_code: 'test-coupon-1' }
+ { state: 'inactive', coupon_code: 'test-coupon-2' }
+ { state: 'active', coupon_code: 'test-coupon-3' }
+ ]
+
+ # rebuild the view model with the redemptions
+ SubscriptionViewModelBuilder.buildUsersSubscriptionViewModel @user, (error, data) ->
+ expect(error).to.not.exist
+ expect(data.personalSubscription.recurly.activeCoupons).to.deep.equal [
+ {
+ coupon_code: 'test-coupon-1',
+ name: '',
+ description: 'Test Coupon 1'
+ }
+ {
+ coupon_code: 'test-coupon-3',
+ name: 'TestCoupon3',
+ description: ''
+ }
+ ]
+ done()
+
describe 'when the user has a subscription without recurly', ->
before (done) ->
Subscription.create {
diff --git a/services/web/test/acceptance/coffee/helpers/MockRecurlyApi.coffee b/services/web/test/acceptance/coffee/helpers/MockRecurlyApi.coffee
index 59ed6ee362..23692c6ac4 100644
--- a/services/web/test/acceptance/coffee/helpers/MockRecurlyApi.coffee
+++ b/services/web/test/acceptance/coffee/helpers/MockRecurlyApi.coffee
@@ -9,6 +9,10 @@ module.exports = MockRecurlyApi =
accounts: {}
+ redemptions: {}
+
+ coupons: {}
+
run: () ->
app.get '/subscriptions/:id', (req, res, next) =>
subscription = @subscriptions[req.params.id]
@@ -36,10 +40,41 @@ module.exports = MockRecurlyApi =
else
res.send """
+ #{req.params.id}
#{account.hosted_login_token}
"""
+ app.get '/coupons/:code', (req, res, next) =>
+ coupon = @coupons[req.params.code]
+ if !coupon?
+ res.status(404).end()
+ else
+ res.send """
+
+ #{req.params.code}
+ #{coupon.name or ''}
+ #{coupon.description or ''}
+
+ """
+
+ app.get '/accounts/:id/redemptions', (req, res, next) =>
+ redemptions = @redemptions[req.params.id] or []
+ redemptionsListXml = ''
+ for redemption in redemptions
+ redemptionsListXml += """
+
+ #{redemption.state}
+ #{redemption.coupon_code}
+
+ """
+
+ res.send """
+
+ #{redemptionsListXml}
+
+ """
+
app.listen 6034, (error) ->
throw error if error?