From 5eed78a3e85995166aa975603a31654891072059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Alby?= Date: Tue, 14 Dec 2021 14:26:07 +0100 Subject: [PATCH] Merge pull request #6099 from overleaf/jpa-recurly-redirect [web] put hosted recurly pages behind a redirect GitOrigin-RevId: 51a45dbcc0b74f1f1ae14eda9ed19a733e2d1334 --- .../Features/Subscription/RecurlyWrapper.js | 1 + .../Subscription/SubscriptionController.js | 12 +++++ .../Subscription/SubscriptionRouter.js | 6 +++ .../SubscriptionViewModelBuilder.js | 51 +++++++++++++------ 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/services/web/app/src/Features/Subscription/RecurlyWrapper.js b/services/web/app/src/Features/Subscription/RecurlyWrapper.js index db95a23f6d..9133544581 100644 --- a/services/web/app/src/Features/Subscription/RecurlyWrapper.js +++ b/services/web/app/src/Features/Subscription/RecurlyWrapper.js @@ -1017,6 +1017,7 @@ const RecurlyWrapper = { } RecurlyWrapper.promises = { + getSubscription: promisify(RecurlyWrapper.getSubscription), updateAccountEmailAddress: promisify(updateAccountEmailAddress), } diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 2ed2ae95b3..ff58a5d4f8 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -482,6 +482,17 @@ async function refreshUserFeatures(req, res) { res.sendStatus(200) } +async function redirectToHostedPage(req, res) { + const userId = SessionManager.getLoggedInUserId(req.session) + const { pageType } = req.params + const url = await SubscriptionViewModelBuilder.promises.getRedirectToHostedPage( + userId, + pageType + ) + logger.warn({ userId, pageType }, 'redirecting to recurly hosted page') + res.redirect(url) +} + module.exports = { plansPage: expressify(plansPage), paymentPage: expressify(paymentPage), @@ -501,4 +512,5 @@ module.exports = { extendTrial: expressify(extendTrial), recurlyNotificationParser, refreshUserFeatures: expressify(refreshUserFeatures), + redirectToHostedPage: expressify(redirectToHostedPage), } diff --git a/services/web/app/src/Features/Subscription/SubscriptionRouter.js b/services/web/app/src/Features/Subscription/SubscriptionRouter.js index 35e4671c6e..94ceb29288 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionRouter.js +++ b/services/web/app/src/Features/Subscription/SubscriptionRouter.js @@ -47,6 +47,12 @@ module.exports = { SubscriptionController.canceledSubscription ) + webRouter.get( + '/user/subscription/recurly/:pageType', + AuthenticationController.requireLogin(), + SubscriptionController.redirectToHostedPage + ) + webRouter.delete( '/subscription/group/user', AuthenticationController.requireLogin(), diff --git a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js index ca1a67e204..d15ec7c0bd 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js +++ b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js @@ -11,23 +11,44 @@ const _ = require('underscore') const async = require('async') const SubscriptionHelper = require('./SubscriptionHelper') const { promisify } = require('../../util/promises') +const { InvalidError, NotFoundError } = require('../Errors/Errors') + +function buildHostedLink(type) { + return `/user/subscription/recurly/${type}` +} + +async function getRedirectToHostedPage(userId, pageType) { + if (!['billing-details', 'account-management'].includes(pageType)) { + throw new InvalidError('unexpected page type') + } + const personalSubscription = await SubscriptionLocator.getUsersSubscription( + userId + ) + const recurlySubscriptionId = personalSubscription?.recurlySubscription_id + if (!recurlySubscriptionId) { + throw new NotFoundError('not a recurly subscription') + } + const recurlySubscription = await RecurlyWrapper.promises.getSubscription( + recurlySubscriptionId, + { includeAccount: true } + ) -function buildHostedLink(recurlySubscription, type) { const recurlySubdomain = Settings.apis.recurly.subdomain const hostedLoginToken = recurlySubscription.account.hosted_login_token + if (!hostedLoginToken) { + throw new Error('recurly account does not have hosted login token') + } let path = '' - if (type === 'billingDetails') { + if (pageType === 'billing-details') { path = 'billing_info/edit?ht=' } - if (hostedLoginToken && recurlySubdomain) { - return [ - 'https://', - recurlySubdomain, - '.recurly.com/account/', - path, - hostedLoginToken, - ].join('') - } + return [ + 'https://', + recurlySubdomain, + '.recurly.com/account/', + path, + hostedLoginToken, + ].join('') } function buildUsersSubscriptionViewModel(user, callback) { @@ -178,11 +199,8 @@ function buildUsersSubscriptionViewModel(user, callback) { taxRate: recurlySubscription.tax_rate ? parseFloat(recurlySubscription.tax_rate._) : 0, - billingDetailsLink: buildHostedLink( - recurlySubscription, - 'billingDetails' - ), - accountManagementLink: buildHostedLink(recurlySubscription), + billingDetailsLink: buildHostedLink('billing-details'), + accountManagementLink: buildHostedLink('account-management'), additionalLicenses, totalLicenses, nextPaymentDueAt: SubscriptionFormatters.formatDate( @@ -335,5 +353,6 @@ module.exports = { buildPlansList, promises: { buildUsersSubscriptionViewModel: promisify(buildUsersSubscriptionViewModel), + getRedirectToHostedPage, }, }