Merge pull request #4781 from lmorchard/fxa-1155-subplat-email-payment-fail

feat(payments): email methods for subplat payment failed and expired
This commit is contained in:
Les Orchard 2020-04-08 10:28:47 -07:00 коммит произвёл GitHub
Родитель 0d676f8c0d b4e97d45fc
Коммит 44f78c9d78
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 298 добавлений и 0 удалений

Просмотреть файл

@ -20,6 +20,8 @@ const { version: VERSION } = require('../../package.json');
// Maps template name to email type
const EMAIL_TYPES = {
subscriptionPaymentExpired: 'subscription_payment_expired',
subscriptionPaymentFailed: 'subscription_payment_failed',
subscriptionAccountDeletion: 'subscription_account_deletion',
subscriptionCancellation: 'subscription_cancellation',
subscriptionSubsequentInvoice: 'subscription_subsequent_invoice',

Просмотреть файл

@ -35,6 +35,8 @@ module.exports = function(log, config, oauthdb) {
// Email template to UTM campaign map, each of these should be unique and
// map to exactly one email template.
const templateNameToCampaignMap = {
subscriptionPaymentExpired: 'subscription-payment-expired',
subscriptionPaymentFailed: 'subscription-payment-failed',
subscriptionAccountDeletion: 'subscription-account-deletion',
subscriptionCancellation: 'subscription-cancellation',
subscriptionSubsequentInvoice: 'subscription-subsequent-invoice',
@ -72,6 +74,8 @@ module.exports = function(log, config, oauthdb) {
// Email template to UTM content, this is typically the main call out link/button
// in template.
const templateNameToContentMap = {
subscriptionPaymentExpired: 'subscriptions',
subscriptionPaymentFailed: 'subscriptions',
subscriptionAccountDeletion: 'subscriptions',
subscriptionCancellation: 'subscriptions',
subscriptionSubsequentInvoice: 'subscriptions',
@ -1802,6 +1806,124 @@ module.exports = function(log, config, oauthdb) {
});
};
Mailer.prototype.subscriptionPaymentExpiredEmail = async function(message) {
const {
email,
uid,
productId,
planId,
planEmailIconURL,
productName,
} = message;
if (!config.subscriptions.transactionalEmails.enabled) {
log.trace('mailer.subscriptionPaymentExpired', {
enabled: false,
email,
productId,
uid,
});
return;
}
log.trace('mailer.subscriptionPaymentExpired', {
enabled: true,
email,
productId,
uid,
});
const query = { plan_id: planId, product_id: productId, uid };
const template = 'subscriptionPaymentExpired';
const translator = this.translator(message.acceptLanguage);
const links = this._generateLinks(null, message, query, template);
const headers = {};
const translatorParams = {
productName,
uid,
email,
};
const subject = translator.gettext(
'Credit card for %(productName)s expiring soon'
);
return this.send({
...message,
headers,
layout: 'subscription',
subject,
template,
templateValues: {
...links,
...translatorParams,
uid,
email,
icon: planEmailIconURL,
product: productName,
subject: translator.format(subject, translatorParams),
},
});
};
Mailer.prototype.subscriptionPaymentFailedEmail = async function(message) {
const {
email,
uid,
productId,
planId,
planEmailIconURL,
productName,
} = message;
if (!config.subscriptions.transactionalEmails.enabled) {
log.trace('mailer.subscriptionPaymentFailed', {
enabled: false,
email,
productId,
uid,
});
return;
}
log.trace('mailer.subscriptionPaymentFailed', {
enabled: true,
email,
productId,
uid,
});
const query = { plan_id: planId, product_id: productId, uid };
const template = 'subscriptionPaymentFailed';
const translator = this.translator(message.acceptLanguage);
const links = this._generateLinks(null, message, query, template);
const headers = {};
const translatorParams = {
productName,
uid,
email,
};
const subject = translator.gettext('%(productName)s payment failed');
return this.send({
...message,
headers,
layout: 'subscription',
subject,
template,
templateValues: {
...links,
...translatorParams,
uid,
email,
icon: planEmailIconURL,
product: productName,
subject: translator.format(subject, translatorParams),
},
});
};
Mailer.prototype.subscriptionAccountDeletionEmail = async function(message) {
const {
email,

Просмотреть файл

@ -1,4 +1,6 @@
{
"subscriptionPaymentExpired": 1,
"subscriptionPaymentFailed": 1,
"subscriptionAccountDeletion": 1,
"subscriptionCancellation": 1,
"subscriptionSubsequentInvoice": 1,

Просмотреть файл

@ -0,0 +1,48 @@
{{> subscriptionIcon}}
<table class="featured-story featured-story--top" cellspacing="0" cellpadding="0">
<tr>
<td style="padding-bottom: 20px;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__inner" style="background: #fff;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__content-inner" style="padding: 32px 30px 45px;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__heading featured-story--top__heading" style="background: #fff;" max-width="640" width="100%" align="left">
<table cellspacing="0" cellpadding="0">
<tr>
<td style="font-family:Helvetica, Geneva, Tahoma, Verdana, sans-serif; font-size: 22px; color: #464646;text-align:center !important;" width="640" align="center">
<b>{{t "Your credit card is about to expire" }}</b>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="featured-story__copy" style="background: #fff;" width="640" align="center">
<table cellspacing="0" cellpadding="0">
<tr>
<td style="font-family:Helvetica, Geneva, Tahoma, Verdana, sans-serif; font-size: 16px; line-height: 22px; color: #555555; padding-top: 16px;text-align:left !important;" align="left">
{{t "The credit card you're using to make payments for %(productName)s is about to expire." }}
<br><br>
{{{t "To prevent any interruption to your service, please <a href=\"%(updateBillingUrl)s\">update your payment information</a> as soon as possible." }}}
<br><br>
{{{t "Questions about your subscription? Our <a href=\"%(subscriptionSupportUrl)s\">support team</a> is here to help you." }}}
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,13 @@
{{{subject}}}
{{{t "Your credit card is about to expire" }}}
{{{t "The credit card you're using to make payments for %(productName)s is about to expire." }}}
{{{t "To prevent any interruption to your service, please update your payment information as soon as possible:" }}}
{{{updateBillingUrl}}}
{{{t "Questions about your subscription? Our support team is here to help you:" }}}
{{{subscriptionSupportUrl}}}

Просмотреть файл

@ -0,0 +1,52 @@
{{> subscriptionIcon}}
<table class="featured-story featured-story--top" cellspacing="0" cellpadding="0">
<tr>
<td style="padding-bottom: 20px;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__inner" style="background: #fff;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__content-inner" style="padding: 32px 30px 45px;">
<table cellspacing="0" cellpadding="0">
<tr>
<td class="featured-story__heading featured-story--top__heading" style="background: #fff;" max-width="640" width="100%" align="left">
<table cellspacing="0" cellpadding="0">
<tr>
<td style="font-family:Helvetica, Geneva, Tahoma, Verdana, sans-serif; font-size: 22px; color: #464646;text-align:center !important;" width="640" align="center">
<b>{{t "Sorry, we're having trouble with your payment" }}</b>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="featured-story__copy" style="background: #fff;" width="640" align="center">
<table cellspacing="0" cellpadding="0">
<tr>
<td style="font-family:Helvetica, Geneva, Tahoma, Verdana, sans-serif; font-size: 16px; line-height: 22px; color: #555555; padding-top: 16px;text-align:left !important;" align="left">
{{t "We had a problem with your latest payment for %(productName)s." }}
<br><br>
{{t "It may be that your credit card has expired, or your current payment method is out of date." }}
<br><br>
{{{t "Help us fix it by <a href=\"%(updateBillingUrl)s\">updating your payment information</a> within three days." }}}
<br><br>
{{t "Unfortunately, if we dont hear from you within three days youll lose access to %(productName)s." }}
<br><br>
{{{t "Questions about your subscription? Our <a href=\"%(subscriptionSupportUrl)s\">support team</a> is here to help you." }}}
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,17 @@
{{{subject}}}
{{{t "Sorry, we're having trouble with your payment" }}}
{{{t "We had a problem with your latest payment for %(productName)s." }}}
{{{t "It may be that your credit card has expired, or your current payment method is out of date." }}}
{{{t "Help us fix it by updating your payment information within three days:" }}}
{{{updateBillingUrl}}}
{{{t "Unfortunately, if we dont hear from you within three days youll lose access to %(productName)s." }}}
{{{t "Questions about your subscription? Our support team is here to help you:" }}}
{{{subscriptionSupportUrl}}}

Просмотреть файл

@ -190,6 +190,8 @@ describe('metrics/amplitude', () => {
postVerifyAddSecondary: 'registration',
postVerifySecondary: 'secondary_email',
recovery: 'reset_password',
subscriptionPaymentExpired: 'subscription_payment_expired',
subscriptionPaymentFailed: 'subscription_payment_failed',
subscriptionAccountDeletion: 'subscription_account_deletion',
subscriptionCancellation: 'subscription_cancellation',
subscriptionFirstInvoice: 'subscription_first_invoice',

Просмотреть файл

@ -280,6 +280,46 @@ const TESTS = new Map([
{ test: 'notInclude', expected: 'utm_source=email' },
]]
])],
['subscriptionPaymentFailedEmail', new Map([
['subject', { test: 'equal', expected: `${MESSAGE.productName} payment failed` }],
['headers', new Map([
['X-SES-MESSAGE-TAGS', { test: 'equal', expected: sesMessageTagsHeaderValue('subscriptionPaymentFailed') }],
['X-Template-Name', { test: 'equal', expected: 'subscriptionPaymentFailed' }],
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.subscriptionPaymentFailed }],
])],
['html', [
{ test: 'include', expected: configHref('privacyUrl', 'subscription-payment-failed', 'privacy') },
{ test: 'include', expected: configHref('subscriptionSettingsUrl', 'subscription-payment-failed', 'cancel-subscription', 'plan_id', 'product_id', 'uid', 'email') },
{ test: 'include', expected: configHref('subscriptionTermsUrl', 'subscription-payment-failed', 'subscription-terms') },
{ test: 'include', expected: `latest payment for ${MESSAGE.productName}.` },
{ test: 'include', expected: `youll lose access to ${MESSAGE.productName}.` },
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: `latest payment for ${MESSAGE.productName}.` },
{ test: 'include', expected: `youll lose access to ${MESSAGE.productName}.` },
{ test: 'notInclude', expected: 'utm_source=email' },
]]
])],
['subscriptionPaymentExpiredEmail', new Map([
['subject', { test: 'equal', expected: `Credit card for ${MESSAGE.productName} expiring soon` }],
['headers', new Map([
['X-SES-MESSAGE-TAGS', { test: 'equal', expected: sesMessageTagsHeaderValue('subscriptionPaymentExpired') }],
['X-Template-Name', { test: 'equal', expected: 'subscriptionPaymentExpired' }],
['X-Template-Version', { test: 'equal', expected: TEMPLATE_VERSIONS.subscriptionPaymentExpired }],
])],
['html', [
{ test: 'include', expected: configHref('privacyUrl', 'subscription-payment-expired', 'privacy') },
{ test: 'include', expected: configHref('subscriptionSettingsUrl', 'subscription-payment-expired', 'cancel-subscription', 'plan_id', 'product_id', 'uid', 'email') },
{ test: 'include', expected: configHref('subscriptionTermsUrl', 'subscription-payment-expired', 'subscription-terms') },
{ test: 'include', expected: `for ${MESSAGE.productName} is about to expire.` },
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: `for ${MESSAGE.productName} is about to expire.` },
{ test: 'notInclude', expected: 'utm_source=email' },
]]
])],
['lowRecoveryCodesEmail', new Map([
['subject', { test: 'equal', expected: '2 recovery codes remaining' }],
['headers', new Map([