Skip to content

Commit d82cf8c

Browse files
Merge pull request #20298 from mozilla/PAY-3601-subscription-reminder-emails-showing-exlusive-tax-for-non-us-customers
fix(payments-next): Subscription reminder emails showing exclusive tax for non-US customers
2 parents a92db8c + e461934 commit d82cf8c

6 files changed

Lines changed: 271 additions & 4 deletions

File tree

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,13 @@ export const MonthlyPlanNoTax = createStory(
9898
},
9999
'Monthly Plan - No Tax'
100100
);
101+
102+
export const MonthlyPlanInclusiveTax = createStory(
103+
{
104+
showTax: false,
105+
invoiceTotalExcludingTax: undefined,
106+
invoiceTax: undefined,
107+
invoiceTotal: '€4,99',
108+
},
109+
'Monthly Plan - Inclusive Tax (non-US)'
110+
);

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

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ describe('SubscriptionReminders', () => {
400400
subscription: formattedSubscription,
401401
reminderLength: 7,
402402
planInterval: 'month',
403-
showTax: true,
403+
showTax: false,
404404
invoiceTotalExcludingTaxInCents: invoicePreview.total_excluding_tax,
405405
invoiceTaxInCents: invoicePreview.tax,
406406
invoiceTotalInCents: invoicePreview.total,
@@ -1206,6 +1206,9 @@ describe('SubscriptionReminders', () => {
12061206
currency: 'usd',
12071207
discount: null,
12081208
discounts: [],
1209+
total_tax_amounts: [
1210+
{ amount: 200, inclusive: false, tax_rate: { display_name: 'Sales Tax' } },
1211+
],
12091212
};
12101213

12111214
reminder.alreadySentEmail = sandbox.fake.resolves(false);
@@ -1374,6 +1377,75 @@ describe('SubscriptionReminders', () => {
13741377
expect(emailData.invoiceTotalInCents).toBe(1000);
13751378
expect(emailData.invoiceTotalCurrency).toBe('usd');
13761379
});
1380+
1381+
it('handles invoice with inclusive tax (non-US)', async () => {
1382+
const subscription = deepCopy(longSubscription1);
1383+
subscription.customer = {
1384+
1385+
metadata: {
1386+
userid: 'uid',
1387+
},
1388+
};
1389+
subscription.latest_invoice = 'in_test123';
1390+
1391+
const account = {
1392+
emails: [],
1393+
1394+
locale: 'DE',
1395+
};
1396+
1397+
const mockInvoice = {
1398+
id: 'in_test123',
1399+
discount: { id: 'discount_ending' },
1400+
discounts: [],
1401+
};
1402+
1403+
const mockUpcomingInvoiceWithInclusiveTax = {
1404+
total_excluding_tax: 887,
1405+
tax: 113,
1406+
total: 1000,
1407+
currency: 'eur',
1408+
discount: null,
1409+
discounts: [],
1410+
total_tax_amounts: [
1411+
{ amount: 113, inclusive: true, tax_rate: { display_name: 'VAT' } },
1412+
],
1413+
};
1414+
1415+
reminder.alreadySentEmail = sandbox.fake.resolves(false);
1416+
reminder.db.account = sandbox.fake.resolves(account);
1417+
mockLog.info = sandbox.fake.returns({});
1418+
mockStripeHelper.formatSubscriptionForEmail = sandbox.fake.resolves({
1419+
id: 'subscriptionId',
1420+
productMetadata: {},
1421+
planConfig: {},
1422+
});
1423+
mockStripeHelper.findAbbrevPlanById = sandbox.fake.resolves({
1424+
amount: longPlan1.amount,
1425+
currency: longPlan1.currency,
1426+
interval_count: longPlan1.interval_count,
1427+
interval: longPlan1.interval,
1428+
});
1429+
mockStripeHelper.getInvoice = sandbox.fake.resolves(mockInvoice);
1430+
mockStripeHelper.previewInvoiceBySubscriptionId = sandbox.fake.resolves(mockUpcomingInvoiceWithInclusiveTax);
1431+
reminder.mailer.sendSubscriptionRenewalReminderEmail = sandbox.fake.resolves(true);
1432+
reminder.updateSentEmail = sandbox.fake.resolves({});
1433+
Date.now = sinon.fake(() => MOCK_DATETIME_MS);
1434+
1435+
const result = await reminder.sendSubscriptionRenewalReminderEmail(
1436+
subscription,
1437+
longPlan1.id
1438+
);
1439+
1440+
expect(result).toBe(true);
1441+
const mailerCall = reminder.mailer.sendSubscriptionRenewalReminderEmail.getCall(0);
1442+
const emailData = mailerCall.args[2];
1443+
expect(emailData.showTax).toBe(false);
1444+
expect(emailData.invoiceTotalExcludingTaxInCents).toBe(887);
1445+
expect(emailData.invoiceTaxInCents).toBe(113);
1446+
expect(emailData.invoiceTotalInCents).toBe(1000);
1447+
expect(emailData.invoiceTotalCurrency).toBe('eur');
1448+
});
13771449
});
13781450

13791451
describe('sendSubscriptionEndingReminderEmail', () => {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,9 @@ export class SubscriptionReminders {
541541
reminderLength: effectiveReminderDuration.as('days'),
542542
planInterval,
543543
// Using invoice prefix instead of plan to accommodate `yarn write-emails`.
544-
showTax: (invoicePreview.tax ?? 0) > 0,
544+
showTax: (invoicePreview.total_tax_amounts ?? []).some(
545+
(tax: { inclusive: boolean }) => !tax.inclusive
546+
),
545547
invoiceTotalExcludingTaxInCents: invoicePreview.total_excluding_tax,
546548
invoiceTaxInCents: invoicePreview.tax,
547549
invoiceTotalInCents: invoicePreview.total,

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,15 @@ export const MonthlyPlanWithTax = createStory(
8787
invoiceTax: '$2.60',
8888
invoiceTotal: '$22.60',
8989
},
90-
'Monthly Plan - With Tax'
90+
'Monthly Plan - Exclusive Tax, With Tax'
91+
);
92+
93+
export const MonthlyPlanInclusiveTax = createStory(
94+
{
95+
showTax: false,
96+
invoiceTotalExcludingTax: undefined,
97+
invoiceTax: undefined,
98+
invoiceTotal: '€4,99',
99+
},
100+
'Monthly Plan - Inclusive Tax'
91101
);

packages/fxa-auth-server/test/local/payments/subscription-reminders.js

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ describe('SubscriptionReminders', () => {
405405
subscription: formattedSubscription,
406406
reminderLength: 7,
407407
planInterval: 'month',
408-
showTax: true,
408+
showTax: false,
409409
invoiceTotalExcludingTaxInCents: invoicePreview.total_excluding_tax,
410410
invoiceTaxInCents: invoicePreview.tax,
411411
invoiceTotalInCents: invoicePreview.total,
@@ -1211,6 +1211,9 @@ describe('SubscriptionReminders', () => {
12111211
currency: 'usd',
12121212
discount: null,
12131213
discounts: [],
1214+
total_tax_amounts: [
1215+
{ amount: 200, inclusive: false, tax_rate: { display_name: 'Sales Tax' } },
1216+
],
12141217
};
12151218

12161219
reminder.alreadySentEmail = sandbox.fake.resolves(false);
@@ -1379,6 +1382,75 @@ describe('SubscriptionReminders', () => {
13791382
assert.strictEqual(emailData.invoiceTotalInCents, 1000);
13801383
assert.strictEqual(emailData.invoiceTotalCurrency, 'usd');
13811384
});
1385+
1386+
it('handles invoice with inclusive tax (non-US)', async () => {
1387+
const subscription = deepCopy(longSubscription1);
1388+
subscription.customer = {
1389+
1390+
metadata: {
1391+
userid: 'uid',
1392+
},
1393+
};
1394+
subscription.latest_invoice = 'in_test123';
1395+
1396+
const account = {
1397+
emails: [],
1398+
1399+
locale: 'DE',
1400+
};
1401+
1402+
const mockInvoice = {
1403+
id: 'in_test123',
1404+
discount: { id: 'discount_ending' },
1405+
discounts: [],
1406+
};
1407+
1408+
const mockUpcomingInvoiceWithInclusiveTax = {
1409+
total_excluding_tax: 887,
1410+
tax: 113,
1411+
total: 1000,
1412+
currency: 'eur',
1413+
discount: null,
1414+
discounts: [],
1415+
total_tax_amounts: [
1416+
{ amount: 113, inclusive: true, tax_rate: { display_name: 'VAT' } },
1417+
],
1418+
};
1419+
1420+
reminder.alreadySentEmail = sandbox.fake.resolves(false);
1421+
reminder.db.account = sandbox.fake.resolves(account);
1422+
mockLog.info = sandbox.fake.returns({});
1423+
mockStripeHelper.formatSubscriptionForEmail = sandbox.fake.resolves({
1424+
id: 'subscriptionId',
1425+
productMetadata: {},
1426+
planConfig: {},
1427+
});
1428+
mockStripeHelper.findAbbrevPlanById = sandbox.fake.resolves({
1429+
amount: longPlan1.amount,
1430+
currency: longPlan1.currency,
1431+
interval_count: longPlan1.interval_count,
1432+
interval: longPlan1.interval,
1433+
});
1434+
mockStripeHelper.getInvoice = sandbox.fake.resolves(mockInvoice);
1435+
mockStripeHelper.previewInvoiceBySubscriptionId = sandbox.fake.resolves(mockUpcomingInvoiceWithInclusiveTax);
1436+
reminder.mailer.sendSubscriptionRenewalReminderEmail = sandbox.fake.resolves(true);
1437+
reminder.updateSentEmail = sandbox.fake.resolves({});
1438+
Date.now = sinon.fake(() => MOCK_DATETIME_MS);
1439+
1440+
const result = await reminder.sendSubscriptionRenewalReminderEmail(
1441+
subscription,
1442+
longPlan1.id
1443+
);
1444+
1445+
assert.isTrue(result);
1446+
const mailerCall = reminder.mailer.sendSubscriptionRenewalReminderEmail.getCall(0);
1447+
const emailData = mailerCall.args[2];
1448+
assert.isFalse(emailData.showTax);
1449+
assert.strictEqual(emailData.invoiceTotalExcludingTaxInCents, 887);
1450+
assert.strictEqual(emailData.invoiceTaxInCents, 113);
1451+
assert.strictEqual(emailData.invoiceTotalInCents, 1000);
1452+
assert.strictEqual(emailData.invoiceTotalCurrency, 'eur');
1453+
});
13821454
});
13831455

13841456
describe('sendSubscriptionEndingReminderEmail', () => {

packages/fxa-auth-server/test/local/senders/emails.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4622,6 +4622,107 @@ const TESTS: [string, any, Record<string, any>?][] = [
46224622
},
46234623
],
46244624

4625+
[
4626+
'subscriptionRenewalReminderEmail',
4627+
new Map<string, Test | any>([
4628+
[
4629+
'subject',
4630+
{
4631+
test: 'equal',
4632+
expected: `${MESSAGE.subscription.productName} automatic renewal notice`,
4633+
},
4634+
],
4635+
[
4636+
'headers',
4637+
new Map([
4638+
[
4639+
'X-SES-MESSAGE-TAGS',
4640+
{
4641+
test: 'equal',
4642+
expected: sesMessageTagsHeaderValue(
4643+
'subscriptionRenewalReminder'
4644+
),
4645+
},
4646+
],
4647+
[
4648+
'X-Template-Name',
4649+
{ test: 'equal', expected: 'subscriptionRenewalReminder' },
4650+
],
4651+
[
4652+
'X-Template-Version',
4653+
{
4654+
test: 'equal',
4655+
expected: TEMPLATE_VERSIONS.subscriptionRenewalReminder,
4656+
},
4657+
],
4658+
]),
4659+
],
4660+
[
4661+
'html',
4662+
[
4663+
{
4664+
test: 'include',
4665+
expected: `Dear ${MESSAGE.subscription.productName} customer`,
4666+
},
4667+
{
4668+
test: 'include',
4669+
expected: `Your current subscription is set to automatically renew in ${MESSAGE.reminderLength} days.`,
4670+
},
4671+
{
4672+
test: 'include',
4673+
expected: `At that time, Mozilla will renew your daily subscription and a charge of ${MESSAGE_FORMATTED.invoiceTotal} will be applied to the payment method on your account.`,
4674+
},
4675+
{
4676+
test: 'notInclude',
4677+
expected: `${MESSAGE_FORMATTED.invoiceTotalExcludingTax} + ${MESSAGE_FORMATTED.invoiceTaxAmount} tax`,
4678+
},
4679+
{ test: 'include', expected: 'Sincerely,' },
4680+
{
4681+
test: 'include',
4682+
expected: `The ${MESSAGE.subscription.productName} team`,
4683+
},
4684+
{ test: 'notInclude', expected: 'utm_source=email' },
4685+
],
4686+
],
4687+
[
4688+
'text',
4689+
[
4690+
{
4691+
test: 'include',
4692+
expected: `${MESSAGE.subscription.productName} automatic renewal notice`,
4693+
},
4694+
{
4695+
test: 'include',
4696+
expected: `Dear ${MESSAGE.subscription.productName} customer`,
4697+
},
4698+
{
4699+
test: 'include',
4700+
expected: `At that time, Mozilla will renew your daily subscription and a charge of ${MESSAGE_FORMATTED.invoiceTotal} will be applied to the payment method on your account.`,
4701+
},
4702+
{
4703+
test: 'notInclude',
4704+
expected: `${MESSAGE_FORMATTED.invoiceTotalExcludingTax} + ${MESSAGE_FORMATTED.invoiceTaxAmount} tax`,
4705+
},
4706+
{ test: 'include', expected: 'Sincerely,' },
4707+
{
4708+
test: 'include',
4709+
expected: `The ${MESSAGE.subscription.productName} team`,
4710+
},
4711+
{ test: 'notInclude', expected: 'utm_source=email' },
4712+
],
4713+
],
4714+
]),
4715+
{
4716+
updateTemplateValues: (x) => ({
4717+
...x,
4718+
productName: MESSAGE.subscription.productName,
4719+
showTax: false,
4720+
invoiceTaxInCents: undefined,
4721+
invoiceTotalExcludingTaxInCents: undefined,
4722+
}),
4723+
},
4724+
],
4725+
46254726
[
46264727
'freeTrialEndingReminderEmail',
46274728
new Map<string, Test | any>([

0 commit comments

Comments
 (0)