From 007637973ea40758da0369906d7be95e635eafaa Mon Sep 17 00:00:00 2001 From: Rishabh Garg Date: Fri, 28 Oct 2022 01:59:05 +0530 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fixed=20comped=20member=20creati?= =?UTF-8?q?on=20via=20Admin=20API=20(#15714)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes https://github.com/TryGhost/Team/issues/2184 - when using the old legacy method of `comped:true` to add complimentary subs to a member along with a label, the API call failed with `Internal Server error` and the member was added as free on the site. - patches the options sent for fetching default product to only pick the relevant keys, as it was picking up the `withRelated` for `labels` that caused the API failure --- ghost/members-api/lib/repositories/member.js | 11 ++++--- .../test/unit/lib/repositories/member.test.js | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/ghost/members-api/lib/repositories/member.js b/ghost/members-api/lib/repositories/member.js index 75c0afc6d2..982d3b051f 100644 --- a/ghost/members-api/lib/repositories/member.js +++ b/ghost/members-api/lib/repositories/member.js @@ -1394,10 +1394,11 @@ module.exports = class MemberRepository { const activeSubscriptions = subscriptions.models.filter((subscription) => { return this.isActiveSubscriptionStatus(subscription.get('status')); }); + const sharedOptions = _.pick(options, ['context', 'transacting']); const ghostProductModel = await this._productRepository.getDefaultProduct({ withRelated: ['stripePrices'], - ...options + ...sharedOptions }); const defaultProduct = ghostProductModel?.toJSON(); @@ -1454,7 +1455,7 @@ module.exports = class MemberRepository { await this.linkSubscription({ id: member.id, subscription: updatedSubscription - }, options); + }, sharedOptions); } } else { const stripeCustomer = await this._stripeAPIService.createCustomer({ @@ -1466,7 +1467,7 @@ module.exports = class MemberRepository { member_id: data.id, email: stripeCustomer.email, name: stripeCustomer.name - }, options); + }, sharedOptions); let zeroValuePrice = zeroValuePrices[0]; @@ -1482,7 +1483,7 @@ module.exports = class MemberRepository { interval: 'year', amount: 0 }] - }, options)).toJSON(); + }, sharedOptions)).toJSON(); zeroValuePrice = product.stripePrices.find((price) => { return price.currency.toLowerCase() === 'usd' && price.amount === 0; }); @@ -1497,7 +1498,7 @@ module.exports = class MemberRepository { await this.linkSubscription({ id: member.id, subscription - }, options); + }, sharedOptions); } } diff --git a/ghost/members-api/test/unit/lib/repositories/member.test.js b/ghost/members-api/test/unit/lib/repositories/member.test.js index 471e3639dc..8b7d02bb61 100644 --- a/ghost/members-api/test/unit/lib/repositories/member.test.js +++ b/ghost/members-api/test/unit/lib/repositories/member.test.js @@ -99,6 +99,38 @@ describe('MemberRepository', function () { assert.equal(err.message, 'Could not find Product "default"'); } }); + + it('uses the right options for fetching default product', async function () { + productRepository = { + getDefaultProduct: sinon.stub().resolves({ + toJSON: () => { + return null; + } + }) + }; + + const repo = new MemberRepository({ + Member, + stripeAPIService: { + configured: true + }, + productRepository + }); + + try { + await repo.setComplimentarySubscription({ + id: 'member_id_123' + }, { + transacting: true, + withRelated: ['labels'] + }); + + assert.fail('setComplimentarySubscription should have thrown'); + } catch (err) { + productRepository.getDefaultProduct.calledWith({withRelated: ['stripePrices'], transacting: true}).should.be.true(); + assert.equal(err.message, 'Could not find Product "default"'); + } + }); }); describe('linkSubscription', function (){