Merge pull request #25166 from overleaf/kh-cancel-subscription

[web] support canceling Stripe subscription

GitOrigin-RevId: a72ccb20fbef9b6662cdfa1dcffacbd76dcb694c
This commit is contained in:
M Fahru
2025-05-01 08:16:49 -07:00
committed by Copybot
parent c4586cdd2e
commit 8b89c31eed
4 changed files with 36 additions and 29 deletions
@@ -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
}
}
@@ -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
*
@@ -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 } =
@@ -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 () {