Skip to content

Commit 99c12c0

Browse files
feat(payments-next): Update subscription reminder emails on discount change
Because: * SubscriptionRenewalReminder doesn't handle the case where a customer’s discount is being replaced by a different discount. This commit: * Handles that case Closes #[PAY-3485](https://mozilla-hub.atlassian.net/browse/PAY-3485)
1 parent 57816b6 commit 99c12c0

11 files changed

Lines changed: 708 additions & 45 deletions

File tree

libs/accounts/email-renderer/src/templates/subscriptionRenewalReminder/index.mjml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,17 @@
1919
</mj-text>
2020

2121
<% if (locals.hadDiscount) { %>
22-
<% if (locals.hasRenewalDiscount) { %>
2322
<mj-text css-class="text-body">
24-
<span data-l10n-id="subscriptionRenewalReminder-content-discount-change">
25-
Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied.
23+
<span data-l10n-id="subscriptionRenewalReminder-content-discount-ending">
24+
Because a previous discount has ended, your subscription will renew at the standard price.
2625
</span>
2726
</mj-text>
28-
<% } else { %>
27+
<% } else if (locals.hasDifferentDiscount) { %>
2928
<mj-text css-class="text-body">
30-
<span data-l10n-id="subscriptionRenewalReminder-content-discount-ending">
31-
Because a previous discount has ended, your subscription will renew at the standard price.
29+
<span data-l10n-id="subscriptionRenewalReminder-content-discount-change">
30+
Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied.
3231
</span>
3332
</mj-text>
34-
<% } %>
3533
<% } %>
3634
3735
<mj-text css-class="text-body">

libs/accounts/email-renderer/src/templates/subscriptionRenewalReminder/index.stories.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const data = {
1919
subscriptionSupportUrl: 'http://localhost:3030/support',
2020
updateBillingUrl: 'http://localhost:3030/subscriptions',
2121
hadDiscount: false,
22-
hasRenewalDiscount: false,
22+
hasDifferentDiscount: false,
2323
};
2424

2525
const createStory = subplatStoryWithProps<TemplateData>(
@@ -59,15 +59,15 @@ export const YearlyPlanDiscountEnding = createStory(
5959
reminderLength: '15',
6060
invoiceTotal: '$199.99',
6161
hadDiscount: true,
62-
hasRenewalDiscount: false,
62+
hasDifferentDiscount: false,
6363
},
6464
'Yearly Plan - Discount Ending'
6565
);
6666

6767
export const MonthlyPlanDiscountChanging = createStory(
6868
{
6969
hadDiscount: true,
70-
hasRenewalDiscount: true,
70+
hasDifferentDiscount: true,
7171
invoiceTotal: '$14.00',
7272
},
7373
'Monthly Plan - Discount Changing'
@@ -80,7 +80,7 @@ export const YearlyPlanDiscountChanging = createStory(
8080
reminderLength: '15',
8181
invoiceTotal: '$139.99',
8282
hadDiscount: true,
83-
hasRenewalDiscount: true,
83+
hasDifferentDiscount: true,
8484
},
8585
'Yearly Plan - Discount Changing'
8686
);

libs/accounts/email-renderer/src/templates/subscriptionRenewalReminder/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type TemplateData = SubscriptionSupportContactTemplateData &
1515
subscriptionSupportUrl: string;
1616
updateBillingUrl: string;
1717
hadDiscount?: boolean;
18-
hasRenewalDiscount?: boolean;
18+
hasDifferentDiscount?: boolean;
1919
};
2020

2121
export const template = 'subscriptionRenewalReminder';

libs/accounts/email-renderer/src/templates/subscriptionRenewalReminder/index.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ subscriptionRenewalReminder-content-greeting = "Dear <%- productName %> customer
77
subscriptionRenewalReminder-content-intro = "Your current subscription is set to automatically renew in <%- reminderLength %> days."
88

