Merge pull request #28202 from overleaf/ls-remove-user-features-for-past-due-stripe-subscriptions

Remove paid user features for past due Stripe subscriptions

GitOrigin-RevId: 07a97f90312db7f6e91cbf15201f71cbdeb2e33c
This commit is contained in:
Liangjun Song
2025-09-08 14:27:41 +01:00
committed by Copybot
parent 8f9a343004
commit b678b545f7
5 changed files with 59 additions and 3 deletions

View File

@@ -120,7 +120,8 @@ async function _getIndividualFeatures(userId) {
await SubscriptionLocator.promises.getUsersSubscription(userId)
if (
subscription == null ||
SubscriptionHelper.getPaidSubscriptionState(subscription) === 'paused'
SubscriptionHelper.getPaidSubscriptionState(subscription) === 'paused' ||
subscription.userFeaturesDisabled
) {
return {}
}
@@ -140,7 +141,9 @@ async function _getIndividualFeatures(userId) {
async function _getGroupFeatureSets(userId) {
const subs =
await SubscriptionLocator.promises.getGroupSubscriptionsMemberOf(userId)
return (subs || []).map(_subscriptionToFeatures)
return (subs || [])
.filter(sub => sub.userFeaturesDisabled !== true)
.map(_subscriptionToFeatures)
}
async function _getFeaturesOverrides(user) {

View File

@@ -94,7 +94,7 @@ const SubscriptionLocator = {
async getGroupSubscriptionsMemberOf(userId) {
return await Subscription.find(
{ member_ids: userId },
{ _id: 1, planCode: 1 }
{ _id: 1, planCode: 1, userFeaturesDisabled: 1 }
)
},

View File

@@ -45,6 +45,7 @@ const SubscriptionSchema = new Schema(
groupSSO: { type: Boolean, default: true },
domainCapture: { type: Boolean, default: false },
},
userFeaturesDisabled: Boolean,
addOns: Schema.Types.Mixed,
overleaf: {
id: {

View File

@@ -135,6 +135,49 @@ describe('FeaturesUpdater', function () {
})
describe('computeFeatures', function () {
describe('when userFeaturesDisabled is true for individual plan', function () {
beforeEach(function () {
this.SubscriptionLocator.promises.getUsersSubscription
.withArgs(this.user._id)
.resolves({
planCode: 'individual-plan',
userFeaturesDisabled: true,
groupPlan: false,
addOns: [this.aiAddOn],
})
})
it('removes all individual plan features', async function () {
const features = await this.FeaturesUpdater.promises.computeFeatures(
this.user._id
)
expect(features).to.deep.equal({ default: 'features' })
})
})
describe('when userFeaturesDisabled is true for group plan', function () {
beforeEach(function () {
const groupSubscription = {
planCode: 'group-plan-1',
userFeaturesDisabled: true,
groupPlan: true,
addOns: [this.aiAddOn],
}
this.SubscriptionLocator.promises.getUsersSubscription
.withArgs(this.user._id)
.resolves(groupSubscription)
this.SubscriptionLocator.promises.getGroupSubscriptionsMemberOf
.withArgs(this.user._id)
.resolves([groupSubscription])
})
it('removes all group plan features', async function () {
const features = await this.FeaturesUpdater.promises.computeFeatures(
this.user._id
)
expect(features).to.deep.equal({ default: 'features' })
})
})
beforeEach(function () {
this.SubscriptionLocator.promises.getUsersSubscription
.withArgs(this.user._id)

View File

@@ -82,6 +82,14 @@ export type InvoiceVoidedWebhookEvent = {
request: Stripe.Event.Request
}
export type InvoiceOverdueWebhookEvent = {
type: 'invoice.overdue'
data: {
object: Stripe.Invoice
}
request: Stripe.Event.Request
}
export type CustomerSubscriptionWebhookEvent =
| CustomerSubscriptionUpdatedWebhookEvent
| CustomerSubscriptionCreatedWebhookEvent
@@ -92,3 +100,4 @@ export type WebhookEvent =
| InvoicePaidWebhookEvent
| InvoiceVoidedWebhookEvent
| PaymentIntentPaymentFailedWebhookEvent
| InvoiceOverdueWebhookEvent