Skip to content

Commit

Permalink
feat(payments): rename downloadURL to successActionButtonURL
Browse files Browse the repository at this point in the history
Because:
* The button on the success screen allows for a configurable label,
  which means it can be used for more than just downloading the product,
  which makes the configuration field "downloadURL" misleading.

This commit:
* Adds support for a new configuration field "successActionButtonURL" to
  replace "downloadURL".
* Updates the code to reflect the name change.

Closes #9338
  • Loading branch information
StaberindeZA committed May 17, 2022
1 parent 99ece32 commit e784754
Show file tree
Hide file tree
Showing 23 changed files with 133 additions and 61 deletions.
23 changes: 14 additions & 9 deletions packages/fxa-auth-server/lib/payments/stripe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export type FormattedSubscriptionForEmail = {
planId: string;
planName: string | null;
planEmailIconURL: string;
planDownloadURL: string;
planSuccessActionButtonURL: string;
productMetadata: Stripe.Metadata;
};

Expand Down Expand Up @@ -2354,9 +2354,13 @@ export class StripeHelper extends StripeHelperBase {
const productMetadata = this.mergeMetadata(plan, abbrevProduct);
const {
emailIconURL: planEmailIconURL = '',
downloadURL: planDownloadURL = '',
downloadURL,
successActionButtonURL,
} = productMetadata;

const planSuccessActionButtonURL =
successActionButtonURL || downloadURL || '';

const { lastFour, cardType } = this.extractCardDetails({
charge,
});
Expand All @@ -2382,7 +2386,7 @@ export class StripeHelper extends StripeHelperBase {
planId,
planName,
planEmailIconURL,
planDownloadURL,
planSuccessActionButtonURL,
productMetadata,
showPaymentMethod: !!invoiceTotalInCents,
discountType,
Expand Down Expand Up @@ -2410,15 +2414,20 @@ export class StripeHelper extends StripeHelperBase {
const productMetadata = this.mergeMetadata(plan, abbrevProduct);
const {
emailIconURL: planEmailIconURL = '',
downloadURL: planDownloadURL = '',
downloadURL,
successActionButtonURL,
} = productMetadata;

const planSuccessActionButtonURL =
successActionButtonURL || downloadURL || '';

return {
productId,
productName,
planId,
planName,
planEmailIconURL,
planDownloadURL,
planSuccessActionButtonURL,
productMetadata,
};
}
Expand Down Expand Up @@ -2586,7 +2595,6 @@ export class StripeHelper extends StripeHelperBase {
const {
productOrder: productOrderNew,
emailIconURL: productIconURLNew = '',
downloadURL: productDownloadURLNew = '',
} = productNewMetadata;

const baseDetails = {
Expand All @@ -2597,7 +2605,6 @@ export class StripeHelper extends StripeHelperBase {
productIdNew,
productNameNew,
productIconURLNew,
productDownloadURLNew,
planIdNew,
paymentAmountNewInCents,
paymentAmountNewCurrency,
Expand Down Expand Up @@ -2824,7 +2831,6 @@ export class StripeHelper extends StripeHelperBase {
const {
productOrder: productOrderOld,
emailIconURL: productIconURLOld = '',
downloadURL: productDownloadURLOld = '',
} = this.mergeMetadata(planOld, abbrevProductOld);

const updateType =
Expand All @@ -2838,7 +2844,6 @@ export class StripeHelper extends StripeHelperBase {
productIdOld,
productNameOld,
productIconURLOld,
productDownloadURLOld,
productPaymentCycleOld:
planOld.interval ?? baseDetails.productPaymentCycleNew,
paymentAmountOldInCents,
Expand Down
4 changes: 3 additions & 1 deletion packages/fxa-auth-server/lib/routes/validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ module.exports.subscriptionProductMetadataBaseValidator = isA
.object({
webIconURL: isA.string().uri().required(),
upgradeCTA: isA.string().optional(),
downloadURL: isA.string().uri().required(),
downloadURL: isA.string().uri(), // TODO - Legacy value. Remove once all Stripe Products have been updated.
successActionButtonURL: isA.string().uri(), // TODO - Mark as required, once downloadURL is removed
appStoreLink: isA.string().uri().optional(),
playStoreLink: isA.string().uri().optional(),
productSet: isA.string().optional(),
Expand All @@ -477,6 +478,7 @@ module.exports.subscriptionProductMetadataBaseValidator = isA
.pattern(capabilitiesClientIdPattern, isA.string(), {
fallthrough: true,
})
.or('downloadURL', 'successActionButtonURL') // TODO - Remove once downloadURL is removed.
.unknown(true);

module.exports.subscriptionProductMetadataValidator = {
Expand Down
4 changes: 2 additions & 2 deletions packages/fxa-auth-server/lib/senders/email.js
Original file line number Diff line number Diff line change
Expand Up @@ -2650,7 +2650,7 @@ module.exports = function (log, config, bounces) {
planId,
productName,
planEmailIconURL,
planDownloadURL,
planSuccessActionButtonURL,
uid,
appStoreLink,
playStoreLink,
Expand All @@ -2661,7 +2661,7 @@ module.exports = function (log, config, bounces) {
const query = { plan_id: planId, product_id: productId, uid };
const template = 'downloadSubscription';
const links = this._generateLinks(
planDownloadURL,
planSuccessActionButtonURL,
message,
query,
template,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,16 @@ export class StripeProductsAndPlansConverter {
urlConfig[key] = value;
}
}

// Support legacy downloadURL, which was renamed to successActionButtonURL
// TODO - Remove once all Prod Products have been updated.
if (
!urlConfig['successActionButton'] &&
stripeObject.metadata['downloadURL']
) {
urlConfig['successActionButton'] = stripeObject.metadata['downloadURL'];
}

return urlConfig;
}

Expand Down
6 changes: 3 additions & 3 deletions packages/fxa-auth-server/scripts/write-emails-to-disk.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function sendMail(mailer, messageToSend) {
planId: 'plan-example',
productName: 'Firefox Fortress',
planEmailIconURL: 'http://placekitten.com/512/512',
planDownloadURL: 'http://getfirefox.com/',
planSuccessActionButtonURL: 'http://getfirefox.com/',
planInterval: 'week',
planIntervalCount: 4,
playStoreLink: 'https://example.com/play-store',
Expand Down Expand Up @@ -159,7 +159,7 @@ function sendMail(mailer, messageToSend) {
productMetadata,
providerName: 'Google',
subscription: {
planDownloadURL: 'http://getfirefox.com/',
planSuccessActionButtonURL: 'http://getfirefox.com/',
planEmailIconURL: 'http://placekitten.com/512/512',
planId: 'plan-example',
productId: '0123456789abcdef',
Expand All @@ -168,7 +168,7 @@ function sendMail(mailer, messageToSend) {
},
subscriptions: [
{
planDownloadURL: 'http://getfirefox.com/',
planSuccessActionButtonURL: 'http://getfirefox.com/',
planEmailIconURL: 'http://placekitten.com/512/512',
planId: 'plan-example',
productId: '0123456789abcdef',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const productConfig = {
support: {},
uiContent: {},
urls: {
download: 'https://download.com',
successActionButton: 'https://download.com',
privacyNotice: 'https://privacy.com',
termsOfService: 'https://terms.com',
termsOfServiceDownload: 'https://terms-download.com',
Expand Down
44 changes: 33 additions & 11 deletions packages/fxa-auth-server/test/local/payments/stripe.js
Original file line number Diff line number Diff line change
Expand Up @@ -4439,7 +4439,8 @@ describe('StripeHelper', () => {
const productId = 'prod_00000000000000';
const productName = 'Example Product';
const planEmailIconURL = 'http://example.com/icon';
const planDownloadURL = 'http://example.com/download';
const downloadURL = 'http://example.com/download';
const successActionButtonURL = 'http://example.com/success';
const sourceId = eventCustomerSourceExpiring.data.object.id;
const chargeId = 'ch_1GVm24BVqmGyQTMaUhRAfUmA';
const privacyNoticeURL =
Expand All @@ -4455,7 +4456,7 @@ describe('StripeHelper', () => {
product: productId,
metadata: {
emailIconURL: planEmailIconURL,
downloadURL: planDownloadURL,
successActionButtonURL: successActionButtonURL,
},
};

Expand Down Expand Up @@ -4625,9 +4626,9 @@ describe('StripeHelper', () => {
planId,
planName,
planEmailIconURL,
planDownloadURL,
planSuccessActionButtonURL: successActionButtonURL,
productMetadata: {
downloadURL: planDownloadURL,
successActionButtonURL: successActionButtonURL,
emailIconURL: planEmailIconURL,
'product:privacyNoticeURL': privacyNoticeURL,
'product:termsOfServiceURL': termsOfServiceURL,
Expand Down Expand Up @@ -4737,6 +4738,32 @@ describe('StripeHelper', () => {
});
});

it('planSuccessActionButton URL, falls back to using downloadURL if successActionButtonURL is not available', async () => {
const fixture = deepCopy(invoicePaidSubscriptionCreate);
fixture.lines.data[0].plan = {
id: planId,
nickname: planName,
metadata: {
...mockPlan.metadata,
successActionButtonURL: undefined,
downloadURL,
},
product: mockProduct,
};
const result = await stripeHelper.extractInvoiceDetailsForEmail(
fixture
);
assert.deepEqual(result, {
...expected,
planSuccessActionButtonURL: downloadURL,
productMetadata: {
...expected.productMetadata,
successActionButtonURL: undefined,
downloadURL,
},
});
});

it('extracts expected details from an invoice with discount', async () => {
const result = await stripeHelper.extractInvoiceDetailsForEmail(
fixtureDiscount
Expand Down Expand Up @@ -4942,9 +4969,9 @@ describe('StripeHelper', () => {
planId,
planName,
planEmailIconURL,
planDownloadURL,
planSuccessActionButtonURL: successActionButtonURL,
productMetadata: {
downloadURL: planDownloadURL,
successActionButtonURL,
emailIconURL: planEmailIconURL,
'product:privacyNoticeURL': privacyNoticeURL,
'product:termsOfServiceURL': termsOfServiceURL,
Expand Down Expand Up @@ -5024,8 +5051,6 @@ describe('StripeHelper', () => {
productNameNew: productName,
productIconURLNew:
eventCustomerSubscriptionUpdated.data.object.plan.metadata.emailIconURL,
productDownloadURLNew:
eventCustomerSubscriptionUpdated.data.object.plan.metadata.downloadURL,
planIdNew: planId,
paymentAmountNewCurrency:
eventCustomerSubscriptionUpdated.data.object.plan.currency,
Expand Down Expand Up @@ -5182,7 +5207,6 @@ describe('StripeHelper', () => {
productIdNew,
productNameNew,
productIconURLNew,
productDownloadURLNew,
productMetadata: {
...expectedBaseUpdateDetails.productMetadata,
emailIconURL: productIconURLNew,
Expand Down Expand Up @@ -5255,7 +5279,6 @@ describe('StripeHelper', () => {
productIdOld,
productNameOld,
productIconURLOld,
productDownloadURLOld,
productPaymentCycleOld: event.data.previous_attributes.plan.interval,
paymentAmountOldCurrency:
event.data.previous_attributes.plan.currency,
Expand Down Expand Up @@ -5291,7 +5314,6 @@ describe('StripeHelper', () => {
productIdNew,
productNameNew,
productIconURLNew,
productDownloadURLNew,
productMetadata: {
...expectedBaseUpdateDetails.productMetadata,
emailIconURL: productIconURLNew,
Expand Down
6 changes: 3 additions & 3 deletions packages/fxa-auth-server/test/local/senders/emails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const MESSAGE = {
paymentProratedInCents: 523099.9,
paymentProratedCurrency: 'usd',
payment_provider: 'stripe',
planDownloadURL: 'http://getfirefox.com/',
planSuccessActionButtonURL: 'http://getfirefox.com/',
planId: 'plan-example',
planInterval: 'day',
planIntervalCount: 2,
Expand Down Expand Up @@ -1159,7 +1159,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
])],
['html', [
{ test: 'include', expected: 'https://www.mozilla.org/privacy/websites/' },
{ test: 'include', expected: MESSAGE.planDownloadURL },
{ test: 'include', expected: MESSAGE.planSuccessActionButtonURL },
{ test: 'include', expected: MESSAGE.appStoreLink },
{ test: 'include', expected: MESSAGE.playStoreLink },
{ test: 'include', expected: decodeUrl(configHref('subscriptionPrivacyUrl', 'new-subscription', 'subscription-privacy')) },
Expand All @@ -1177,7 +1177,7 @@ const TESTS: [string, any, Record<string, any>?][] = [
{ test: 'notInclude', expected: 'utm_source=email' },
]],
['text', [
{ test: 'include', expected: MESSAGE.planDownloadURL },
{ test: 'include', expected: MESSAGE.planSuccessActionButtonURL },
{ test: 'include', expected: configUrl('subscriptionPrivacyUrl', 'new-subscription', 'subscription-privacy') },
{ test: 'include', expected: configUrl('subscriptionSettingsUrl', 'new-subscription', 'cancel-subscription', 'plan_id', 'product_id', 'uid', 'email') },
{ test: 'include', expected: configUrl('subscriptionTermsUrl', 'new-subscription', 'subscription-terms') },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,29 @@ describe('StripeProductsAndPlansConverter', () => {
},
};
const expected = {
download: 'http://127.0.0.1:8080/',
successActionButton: 'http://127.0.0.1:8080/',
webIcon: 'https://123done-stage.dev.lcip.org/img/transparent-logo.png',
emailIcon:
'https://123done-stage.dev.lcip.org/img/transparent-logo.png',
appStore: 'https://www.appstore.com',
privacyNotice: 'https://www.privacy.wow',
};
const result = converter.urlMetadataToUrlConfig(testProduct);
assert.deepEqual(expected, result);
});

it('transforms the data - without successActionButtonURL', () => {
const testProduct = {
...deepCopy(product),
metadata: {
...deepCopy(product.metadata),
successActionButtonURL: undefined,
appStoreLink: 'https://www.appstore.com',
'product:privacyNoticeURL': 'https://www.privacy.wow',
},
};
const expected = {
successActionButton: 'http://127.0.0.1:8080/legacy/download',
webIcon: 'https://123done-stage.dev.lcip.org/img/transparent-logo.png',
emailIcon:
'https://123done-stage.dev.lcip.org/img/transparent-logo.png',
Expand Down Expand Up @@ -240,7 +262,7 @@ describe('StripeProductsAndPlansConverter', () => {
support: {},
uiContent: {},
urls: {
download: 'http://127.0.0.1:8080/',
successActionButton: 'http://127.0.0.1:8080/',
privacyNotice: 'http://127.0.0.1:8080/',
termsOfService: 'http://127.0.0.1:8080/',
termsOfServiceDownload: 'http://127.0.0.1:8080/',
Expand Down Expand Up @@ -282,7 +304,7 @@ describe('StripeProductsAndPlansConverter', () => {
upgradeCTA: 'hello <a href="http://example.org">world</a>',
},
urls: {
download: 'https://example.com/download',
successActionButton: 'https://example.com/download',
},
productOrder: 2,
};
Expand Down Expand Up @@ -387,7 +409,7 @@ describe('StripeProductsAndPlansConverter', () => {
support: {},
uiContent: {},
urls: {
download: 'http://127.0.0.1:8080/',
successActionButton: 'http://127.0.0.1:8080/',
privacyNotice: 'http://127.0.0.1:8080/',
termsOfService: 'http://127.0.0.1:8080/',
termsOfServiceDownload: 'http://127.0.0.1:8080/',
Expand Down Expand Up @@ -421,7 +443,7 @@ describe('StripeProductsAndPlansConverter', () => {
upgradeCTA: 'hello <a href="http://example.org">world</a>',
},
urls: {
download: 'https://example.com/download',
successActionButton: 'https://example.com/download',
},
productOrder: 2,
};
Expand All @@ -444,7 +466,7 @@ describe('StripeProductsAndPlansConverter', () => {
upgradeCTA: 'hello <a href="http://example.org">world</a>',
},
urls: {
download: 'https://example.com/download',
successActionButton: 'https://example.com/download',
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class SetPassword extends FormView {
const plan = plans.find((p) => p.product_id === productId);
const url = new URL(
plan && plan.product_metadata
? plan.product_metadata.downloadURL
? plan.product_metadata.successActionButtonURL ||
plan.product_metadata.downloadURL
: 'https://mozilla.org'
);
url.searchParams.set('email', account.get('email'));
Expand Down
Loading

0 comments on commit e784754

Please sign in to comment.