Skip to content

Commit

Permalink
Merge pull request #7653 from mozilla/feat/issue-2898
Browse files Browse the repository at this point in the history
feat(auth-server): Modify transactional emails to accomodate PayPal payments
  • Loading branch information
lesleyjanenorton committed Mar 2, 2021
2 parents f964e23 + d6ad532 commit 6692a2d
Show file tree
Hide file tree
Showing 21 changed files with 291 additions and 138 deletions.
15 changes: 15 additions & 0 deletions packages/fxa-auth-server/lib/payments/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,18 @@ export class StripeHelper {
return await this.stripe.paymentMethods.retrieve(paymentMethodId);
}

getPaymentProvider(customer: Stripe.Customer) {
const subscription = customer.subscriptions?.data.find(
(sub: { status: string }) => ['active', 'past_due'].includes(sub.status)
);
if (subscription) {
return subscription.collection_method === 'send_invoice'
? 'paypal'
: 'stripe';
}
return 'not_chosen';
}

async detachPaymentMethod(
paymentMethodId: string
): Promise<Stripe.PaymentMethod> {
Expand Down Expand Up @@ -1412,11 +1424,14 @@ export class StripeHelper {
charge,
});

const payment_provider = this.getPaymentProvider(customer);

return {
uid,
email,
cardType,
lastFour,
payment_provider,
invoiceNumber,
invoiceTotalInCents,
invoiceTotalCurrency,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ export class StripeWebhookHandler extends StripeHandler {
await this.handleInvoiceOpenEvent(request, event);
}
break;
case 'invoice.payment_succeeded':
await this.handleInvoicePaymentSucceededEvent(request, event);
case 'invoice.paid':
await this.handleInvoicePaidEvent(request, event);
break;
case 'invoice.payment_failed':
await this.handleInvoicePaymentFailedEvent(request, event);
Expand Down Expand Up @@ -230,19 +230,16 @@ export class StripeWebhookHandler extends StripeHandler {
}

/**
* Handle `invoice.payment_succeeded` Stripe wehbook events.
* Handle `invoice.paid` Stripe wehbook events.
*/
async handleInvoicePaymentSucceededEvent(
request: AuthRequest,
event: Stripe.Event
) {
async handleInvoicePaidEvent(request: AuthRequest, event: Stripe.Event) {
const invoice = event.data.object as Stripe.Invoice;
const { uid, email } = await this.sendSubscriptionInvoiceEmail(invoice);
await this.updateCustomer(uid, email);
}

/**
* Handle `invoice.payment_succeeded` Stripe wehbook events.
* Handle `invoice.paid` Stripe wehbook events.
*/
async handleInvoicePaymentFailedEvent(
request: AuthRequest,
Expand Down
19 changes: 5 additions & 14 deletions packages/fxa-auth-server/lib/routes/subscriptions/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,22 +299,13 @@ export class StripeHandler {
return activeSubscriptions;
}

getPaymentProvider(customer: Stripe.Customer) {
const subscription = customer.subscriptions?.data.find(
(sub: { status: string }) => ['active', 'past_due'].includes(sub.status)
);
if (subscription) {
return subscription.collection_method === 'send_invoice'
? 'paypal'
: 'stripe';
}
return 'not_chosen';
}
/**
* Extracts billing details if a customer has a source on file.
*/
extractBillingDetails(customer: Stripe.Customer) {
const defaultPayment = customer.invoice_settings.default_payment_method;
const paymentProvider = this.stripeHelper.getPaymentProvider(customer);

if (defaultPayment) {
if (typeof defaultPayment === 'string') {
// This should always be expanded here.
Expand All @@ -324,7 +315,7 @@ export class StripeHandler {
if (defaultPayment.card) {
return {
billing_name: defaultPayment.billing_details.name,
payment_provider: this.getPaymentProvider(customer),
payment_provider: paymentProvider,
payment_type: defaultPayment.card.funding,
last4: defaultPayment.card.last4,
exp_month: defaultPayment.card.exp_month,
Expand All @@ -340,7 +331,7 @@ export class StripeHandler {
if (src.object === 'card') {
return {
billing_name: src.name,
payment_provider: this.getPaymentProvider(customer),
payment_provider: paymentProvider,
payment_type: src.funding,
last4: src.last4,
exp_month: src.exp_month,
Expand All @@ -351,7 +342,7 @@ export class StripeHandler {
}

return {
payment_provider: this.getPaymentProvider(customer),
payment_provider: paymentProvider,
};
}

Expand Down
6 changes: 6 additions & 0 deletions packages/fxa-auth-server/lib/senders/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,7 @@ module.exports = function (log, config) {
cardType,
lastFour,
nextInvoiceDate,
payment_provider,
} = message;

const enabled = config.subscriptions.transactionalEmails.enabled;
Expand Down Expand Up @@ -2245,6 +2246,7 @@ module.exports = function (log, config) {
invoiceTotalCurrency,
message.acceptLanguage
),
payment_provider,
cardType: cardTypeToText(cardType),
lastFour,
nextInvoiceDate,
Expand All @@ -2269,6 +2271,7 @@ module.exports = function (log, config) {
cardType,
lastFour,
nextInvoiceDate,
payment_provider,
paymentProratedInCents,
paymentProratedCurrency,
} = message;
Expand Down Expand Up @@ -2335,6 +2338,7 @@ module.exports = function (log, config) {
invoiceTotalCurrency,
message.acceptLanguage
),
payment_provider,
cardType: cardTypeToText(cardType),
lastFour,
nextInvoiceDate,
Expand All @@ -2356,6 +2360,7 @@ module.exports = function (log, config) {
invoiceDate,
invoiceTotalInCents,
invoiceTotalCurrency,
payment_provider,
cardType,
lastFour,
nextInvoiceDate,
Expand Down Expand Up @@ -2412,6 +2417,7 @@ module.exports = function (log, config) {
invoiceTotalCurrency,
message.acceptLanguage
),
payment_provider,
cardType: cardTypeToText(cardType),
lastFour,
nextInvoiceDate,
Expand Down
6 changes: 6 additions & 0 deletions packages/fxa-auth-server/lib/senders/templates/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ async function init(log) {
handlebars.txt.registerHelper('t', translate);
handlebars.html.registerHelper('or', orHelper);
handlebars.txt.registerHelper('or', orHelper);
handlebars.html.registerHelper('equal', equalHelper);
handlebars.txt.registerHelper('equal', equalHelper);

// helpers from https://gist.github.com/servel333/21e1eedbd70db5a7cfff327526c72bc5
const reduceOp = function (args, reducer) {
Expand All @@ -47,6 +49,10 @@ async function init(log) {
return reduceOp(arguments, (a, b) => a || b);
}

function equalHelper(a, b, options) {
return reduceOp(arguments, (a, b) => a === b);
}

await forEachTemplate(PARTIALS_DIR, (template, name, type) => {
handlebars[type].registerPartial(name, template);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<br>
{{#if (equal payment_provider "paypal") }}
{{t "PayPal" }}
{{/if}}

{{#if (equal payment_provider "stripe") }}
{{#if lastFour}}
{{t "%(cardType)s card ending in %(lastFour)s" }}
{{/if}}
{{/if}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{#if (equal payment_provider "paypal") }}
{{t "PayPal" }}
{{/if}}

{{#if (equal payment_provider "stripe") }}
{{#if lastFour}}
{{t "%(cardType)s card ending in %(lastFour)s" }}
{{/if}}
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
{{{t "Invoice Number: <b>%(invoiceNumber)s</b>" }}}
<br>
{{t "Charged %(invoiceTotal)s on %(invoiceDateOnly)s" }}
{{#if lastFour}}
<br>
{{t "%(cardType)s card ending in %(lastFour)s" }}
{{/if}}
{{> paymentProvider }}
<br><br>
{{t "Next Invoice: %(nextInvoiceDateOnly)s" }}
<br><br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
{{{t "Invoice Number: %(invoiceNumber)s" }}}

{{{t "Charged %(invoiceTotal)s on %(invoiceDateOnly)s" }}}

{{#if lastFour}}{{{t "%(cardType)s card ending in %(lastFour)s" }}}{{/if}}
{{> paymentProvider }}

{{{t "Next Invoice: %(nextInvoiceDateOnly)s" }}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<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." }}}
{{{t "Help us fix it by <a href=\"%(updateBillingUrl)s\">updating your payment information</a> within 24 hours." }}}
<br><br>
{{t "Unfortunately, if we don’t hear from you within three days, you’ll lose access to %(productName)s." }}
{{t "Unfortunately, if we don’t hear from you within 24 hours, you’ll 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." }}}
{{/inline}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

{{{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:" }}}
{{{t "Help us fix it by updating your payment information within 24 hours:" }}}

{{{updateBillingUrl}}}

{{{t "Unfortunately, if we don’t hear from you within three days, you’ll lose access to %(productName)s." }}}
{{{t "Unfortunately, if we don’t hear from you within 24 hours, you’ll lose access to %(productName)s." }}}

{{{t "Questions about your subscription? Our support team is here to help you:" }}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
{{/inline}}

{{#*inline "content"}}
{{#if lastFour}}
{{t "Your billing cycle and payment will remain the same. Your next charge will be %(invoiceTotal)s to the %(cardType)s card ending in %(lastFour)s on %(nextInvoiceDateOnly)s. Your subscription will automatically renew each billing period unless you choose to cancel." }}
{{else}}
{{t "Your billing cycle and payment will remain the same. Your next charge will be %(invoiceTotal)s on %(nextInvoiceDateOnly)s. Your subscription will automatically renew each billing period unless you choose to cancel." }}
{{/if}}
{{t "Your billing cycle and payment will remain the same. Your next charge will be %(invoiceTotal)s on %(nextInvoiceDateOnly)s. Your subscription will automatically renew each billing period unless you choose to cancel." }}

<br><br>
{{{t "Questions about your subscription? Our <a href=\"%(subscriptionSupportUrl)s\">support team</a> is here to help you." }}}
{{/inline}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@

{{{t "Thank you for reactivating your %(productName)s subscription!" }}}

{{#if lastFour}}
{{t "Your billing cycle and payment will remain the same. Your next charge will be %(invoiceTotal)s to the %(cardType)s card ending in %(lastFour)s on %(nextInvoiceDateOnly)s. Your subscription will automatically renew each billing period unless you choose to cancel." }}
{{else}}
{{t "Your billing cycle and payment will remain the same. Your next charge will be %(invoiceTotal)s on %(nextInvoiceDateOnly)s. Your subscription will automatically renew each billing period unless you choose to cancel." }}
{{/if}}

{{{t "Questions about your subscription? Our support team is here to help you:" }}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
<br>
{{/showProratedAmount}}
{{t "Charged %(invoiceTotal)s on %(invoiceDateOnly)s" }}
{{#if lastFour}}
<br>
{{t "%(cardType)s card ending in %(lastFour)s" }}
{{/if}}
{{> paymentProvider }}
<br><br>
{{t "Next Invoice: %(nextInvoiceDateOnly)s" }}
<br><br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
{{#showProratedAmount}}{{{t "Plan change: %(paymentProrated)s" }}}{{/showProratedAmount}}

{{{t "Charged %(invoiceTotal)s on %(invoiceDateOnly)s" }}}

{{#if lastFour}}{{{t "%(cardType)s card ending in %(lastFour)s" }}}{{/if}}
{{> paymentProvider }}

{{{t "Next Invoice: %(nextInvoiceDateOnly)s" }}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,5 @@
"id": "req_k0H9kTbrxkqtfB",
"idempotency_key": "a9482497-dde5-4ac6-90c2-1d3f5cada747"
},
"type": "invoice.payment_succeeded"
"type": "invoice.paid"
}

0 comments on commit 6692a2d

Please sign in to comment.