99
<% if (locals.hadDiscount) { %>
10-
<% if (locals.hasRenewalDiscount) { %>
11-
subscriptionRenewalReminder-content-discount-change = "Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied."
12-
<% } else { %>
1310
subscriptionRenewalReminder-content-discount-ending = "Because a previous discount has ended, your subscription will renew at the standard price."
14-
<% } %>
11+
<% } else if (locals.hasDifferentDiscount) { %>
12+
subscriptionRenewalReminder-content-discount-change = "Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied."
1513
<% } %>
1614

1715
subscriptionRenewalReminder-content-charge = "At that time, Mozilla will renew your <%- planIntervalCount %> <%- planInterval %> subscription and a charge of <%- invoiceTotal %> will be applied to the payment method on your account."

packages/fxa-auth-server/lib/payments/subscription-reminders.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -266,23 +266,25 @@ export class SubscriptionReminders {
266266
}
267267

268268
/**
269-
* Determine if a discount is ending by checking that the subscription currently
270-
* has a discount but the upcoming invoice does not.
269+
* Determine if a discount is ending by checking that a discount currently exists
270+
* but will not be present on the upcoming invoice does not.
271271
*/
272272
private hasDiscountEnding(
273-
subscription: Stripe.Subscription,
274-
invoicePreview: Stripe.UpcomingInvoice
273+
currentDiscountId: string | null,
274+
upcomingDiscountId: string | null,
275275
): boolean {
276-
return !!subscription.discount && !invoicePreview.discount;
276+
return !!currentDiscountId && !upcomingDiscountId;
277277
}
278278

