diff --git a/ghost/members-api/lib/repositories/member.js b/ghost/members-api/lib/repositories/member.js index d036b1a861..71771ee01a 100644 --- a/ghost/members-api/lib/repositories/member.js +++ b/ghost/members-api/lib/repositories/member.js @@ -1086,6 +1086,7 @@ module.exports = class MemberRepository { memberId: member.id, subscriptionId: subscriptionModel.get('id'), offerId: offerId, + attribution: data.attribution, batchId: options.batch_id }); this.dispatchEvent(activatedEvent, options); diff --git a/ghost/staff-service/lib/email-templates/new-free-signup.hbs b/ghost/staff-service/lib/email-templates/new-free-signup.hbs index e2bf815e48..3a54fb71b9 100644 --- a/ghost/staff-service/lib/email-templates/new-free-signup.hbs +++ b/ghost/staff-service/lib/email-templates/new-free-signup.hbs @@ -48,6 +48,9 @@ {{/if}}

Created on {{memberData.createdAt}}{{#if memberData.location}} • {{memberData.location}} {{/if}} + {{#if referrerSource}} +

+ {{/if}}

diff --git a/ghost/staff-service/lib/email-templates/new-paid-started.hbs b/ghost/staff-service/lib/email-templates/new-paid-started.hbs index 2caa2be825..c25ac916ed 100644 --- a/ghost/staff-service/lib/email-templates/new-paid-started.hbs +++ b/ghost/staff-service/lib/email-templates/new-paid-started.hbs @@ -48,6 +48,9 @@ {{/if}}

Subscription started on {{subscriptionData.startedOn}}

+ {{#if referrerSource}} + + {{/if}} diff --git a/ghost/staff-service/lib/emails.js b/ghost/staff-service/lib/emails.js index d36ca56c45..089b965a01 100644 --- a/ghost/staff-service/lib/emails.js +++ b/ghost/staff-service/lib/emails.js @@ -16,7 +16,9 @@ class StaffServiceEmails { this.registerPartials(); } - async notifyFreeMemberSignup(member, options) { + async notifyFreeMemberSignup({ + member, attribution + }, options) { const users = await this.models.User.getEmailAlertUsers('free-signup', options); for (const user of users) { @@ -27,6 +29,7 @@ class StaffServiceEmails { const templateData = { memberData, + referrerSource: attribution?.referrerSource, siteTitle: this.settingsCache.get('title'), siteUrl: this.urlUtils.getSiteUrl(), siteDomain: this.siteDomain, @@ -47,7 +50,7 @@ class StaffServiceEmails { } } - async notifyPaidSubscriptionStarted({member, subscription, offer, tier}, options = {}) { + async notifyPaidSubscriptionStarted({member, subscription, offer, tier, attribution}, options = {}) { const users = await this.models.User.getEmailAlertUsers('paid-started', options); for (const user of users) { @@ -72,6 +75,7 @@ class StaffServiceEmails { const templateData = { memberData, + referrerSource: attribution?.referrerSource, tierData, offerData, subscriptionData, diff --git a/ghost/staff-service/lib/staff-service.js b/ghost/staff-service/lib/staff-service.js index 8a41771b14..47fc642e71 100644 --- a/ghost/staff-service/lib/staff-service.js +++ b/ghost/staff-service/lib/staff-service.js @@ -98,13 +98,17 @@ class StaffService { }); if (type === MemberCreatedEvent && member.status === 'free') { - await this.emails.notifyFreeMemberSignup(member); + await this.emails.notifyFreeMemberSignup({ + member, + attribution: event?.data?.attribution + }); } else if (type === SubscriptionActivatedEvent) { await this.emails.notifyPaidSubscriptionStarted({ member, offer, tier, - subscription + subscription, + attribution: event?.data?.attribution }); } else if (type === SubscriptionCancelledEvent) { subscription.canceledAt = event.timestamp; diff --git a/ghost/staff-service/test/staff-service.test.js b/ghost/staff-service/test/staff-service.test.js index f628c12bbd..cc339ed3c9 100644 --- a/ghost/staff-service/test/staff-service.test.js +++ b/ghost/staff-service/test/staff-service.test.js @@ -377,7 +377,7 @@ describe('StaffService', function () { created_at: '2022-08-01T07:30:39.882Z' }; - await service.emails.notifyFreeMemberSignup(member, options); + await service.emails.notifyFreeMemberSignup({member}, options); mailStub.calledOnce.should.be.true(); testCommonMailData(stubs); @@ -402,7 +402,7 @@ describe('StaffService', function () { created_at: '2022-08-01T07:30:39.882Z' }; - await service.emails.notifyFreeMemberSignup(member, options); + await service.emails.notifyFreeMemberSignup({member}, options); mailStub.calledOnce.should.be.true(); testCommonMailData(stubs); @@ -418,6 +418,39 @@ describe('StaffService', function () { sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) ).should.be.true(); }); + + it('sends free member signup alert with attribution', async function () { + const member = { + name: 'Ghost', + email: 'member@example.com', + id: 'abc', + geolocation: '{"country": "France"}', + created_at: '2022-08-01T07:30:39.882Z' + }; + + const attribution = { + referrerSource: 'Twitter' + }; + + await service.emails.notifyFreeMemberSignup({member, attribution}, options); + + mailStub.calledOnce.should.be.true(); + testCommonMailData(stubs); + getEmailAlertUsersStub.calledWith('free-signup').should.be.true(); + + mailStub.calledWith( + sinon.match({subject: '🥳 Free member signup: Ghost'}) + ).should.be.true(); + mailStub.calledWith( + sinon.match.has('html', sinon.match('🥳 Free member signup: Ghost')) + ).should.be.true(); + mailStub.calledWith( + sinon.match.has('html', sinon.match('Created on 1 Aug 2022 • France')) + ).should.be.true(); + mailStub.calledWith( + sinon.match.has('html', sinon.match('Source: Twitter')) + ).should.be.true(); + }); }); describe('notifyPaidSubscriptionStart', function () { @@ -451,6 +484,21 @@ describe('StaffService', function () { }; }); + it('sends paid subscription start alert with attribution', async function () { + const attribution = { + referrerSource: 'Twitter' + }; + await service.emails.notifyPaidSubscriptionStarted({member, offer: null, tier, subscription, attribution}, options); + + mailStub.calledOnce.should.be.true(); + testCommonPaidSubMailData({...stubs, member}); + + // check attribution text + mailStub.calledWith( + sinon.match.has('html', sinon.match('Source: Twitter')) + ).should.be.true(); + }); + it('sends paid subscription start alert without offer', async function () { await service.emails.notifyPaidSubscriptionStarted({member, offer: null, tier, subscription}, options);