Skip to content

Commit e930dff

Browse files
committed
Added paid member welcome email sending
1 parent 0752da3 commit e930dff

File tree

3 files changed

+473
-1
lines changed

3 files changed

+473
-1
lines changed

ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,10 +971,35 @@ module.exports = class MemberRepository {
971971
options.batch_id = ObjectId().toHexString();
972972
}
973973

974+
const context = options?.context || {};
975+
const source = this._resolveContextSource(context);
976+
const shouldSendPaidWelcomeEmail = config.get('memberWelcomeEmailTestInbox') && WELCOME_EMAIL_SOURCES.includes(source);
977+
let isPaidWelcomeEmailActive = false;
978+
if (shouldSendPaidWelcomeEmail && this._AutomatedEmail) {
979+
const paidWelcomeEmail = await this._AutomatedEmail.findOne({slug: MEMBER_WELCOME_EMAIL_SLUGS.paid}, options);
980+
isPaidWelcomeEmailActive = paidWelcomeEmail && paidWelcomeEmail.get('lexical') && paidWelcomeEmail.get('status') === 'active';
981+
}
982+
974983
const memberModel = await this._Member.findOne({
975984
id: data.id
976985
}, {...options, forUpdate: true});
977986

987+
const queuePaidWelcomeEmail = async (subscriptionModel) => {
988+
await this._Outbox.add({
989+
id: ObjectId().toHexString(),
990+
event_type: MemberCreatedEvent.name,
991+
payload: JSON.stringify({
992+
memberId: memberModel.id,
993+
email: memberModel.get('email'),
994+
name: memberModel.get('name'),
995+
source,
996+
timestamp: subscriptionModel.get('created_at'),
997+
memberStatus: 'paid'
998+
})
999+
}, options);
1000+
this.dispatchEvent(StartOutboxProcessingEvent.create({memberId: memberModel.id}), options);
1001+
};
1002+
9781003
const memberStripeCustomerModel = await memberModel.related('stripeCustomers').query({
9791004
where: {
9801005
customer_id: data.subscription.customer
@@ -1189,6 +1214,9 @@ module.exports = class MemberRepository {
11891214
batchId: options.batch_id
11901215
});
11911216
this.dispatchEvent(subscriptionActivatedEvent, options);
1217+
if (isPaidWelcomeEmailActive) {
1218+
await queuePaidWelcomeEmail(updatedStripeCustomerSubscriptionModel);
1219+
}
11921220
}
11931221

11941222
// Dispatch cancellation event, i.e. send paid cancellation staff notification, if:
@@ -1277,6 +1305,9 @@ module.exports = class MemberRepository {
12771305
batchId: options.batch_id
12781306
});
12791307
this.dispatchEvent(subscriptionActivatedEvent, options);
1308+
if (isPaidWelcomeEmailActive) {
1309+
await queuePaidWelcomeEmail(newStripeCustomerSubscriptionModel);
1310+
}
12801311
}
12811312
}
12821313

ghost/core/test/integration/services/member-welcome-emails.test.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ describe('Member Welcome Emails Integration', function () {
4646
lexical,
4747
created_at: new Date()
4848
});
49+
50+
await db.knex('automated_emails').insert({
51+
id: ObjectId().toHexString(),
52+
status: 'active',
53+
name: 'Paid Member Welcome Email',
54+
slug: MEMBER_WELCOME_EMAIL_SLUGS.paid,
55+
subject: 'Welcome paid member to {{site.title}}',
56+
lexical,
57+
created_at: new Date()
58+
});
4959
});
5060

5161
afterEach(async function () {
@@ -235,6 +245,54 @@ describe('Member Welcome Emails Integration', function () {
235245
assert.equal(entriesAfterJob.length, 1);
236246
assert.ok(entriesAfterJob.models[0].get('message'));
237247
});
248+
249+
it('does not send email when paid template is inactive but entry has memberStatus paid', async function () {
250+
await db.knex('automated_emails')
251+
.where('slug', MEMBER_WELCOME_EMAIL_SLUGS.paid)
252+
.update({status: 'inactive'});
253+
254+
await models.Outbox.add({
255+
event_type: 'MemberCreatedEvent',
256+
payload: JSON.stringify({
257+
memberId: 'paid_member_1',
258+
email: 'paid-inactive@example.com',
259+
name: 'Paid Inactive Template Member',
260+
memberStatus: 'paid'
261+
}),
262+
status: OUTBOX_STATUSES.PENDING
263+
});
264+
265+
await scheduleInlineJob();
266+
267+
assert.equal(mailService.GhostMailer.prototype.send.callCount, 0);
268+
269+
const entriesAfterJob = await models.Outbox.findAll();
270+
assert.equal(entriesAfterJob.length, 1);
271+
assert.ok(entriesAfterJob.models[0].get('message').includes('inactive'));
272+
});
273+
274+
it('does not send email when no paid template exists but entry has memberStatus paid', async function () {
275+
await db.knex('automated_emails').where('slug', MEMBER_WELCOME_EMAIL_SLUGS.paid).del();
276+
277+
await models.Outbox.add({
278+
event_type: 'MemberCreatedEvent',
279+
payload: JSON.stringify({
280+
memberId: 'paid_member_2',
281+
email: 'paid-notemplate@example.com',
282+
name: 'Paid No Template Member',
283+
memberStatus: 'paid'
284+
}),
285+
status: OUTBOX_STATUSES.PENDING
286+
});
287+
288+
await scheduleInlineJob();
289+
290+
assert.equal(mailService.GhostMailer.prototype.send.callCount, 0);
291+
292+
const entriesAfterJob = await models.Outbox.findAll();
293+
assert.equal(entriesAfterJob.length, 1);
294+
assert.ok(entriesAfterJob.models[0].get('message'));
295+
});
238296
});
239297
});
240298

0 commit comments

Comments
 (0)