279279
/**
280-
* Determine if the upcoming invoice has a discount.
280+
* Determine if the upcoming invoice has a discount that is different from
281+
* the current discount.
281282
*/
282-
private hasRenewalDiscount(
283-
invoicePreview: Stripe.UpcomingInvoice
283+
private hasDifferentDiscount(
284+
currentDiscountId: string | null,
285+
upcomingDiscountId: string | null,
284286
): boolean {
285-
return !!invoicePreview.discount;
287+
return !!currentDiscountId && !!upcomingDiscountId && currentDiscountId !== upcomingDiscountId;
286288
}
287289

288290
/**
@@ -347,10 +349,28 @@ export class SubscriptionReminders {
347349
subscriptionId: subscription.id,
348350
});
349351

352+
// Check latest invoice for current discount
353+
let latestInvoice = subscription.latest_invoice;
354+
if (typeof latestInvoice === 'string') {
355+
latestInvoice = await this.stripeHelper.getInvoice(latestInvoice);
356+
}
357+
const currentDiscount = latestInvoice?.discount || latestInvoice?.discounts?.[0];
358+
const currentDiscountId = typeof currentDiscount === 'string'
359+
? currentDiscount
360+
: currentDiscount?.id ?? null;
361+
362+
// Check upcoming invoice for upcoming discount
363+
const upcomingDiscount = invoicePreview.discount || invoicePreview.discounts?.[0];
364+
const upcomingDiscountId = upcomingDiscount
365+
? typeof upcomingDiscount === 'string'
366+
? upcomingDiscount
367+
: upcomingDiscount.id
368+
: null;
369+
350370
// Detect if discount is ending
351-
const hadDiscount = this.hasDiscountEnding(subscription, invoicePreview);
352-
// Detect if renewal has a discount
353-
const hasRenewalDiscount = this.hasRenewalDiscount(invoicePreview);
371+
const hadDiscount = this.hasDiscountEnding(currentDiscountId, upcomingDiscountId);
372+
// Detect if renewal has a different discount
373+
const hasDifferentDiscount = this.hasDifferentDiscount(currentDiscountId, upcomingDiscountId);
354374

355375
await this.mailer.sendSubscriptionRenewalReminderEmail(
356376
account.emails,
@@ -369,7 +389,7 @@ export class SubscriptionReminders {
369389
productMetadata: formattedSubscription.productMetadata,
370390
planConfig: formattedSubscription.planConfig,
371391
hadDiscount,
372-
hasRenewalDiscount,
392+
hasDifferentDiscount,
373393
}
374394
);
375395
await this.updateSentEmail(

packages/fxa-auth-server/lib/senders/email.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3253,7 +3253,7 @@ module.exports = function (log, config, bounces, statsd) {
32533253
message.acceptLanguage
32543254
),
32553255
hadDiscount: message.hadDiscount || false,
3256-
hasRenewalDiscount: message.hasRenewalDiscount || false,
3256+
hasDifferentDiscount: message.hasDifferentDiscount || false,
32573257
},
32583258
});
32593259
};

packages/fxa-auth-server/lib/senders/emails/templates/subscriptionRenewalReminder/index.mjml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,17 @@
1919
</mj-text>
2020

2121
<% if (hadDiscount) { %>
22-
<% if (hasRenewalDiscount) { %>
2322
<mj-text css-class="text-body">
24-
<span data-l10n-id="subscriptionRenewalReminder-content-discount-change">
25-
Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied.
23+
<span data-l10n-id="subscriptionRenewalReminder-content-discount-ending">
24+
Because a previous discount has ended, your subscription will renew at the standard price.
2625
</span>
2726
</mj-text>
28-
<% } else { %>
27+
<% } else if (hasDifferentDiscount) { %>
2928
<mj-text css-class="text-body">
30-
<span data-l10n-id="subscriptionRenewalReminder-content-discount-ending">
31-
Because a previous discount has ended, your subscription will renew at the standard price.
29+
<span data-l10n-id="subscriptionRenewalReminder-content-discount-change">
30+
Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied.
3231
</span>
3332
</mj-text>
34-
<% } %>
3533
<% } %>
3634
3735
<mj-text css-class="text-body">

packages/fxa-auth-server/lib/senders/emails/templates/subscriptionRenewalReminder/index.stories.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const createStory = subplatStoryWithProps(
2121
subscriptionSupportUrl: 'http://localhost:3030/support',
2222
updateBillingUrl: 'http://localhost:3030/subscriptions',
2323
hadDiscount: false,
24-
hasRenewalDiscount: false,
24+
hasDifferentDiscount: false,
2525
}
2626
);
2727

@@ -55,15 +55,15 @@ export const YearlyPlanDiscountEnding = createStory(
5555
reminderLength: '15',
5656
invoiceTotal: '$199.99',
5757
hadDiscount: true,
58-
hasRenewalDiscount: false,
58+
hasDifferentDiscount: false,
5959
},
6060
'Yearly Plan - Discount Ending'
6161
);
6262

6363
export const MonthlyPlanDiscountChanging = createStory(
6464
{
6565
hadDiscount: true,
66-
hasRenewalDiscount: true,
66+
hasDifferentDiscount: true,
6767
invoiceTotal: '$14.00',
6868
},
6969
'Monthly Plan - Discount Changing'
@@ -76,7 +76,7 @@ export const YearlyPlanDiscountChanging = createStory(
7676
reminderLength: '15',
7777
invoiceTotal: '$139.99',
7878
hadDiscount: true,
79-
hasRenewalDiscount: true,
79+
hasDifferentDiscount: true,
8080
},
8181
'Yearly Plan - Discount Changing'
8282
);

packages/fxa-auth-server/lib/senders/emails/templates/subscriptionRenewalReminder/index.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ subscriptionRenewalReminder-content-greeting = "Dear <%- productName %> customer
77
subscriptionRenewalReminder-content-intro = "Your current subscription is set to automatically renew in <%- reminderLength %> days."
88

99
<% if (hadDiscount) { %>
10-
<% if (hasRenewalDiscount) { %>
11-
subscriptionRenewalReminder-content-discount-change = "Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied."
12-
<% } else { %>
1310
subscriptionRenewalReminder-content-discount-ending = "Because a previous discount has ended, your subscription will renew at the standard price."
14-
<% } %>
11+
<% } else if (hasDifferentDiscount) { %>
12+
subscriptionRenewalReminder-content-discount-change = "Your next invoice reflects a change in pricing, as a previous discount has ended and a new discount has been applied."
1513
<% } %>
1614

1715
subscriptionRenewalReminder-content-charge = "At that time, Mozilla will renew your <%- planIntervalCount %> <%- planInterval %> subscription and a charge of <%- invoiceTotal %> will be applied to the payment method on your account."

0 commit comments

Comments
 (0)