🐛 Fixed broken redemption count for offers (#15954)

refs https://github.com/TryGhost/Team/issues/2369

- offer id was not getting attached to stripe checkout metadata, causing the checkout event to not store any offer information for a subscription. This got changed in a prev refactor [here](25d8d694a0 (diff-b7dfcd660902a2a20dff7da5e886d8e10234bda4ba78228255afc8d4a8e78cf6L206))
- cleans up offer id handling for checkout session event
This commit is contained in:
Rishabh Garg 2022-12-07 14:30:11 +05:30 committed by GitHub
parent 55b0f564ba
commit 8bdad78377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 1 deletions

View File

@ -186,6 +186,9 @@ module.exports = class RouterController {
offer = await this._offersAPI.getOffer({id: offerId}); offer = await this._offersAPI.getOffer({id: offerId});
tier = await this._tiersService.api.read(offer.tier.id); tier = await this._tiersService.api.read(offer.tier.id);
cadence = offer.cadence; cadence = offer.cadence;
// Attach offer information to stripe metadata for free trial offers
// free trial offers don't have associated stripe coupons
metadata.offer = offer.id;
} else { } else {
offer = null; offer = null;
tier = await this._tiersService.api.read(tierId); tier = await this._tiersService.api.read(tierId);

View File

@ -1033,7 +1033,7 @@ module.exports = class MemberRepository {
tierId: ghostProduct?.get('id'), tierId: ghostProduct?.get('id'),
memberId: member.id, memberId: member.id,
subscriptionId: subscriptionModel.get('id'), subscriptionId: subscriptionModel.get('id'),
offerId: data.offerId, offerId: offerId,
attribution: data.attribution, attribution: data.attribution,
batchId: options.batch_id batchId: options.batch_id
}); });

View File

@ -0,0 +1,93 @@
const sinon = require('sinon');
const RouterController = require('../../../../lib/controllers/router');
describe('RouterController', function () {
describe('createCheckoutSession', function (){
let offersAPI;
let paymentsService;
let tiersService;
let stripeAPIService;
let labsService;
let getPaymentLinkSpy;
beforeEach(async function () {
getPaymentLinkSpy = sinon.spy();
tiersService = {
api: {
read: sinon.stub().resolves({
id: 'tier_123'
})
}
};
paymentsService = {
getPaymentLink: getPaymentLinkSpy
};
offersAPI = {
getOffer: sinon.stub().resolves({
id: 'offer_123',
tier: {
id: 'tier_123'
}
}),
findOne: sinon.stub().resolves({
related: () => {
return {
query: sinon.stub().returns({
fetchOne: sinon.stub().resolves({})
}),
toJSON: sinon.stub().returns([]),
fetch: sinon.stub().resolves({
toJSON: sinon.stub().returns({})
})
};
},
toJSON: sinon.stub().returns({})
}),
edit: sinon.stub().resolves({
attributes: {},
_previousAttributes: {}
})
};
stripeAPIService = {
configured: true
};
labsService = {
isSet: sinon.stub().returns(true)
};
});
it('passes offer metadata to payment link method', async function (){
const routerController = new RouterController({
tiersService,
paymentsService,
offersAPI,
stripeAPIService,
labsService
});
await routerController.createCheckoutSession({
body: {
offerId: 'offer_123'
}
}, {
writeHead: () => {},
end: () => {}
});
getPaymentLinkSpy.calledOnce.should.be.true();
// Payment link is called with the offer id in metadata
getPaymentLinkSpy.calledWith(sinon.match({
metadata: {offer: 'offer_123'}
})).should.be.true();
});
afterEach(function () {
sinon.restore();
});
});
});

View File

@ -141,6 +141,7 @@ describe('MemberRepository', function () {
let MemberProductEvent; let MemberProductEvent;
let stripeAPIService; let stripeAPIService;
let productRepository; let productRepository;
let offerRepository;
let labsService; let labsService;
let subscriptionData; let subscriptionData;
@ -221,6 +222,12 @@ describe('MemberRepository', function () {
labsService = { labsService = {
isSet: sinon.stub().returns(true) isSet: sinon.stub().returns(true)
}; };
offerRepository = {
getById: sinon.stub().resolves({
id: 'offer_123'
})
};
}); });
it('dispatches paid subscription event', async function (){ it('dispatches paid subscription event', async function (){
@ -250,6 +257,42 @@ describe('MemberRepository', function () {
notifySpy.calledOnce.should.be.true(); notifySpy.calledOnce.should.be.true();
}); });
it('attaches offer information to subscription event', async function (){
const repo = new MemberRepository({
stripeAPIService,
StripeCustomerSubscription,
MemberPaidSubscriptionEvent,
MemberProductEvent,
productRepository,
offerRepository,
labsService,
Member
});
sinon.stub(repo, 'getSubscriptionByStripeID').resolves(null);
DomainEvents.subscribe(SubscriptionCreatedEvent, notifySpy);
await repo.linkSubscription({
id: 'member_id_123',
subscription: subscriptionData,
offerId: 'offer_123'
}, {
transacting: {
executionPromise: Promise.resolve()
},
context: {}
});
notifySpy.calledOnce.should.be.true();
notifySpy.calledWith(sinon.match((event) => {
if (event.data.offerId === 'offer_123') {
return true;
}
return false;
})).should.be.true();
});
afterEach(function () { afterEach(function () {
sinon.restore(); sinon.restore();
}); });