From 2ad98aa61c349f64f7ea1f8e5d17f4efdac6dbf5 Mon Sep 17 00:00:00 2001 From: John Lees-Miller Date: Wed, 23 Aug 2023 09:37:33 +0100 Subject: [PATCH] Merge pull request #14460 from overleaf/tm-prevent-checkout-unconfirmed-email Restrict checkout page for users without confirmed primary email GitOrigin-RevId: a02b0f64824f8adc8f15ae24b9132569bd1029f3 --- .../Subscription/SubscriptionController.js | 16 +++++++++++++ .../unconfirmed-primary-email.pug | 11 +++++++++ .../SubscriptionControllerTests.js | 23 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 services/web/app/views/subscriptions/unconfirmed-primary-email.pug diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 2fdb861457..b23af6139f 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -23,6 +23,7 @@ const OError = require('@overleaf/o-error') const SplitTestHandler = require('../SplitTests/SplitTestHandler') const SubscriptionHelper = require('./SubscriptionHelper') const Features = require('../../infrastructure/Features') +const UserGetter = require('../User/UserGetter') const groupPlanModalOptions = Settings.groupPlanModalOptions const validGroupPlanModalOptions = { @@ -193,6 +194,21 @@ async function paymentPage(req, res) { currency = recommendedCurrency } + // Prevent checkout for users without a confirmed primary email address + const userData = await UserGetter.promises.getUser(user._id, { + email: 1, + emails: 1, + }) + const userPrimaryEmail = userData.emails.find( + emailEntry => emailEntry.email === userData.email + ) + if (userPrimaryEmail?.confirmedAt == null) { + return res.render('subscriptions/unconfirmed-primary-email', { + title: 'confirm_email', + email: userData.email, + }) + } + // Block web sales to restricted countries if (['CU', 'IR', 'KP', 'RU', 'SY', 'VE'].includes(countryCode)) { return res.render('subscriptions/restricted-country', { diff --git a/services/web/app/views/subscriptions/unconfirmed-primary-email.pug b/services/web/app/views/subscriptions/unconfirmed-primary-email.pug new file mode 100644 index 0000000000..c7b4fec615 --- /dev/null +++ b/services/web/app/views/subscriptions/unconfirmed-primary-email.pug @@ -0,0 +1,11 @@ +extends ../layout-marketing + +block content + main.content.content-alt#main-content + .container + .error-container + .error-details + p.error-status #{translate("confirm_email")} + p.error-description !{translate("please_confirm_email", {emailAddress: email})} + p.error-actions + a.error-btn(href="/user/settings") #{translate("account_settings")} diff --git a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js index 1bdeba2208..b5fe555c05 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionControllerTests.js @@ -30,6 +30,7 @@ describe('SubscriptionController', function () { email: 'tom@yahoo.com', _id: 'one', signUpDate: new Date('2000-10-01'), + emails: [{ email: 'tom@yahoo.com', confirmedAt: new Date('2000-10-02') }], } this.activeRecurlySubscription = mockSubscriptions['subscription-123-active'] @@ -386,6 +387,28 @@ describe('SubscriptionController', function () { }) }) + describe('with a user that has not confirmed their primary email address', function () { + beforeEach(function () { + this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves( + false + ) + this.PlansLocator.findLocalPlanInSettings.returns({}) + this.UserGetter.promises.getUser.resolves({ + email: 'test@example.com', + emails: [{ email: 'test@example.com' }], + }) + }) + + it('should not render the checkout and instead show the unconfirmed primary email page', function (done) { + this.res.render = (page, opts) => { + page.should.equal('subscriptions/unconfirmed-primary-email') + opts.email.should.equal('test@example.com') + done() + } + this.SubscriptionController.paymentPage(this.req, this.res, done) + }) + }) + describe('with a user from a restricted country', function () { beforeEach(function () { this.LimitationsManager.promises.userHasV1OrV2Subscription.resolves(