mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
Merge pull request #26494 from overleaf/kh-fix-trial-transitions
[web] fix upgrades/downgrades while on trial GitOrigin-RevId: 4076befc5dbbee32b0cf2a4ff99db96a0cf3ad8b
This commit is contained in:
@@ -134,9 +134,11 @@ class PaymentProviderSubscription {
|
||||
if (newPlan == null) {
|
||||
throw new OError('Unable to find plan in settings', { planCode })
|
||||
}
|
||||
const isInTrial = SubscriptionHelper.isInTrial(this.trialPeriodEnd)
|
||||
const shouldChangeAtTermEnd = SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
currentPlan,
|
||||
newPlan
|
||||
newPlan,
|
||||
isInTrial
|
||||
)
|
||||
|
||||
const changeRequest = new PaymentProviderSubscriptionChangeRequest({
|
||||
@@ -250,9 +252,10 @@ class PaymentProviderSubscription {
|
||||
const addOnUpdates = this.addOns
|
||||
.filter(addOn => addOn.code !== code)
|
||||
.map(addOn => addOn.toAddOnUpdate())
|
||||
const isInTrial = SubscriptionHelper.isInTrial(this.trialPeriodEnd)
|
||||
return new PaymentProviderSubscriptionChangeRequest({
|
||||
subscription: this,
|
||||
timeframe: 'term_end',
|
||||
timeframe: isInTrial ? 'now' : 'term_end',
|
||||
addOnUpdates,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -88,9 +88,13 @@ async function userSubscriptionPage(req, res) {
|
||||
await Modules.promises.hooks.fire('userCanExtendTrial', user)
|
||||
)?.[0]
|
||||
const fromPlansPage = req.query.hasSubscription
|
||||
const isInTrial = SubscriptionHelper.isInTrial(
|
||||
personalSubscription?.payment?.trialEndsAt
|
||||
)
|
||||
const plansData =
|
||||
SubscriptionViewModelBuilder.buildPlansListForSubscriptionDash(
|
||||
personalSubscription?.plan
|
||||
personalSubscription?.plan,
|
||||
isInTrial
|
||||
)
|
||||
|
||||
AnalyticsManager.recordEventForSession(req.session, 'subscription-page-view')
|
||||
|
||||
@@ -6,7 +6,12 @@ const { isStandaloneAiAddOnPlanCode } = require('./PaymentProviderEntities')
|
||||
* If the user changes to a less expensive plan, we shouldn't apply the change immediately.
|
||||
* This is to avoid unintended/artifical credits on users Recurly accounts.
|
||||
*/
|
||||
function shouldPlanChangeAtTermEnd(oldPlan, newPlan) {
|
||||
function shouldPlanChangeAtTermEnd(oldPlan, newPlan, isInTrial) {
|
||||
if (isInTrial) {
|
||||
// we should always upgrade or downgrade immediately if actively in trial
|
||||
return false
|
||||
}
|
||||
|
||||
if (
|
||||
oldPlan.annual === newPlan.annual &&
|
||||
isStandaloneAiAddOnPlanCode(oldPlan.planCode) &&
|
||||
@@ -148,6 +153,14 @@ function getSubscriptionTrialEndsAt(subscription) {
|
||||
return subscription?.paymentProvider?.trialEndsAt
|
||||
}
|
||||
|
||||
function isInTrial(trialEndsAt) {
|
||||
if (!trialEndsAt) {
|
||||
return false
|
||||
}
|
||||
|
||||
return trialEndsAt.getTime() > Date.now()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shouldPlanChangeAtTermEnd,
|
||||
generateInitialLocalizedGroupPrice,
|
||||
@@ -157,4 +170,5 @@ module.exports = {
|
||||
getPaidSubscriptionState,
|
||||
getSubscriptionTrialStartedAt,
|
||||
getSubscriptionTrialEndsAt,
|
||||
isInTrial,
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ async function getUsersSubscriptionDetails(user) {
|
||||
return { bestSubscription, individualSubscription, memberGroupSubscriptions }
|
||||
}
|
||||
|
||||
function buildPlansList(currentPlan) {
|
||||
function buildPlansList(currentPlan, isInTrial) {
|
||||
const { plans } = Settings
|
||||
|
||||
const allPlans = {}
|
||||
@@ -484,7 +484,11 @@ function buildPlansList(currentPlan) {
|
||||
result.planCodesChangingAtTermEnd = _.map(
|
||||
_.filter(plans, plan => {
|
||||
if (!plan.hideFromUsers) {
|
||||
return SubscriptionHelper.shouldPlanChangeAtTermEnd(currentPlan, plan)
|
||||
return SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
currentPlan,
|
||||
plan,
|
||||
isInTrial
|
||||
)
|
||||
}
|
||||
}),
|
||||
'planCode'
|
||||
@@ -569,8 +573,8 @@ function buildGroupSubscriptionForView(groupSubscription) {
|
||||
}
|
||||
}
|
||||
|
||||
function buildPlansListForSubscriptionDash(currentPlan) {
|
||||
const allPlansData = buildPlansList(currentPlan)
|
||||
function buildPlansListForSubscriptionDash(currentPlan, isInTrial) {
|
||||
const allPlansData = buildPlansList(currentPlan, isInTrial)
|
||||
const plans = []
|
||||
// only list individual and visible plans for "change plans" UI
|
||||
if (allPlansData.studentAccounts) {
|
||||
|
||||
@@ -104,6 +104,23 @@ describe('PaymentProviderEntities', function () {
|
||||
)
|
||||
})
|
||||
|
||||
it('returns a change request for downgrades while on trial', function () {
|
||||
const fiveDaysFromNow = new Date()
|
||||
fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5)
|
||||
this.subscription.trialPeriodEnd = fiveDaysFromNow
|
||||
const { PaymentProviderSubscriptionChangeRequest } =
|
||||
this.PaymentProviderEntities
|
||||
const changeRequest =
|
||||
this.subscription.getRequestForPlanChange('cheap-plan')
|
||||
expect(changeRequest).to.deep.equal(
|
||||
new PaymentProviderSubscriptionChangeRequest({
|
||||
subscription: this.subscription,
|
||||
timeframe: 'now',
|
||||
planCode: 'cheap-plan',
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves the AI add-on on upgrades', function () {
|
||||
const { PaymentProviderSubscriptionChangeRequest } =
|
||||
this.PaymentProviderEntities
|
||||
@@ -282,6 +299,22 @@ describe('PaymentProviderEntities', function () {
|
||||
)
|
||||
})
|
||||
|
||||
it('returns a change request when in trial', function () {
|
||||
const fiveDaysFromNow = new Date()
|
||||
fiveDaysFromNow.setDate(fiveDaysFromNow.getDate() + 5)
|
||||
this.subscription.trialPeriodEnd = fiveDaysFromNow
|
||||
const changeRequest = this.subscription.getRequestForAddOnRemoval(
|
||||
this.addOn.code
|
||||
)
|
||||
expect(changeRequest).to.deep.equal(
|
||||
new PaymentProviderSubscriptionChangeRequest({
|
||||
subscription: this.subscription,
|
||||
timeframe: 'now',
|
||||
addOnUpdates: [],
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("throws an AddOnNotPresentError if the subscription doesn't have the add-on", function () {
|
||||
expect(() =>
|
||||
this.subscription.getRequestForAddOnRemoval('another-add-on')
|
||||
|
||||
@@ -102,38 +102,65 @@ describe('SubscriptionHelper', function () {
|
||||
})
|
||||
|
||||
describe('shouldPlanChangeAtTermEnd', function () {
|
||||
it('should return true if the new plan is less expensive', function () {
|
||||
it('should return false if isInTrial is true', function () {
|
||||
const isInTrial = true
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.expensive,
|
||||
plans.cheaper
|
||||
plans.cheaper,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if the new plan is less expensive', function () {
|
||||
const isInTrial = false
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.expensive,
|
||||
plans.cheaper,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.true
|
||||
})
|
||||
|
||||
it('should return false if the new plan is more exepensive', function () {
|
||||
const isInTrial = false
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.cheaper,
|
||||
plans.expensive
|
||||
plans.expensive,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if the new plan is the same price', function () {
|
||||
const isInTrial = false
|
||||
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.cheaper,
|
||||
plans.alsoCheap
|
||||
plans.alsoCheap,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if the change is from an individual plan to a more expensive group plan', function () {
|
||||
const isInTrial = false
|
||||
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.expensive,
|
||||
plans.expensiveGroup
|
||||
plans.expensiveGroup,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if the change is from an individual plan to a cheaper group plan', function () {
|
||||
const isInTrial = false
|
||||
|
||||
const changeAtTermEnd = this.SubscriptionHelper.shouldPlanChangeAtTermEnd(
|
||||
plans.expensive,
|
||||
plans.cheapGroup
|
||||
plans.cheapGroup,
|
||||
isInTrial
|
||||
)
|
||||
expect(changeAtTermEnd).to.be.true
|
||||
})
|
||||
@@ -471,4 +498,25 @@ describe('SubscriptionHelper', function () {
|
||||
expect(result).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
describe('isInTrial', function () {
|
||||
it('should return false if trialEndsAt is null', function () {
|
||||
const result = this.SubscriptionHelper.isInTrial(null)
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('should return false if trialEndsAt is before now', function () {
|
||||
const tenDaysAgo = new Date()
|
||||
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10)
|
||||
const result = this.SubscriptionHelper.isInTrial(tenDaysAgo)
|
||||
expect(result).to.be.false
|
||||
})
|
||||
|
||||
it('should return true if trialEndsAt is after now', function () {
|
||||
const tenDaysFromNow = new Date()
|
||||
tenDaysFromNow.setDate(tenDaysFromNow.getDate() + 10)
|
||||
const result = this.SubscriptionHelper.isInTrial(tenDaysFromNow)
|
||||
expect(result).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user