From 8b89c31eedd2696806d54e037d8615fa829efebb Mon Sep 17 00:00:00 2001 From: M Fahru Date: Thu, 1 May 2025 08:16:49 -0700 Subject: [PATCH] Merge pull request #25166 from overleaf/kh-cancel-subscription [web] support canceling Stripe subscription GitOrigin-RevId: a72ccb20fbef9b6662cdfa1dcffacbd76dcb694c --- .../Subscription/LimitationsManager.js | 6 ++++- .../Subscription/SubscriptionHandler.js | 23 ++++--------------- .../Subscription/LimitationsManagerTests.js | 13 +++++++++++ .../Subscription/SubscriptionHandlerTests.js | 23 +++++++++++-------- 4 files changed, 36 insertions(+), 29 deletions(-) diff --git a/services/web/app/src/Features/Subscription/LimitationsManager.js b/services/web/app/src/Features/Subscription/LimitationsManager.js index d0c3d29b7b..464a8bc273 100644 --- a/services/web/app/src/Features/Subscription/LimitationsManager.js +++ b/services/web/app/src/Features/Subscription/LimitationsManager.js @@ -128,7 +128,11 @@ async function userHasSubscription(user) { ) let hasValidSubscription = false if (subscription) { - if (subscription.recurlySubscription_id || subscription.customAccount) { + if ( + subscription.recurlySubscription_id || + subscription.paymentProvider?.subscriptionId || + subscription.customAccount + ) { hasValidSubscription = true } } diff --git a/services/web/app/src/Features/Subscription/SubscriptionHandler.js b/services/web/app/src/Features/Subscription/SubscriptionHandler.js index f0fdb22aa2..9cff487aec 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionHandler.js @@ -12,6 +12,7 @@ const EmailHandler = require('../Email/EmailHandler') const { callbackify } = require('@overleaf/promise-utils') const UserUpdater = require('../User/UserUpdater') const { NotFoundError } = require('../Errors/Errors') +const Modules = require('../../infrastructure/Modules') /** * @import { PaymentProviderSubscription, PaymentProviderSubscriptionChange } from './PaymentProviderEntities' @@ -149,10 +150,7 @@ async function cancelSubscription(user) { const { hasSubscription, subscription } = await LimitationsManager.promises.userHasSubscription(user) if (hasSubscription && subscription != null) { - await RecurlyClient.promises.cancelSubscriptionByUuid( - subscription.recurlySubscription_id - ) - await _updateSubscriptionFromRecurly(subscription) + await Modules.promises.hooks.fire('cancelPaidSubscription', subscription) const emailOpts = { to: user.email, first_name: user.first_name, @@ -180,10 +178,10 @@ async function reactivateSubscription(user) { const { hasSubscription, subscription } = await LimitationsManager.promises.userHasSubscription(user) if (hasSubscription && subscription != null) { - await RecurlyClient.promises.reactivateSubscriptionByUuid( - subscription.recurlySubscription_id + await Modules.promises.hooks.fire( + 'reactivatePaidSubscription', + subscription ) - await _updateSubscriptionFromRecurly(subscription) EmailHandler.sendEmail( 'reactivatedSubscription', { to: user.email }, @@ -267,17 +265,6 @@ async function extendTrial(subscription, daysToExend) { ) } -async function _updateSubscriptionFromRecurly(subscription) { - const recurlySubscription = await RecurlyWrapper.promises.getSubscription( - subscription.recurlySubscription_id, - {} - ) - await SubscriptionUpdater.promises.updateSubscriptionFromRecurly( - recurlySubscription, - subscription - ) -} - /** * Preview the effect of purchasing an add-on * diff --git a/services/web/test/unit/src/Subscription/LimitationsManagerTests.js b/services/web/test/unit/src/Subscription/LimitationsManagerTests.js index 29c97de6b6..96de680e82 100644 --- a/services/web/test/unit/src/Subscription/LimitationsManagerTests.js +++ b/services/web/test/unit/src/Subscription/LimitationsManagerTests.js @@ -434,6 +434,19 @@ describe('LimitationsManager', function () { expect(hasSubscription).to.be.true }) + it('should return true if the paymentProvider field is set', async function () { + this.SubscriptionLocator.promises.getUsersSubscription = sinon + .stub() + .resolves({ + paymentProvider: { + subscriptionId: '1234', + }, + }) + const { hasSubscription } = + await this.LimitationsManager.promises.userHasSubscription(this.user) + expect(hasSubscription).to.be.true + }) + it('should return false if the recurly token is not set', async function () { this.SubscriptionLocator.promises.getUsersSubscription.resolves({}) const { hasSubscription } = diff --git a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js index e6ff94dd66..0517e451d4 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js @@ -156,6 +156,13 @@ describe('SubscriptionHandler', function () { '../Email/EmailHandler': this.EmailHandler, '../Analytics/AnalyticsManager': this.AnalyticsManager, '../User/UserUpdater': this.UserUpdater, + '../../infrastructure/Modules': (this.Modules = { + promises: { + hooks: { + fire: sinon.stub(), + }, + }, + }), }, }) }) @@ -426,12 +433,10 @@ describe('SubscriptionHandler', function () { }) it('should cancel the subscription', function () { - this.RecurlyClient.promises.cancelSubscriptionByUuid.called.should.equal( - true + expect(this.Modules.promises.hooks.fire).to.have.been.calledWith( + 'cancelPaidSubscription', + this.subscription ) - this.RecurlyClient.promises.cancelSubscriptionByUuid - .calledWith(this.subscription.recurlySubscription_id) - .should.equal(true) }) it('should send the email after 1 hour', function () { @@ -626,12 +631,10 @@ describe('SubscriptionHandler', function () { }) it('should reactivate the subscription', function () { - this.RecurlyClient.promises.reactivateSubscriptionByUuid.called.should.equal( - true + expect(this.Modules.promises.hooks.fire).to.have.been.calledWith( + 'reactivatePaidSubscription', + this.subscription ) - this.RecurlyClient.promises.reactivateSubscriptionByUuid - .calledWith(this.subscription.recurlySubscription_id) - .should.equal(true) }) it('should send a notification email', function () {