diff --git a/ghost/members-api/lib/MembersAPI.js b/ghost/members-api/lib/MembersAPI.js index 6f072d4d36..4b0ca3d57b 100644 --- a/ghost/members-api/lib/MembersAPI.js +++ b/ghost/members-api/lib/MembersAPI.js @@ -132,6 +132,7 @@ module.exports = function MembersAPI({ const memberController = new MemberController({ memberRepository, + productRepository, StripePrice, tokenService, sendEmailWithMagicLink diff --git a/ghost/members-api/lib/controllers/member.js b/ghost/members-api/lib/controllers/member.js index 25aae2db73..d0c3e1b2ba 100644 --- a/ghost/members-api/lib/controllers/member.js +++ b/ghost/members-api/lib/controllers/member.js @@ -4,17 +4,20 @@ module.exports = class MemberController { /** * @param {object} deps * @param {any} deps.memberRepository + * @param {any} deps.productRepository * @param {any} deps.StripePrice * @param {any} deps.tokenService * @param {any} deps.sendEmailWithMagicLink */ constructor({ memberRepository, + productRepository, StripePrice, tokenService, sendEmailWithMagicLink }) { this._memberRepository = memberRepository; + this._productRepository = productRepository; this._StripePrice = StripePrice; this._tokenService = tokenService; this._sendEmailWithMagicLink = sendEmailWithMagicLink; @@ -112,6 +115,13 @@ module.exports = class MemberController { } const priceId = price.get('stripe_price_id'); + const product = await this._productRepository.get({stripe_price_id: priceId}); + + if (product.get('active') !== true) { + res.writeHead(403); + return res.end('Tier is archived.'); + } + await this._memberRepository.updateSubscription({ email, subscription: { diff --git a/ghost/members-api/test/unit/lib/controllers/member/index.test.js b/ghost/members-api/test/unit/lib/controllers/member/index.test.js index a0f9eb0728..926f94e55e 100644 --- a/ghost/members-api/test/unit/lib/controllers/member/index.test.js +++ b/ghost/members-api/test/unit/lib/controllers/member/index.test.js @@ -3,7 +3,7 @@ const MemberController = require('../../../../../lib/controllers/member'); describe('MemberController', function () { describe('updateSubscription', function () { - it('Updates a subscriptions plan via the member repository', async function () { + it('Updates a subscriptions plan via the member repository if the Tier is active', async function () { const tokenService = { decodeToken: sinon.fake.resolves({sub: 'fake@email.com'}) }; @@ -27,8 +27,69 @@ describe('MemberController', function () { }) }; + const productRepository = { + get: sinon.fake.resolves({ + get() { + return true; + } + }) + }; + const controller = new MemberController({ memberRepository, + productRepository, + StripePrice, + tokenService + }); + + const req = { + body: { + identity: 'token', + priceId: 'plan_name' + }, + params: { + id: 'subscription_id' + } + }; + const res = { + writeHead() {}, + end() {} + }; + + await controller.updateSubscription(req, res); + + memberRepository.updateSubscription.verify(); + }); + + it('Does not a subscriptions plan via the member repository if the Tier is not active', async function () { + const tokenService = { + decodeToken: sinon.fake.resolves({sub: 'fake@email.com'}) + }; + const StripePrice = { + findOne: sinon.fake.returns({ + id: 'plan_id', + stripe_price_id: 'stripe_price_id', + get: () => { + return 'stripe_price_id'; + } + }) + }; + + const memberRepository = { + updateSubscription: sinon.mock('updateSubscription').never() + }; + + const productRepository = { + get: sinon.fake.resolves({ + get() { + return false; + } + }) + }; + + const controller = new MemberController({ + memberRepository, + productRepository, StripePrice, tokenService });