mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-28 19:41:33 +02:00
Merge pull request #24523 from overleaf/jdt-prevent-bundle-dbl-buys
Redirect bundle purchases when users already have it GitOrigin-RevId: d8e3c0256db08c08c2be24f38caef91fb26b90e8
This commit is contained in:
committed by
Copybot
parent
f11a6a6b87
commit
f7f4a03abb
@@ -197,6 +197,14 @@ async function doSyncFromV1(v1UserId) {
|
||||
return refreshFeatures(user._id, 'sync-v1')
|
||||
}
|
||||
|
||||
async function hasFeaturesViaWritefull(userId) {
|
||||
const user = await UserGetter.promises.getUser(userId, {
|
||||
_id: 1,
|
||||
writefull: 1,
|
||||
})
|
||||
return Boolean(user?.writefull?.isPremium)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
featuresEpochIsCurrent,
|
||||
computeFeatures: callbackify(computeFeatures),
|
||||
@@ -209,10 +217,12 @@ module.exports = {
|
||||
'featuresChanged',
|
||||
]),
|
||||
scheduleRefreshFeatures: callbackify(scheduleRefreshFeatures),
|
||||
hasFeaturesViaWritefull: callbackify(hasFeaturesViaWritefull),
|
||||
promises: {
|
||||
computeFeatures,
|
||||
refreshFeatures,
|
||||
scheduleRefreshFeatures,
|
||||
doSyncFromV1,
|
||||
hasFeaturesViaWritefull,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -477,6 +477,22 @@ function isStandaloneAiAddOnPlanCode(planCode) {
|
||||
return STANDALONE_AI_ADD_ON_CODES.includes(planCode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether subscription change will have have the ai bundle once the change is processed
|
||||
*
|
||||
* @param {RecurlySubscriptionChange} subscriptionChange The subscription change object coming from Recurly
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
function subscriptionChangeIsAiAssistUpgrade(subscriptionChange) {
|
||||
return Boolean(
|
||||
isStandaloneAiAddOnPlanCode(subscriptionChange.nextPlanCode) ||
|
||||
subscriptionChange.nextAddOns?.some(
|
||||
addOn => addOn.code === AI_ADD_ON_CODE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AI_ADD_ON_CODE,
|
||||
MEMBERS_LIMIT_ADD_ON_CODE,
|
||||
@@ -493,5 +509,6 @@ module.exports = {
|
||||
RecurlyCoupon,
|
||||
RecurlyAccount,
|
||||
isStandaloneAiAddOnPlanCode,
|
||||
subscriptionChangeIsAiAssistUpgrade,
|
||||
RecurlyImmediateCharge,
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ const HttpErrorHandler = require('../Errors/HttpErrorHandler')
|
||||
const RecurlyClient = require('./RecurlyClient')
|
||||
const { AI_ADD_ON_CODE } = require('./RecurlyEntities')
|
||||
const PlansLocator = require('./PlansLocator')
|
||||
const RecurlyEntities = require('./RecurlyEntities')
|
||||
|
||||
/**
|
||||
* @import { SubscriptionChangeDescription } from '../../../../types/subscription/subscription-change-preview'
|
||||
@@ -45,7 +46,6 @@ function formatGroupPlansDataForDash() {
|
||||
|
||||
async function userSubscriptionPage(req, res) {
|
||||
const user = SessionManager.getSessionUser(req.session)
|
||||
|
||||
await SplitTestHandler.promises.getAssignment(req, res, 'pause-subscription')
|
||||
|
||||
const groupPricingDiscount = await SplitTestHandler.promises.getAssignment(
|
||||
@@ -321,13 +321,19 @@ async function previewAddonPurchase(req, res) {
|
||||
try {
|
||||
subscriptionChange =
|
||||
await SubscriptionHandler.promises.previewAddonPurchase(userId, addOnCode)
|
||||
|
||||
const hasBundleViaWritefull =
|
||||
await FeaturesUpdater.promises.hasFeaturesViaWritefull(userId)
|
||||
const isAiUpgrade =
|
||||
RecurlyEntities.subscriptionChangeIsAiAssistUpgrade(subscriptionChange)
|
||||
if (hasBundleViaWritefull && isAiUpgrade) {
|
||||
return res.redirect(
|
||||
'/user/subscription?redirect-reason=writefull-entitled'
|
||||
)
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof DuplicateAddOnError) {
|
||||
return HttpErrorHandler.badRequest(
|
||||
req,
|
||||
res,
|
||||
`Subscription already has add-on "${addOnCode}"`
|
||||
)
|
||||
return res.redirect('/user/subscription?redirect-reason=double-buy')
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -670,6 +670,8 @@
|
||||
"go_to_pdf_location_in_code": "",
|
||||
"go_to_settings": "",
|
||||
"go_to_subscriptions": "",
|
||||
"good_news_you_already_purchased_this_add_on": "",
|
||||
"good_news_you_are_already_receiving_this_add_on_via_writefull": "",
|
||||
"group_admin": "",
|
||||
"group_invitations": "",
|
||||
"group_invite_has_been_sent_to_email": "",
|
||||
|
||||
@@ -33,6 +33,27 @@ function PastDueSubscriptionAlert({
|
||||
)
|
||||
}
|
||||
|
||||
function RedirectAlerts() {
|
||||
const queryParams = new URLSearchParams(window.location.search)
|
||||
const redirectReason = queryParams.get('redirect-reason')
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (!redirectReason) {
|
||||
return null
|
||||
}
|
||||
|
||||
let warning
|
||||
if (redirectReason === 'writefull-entitled') {
|
||||
warning = t('good_news_you_are_already_receiving_this_add_on_via_writefull')
|
||||
} else if (redirectReason === 'double-buy') {
|
||||
warning = t('good_news_you_already_purchased_this_add_on')
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return <OLNotification type="warning" content={<>{warning}</>} />
|
||||
}
|
||||
|
||||
function PersonalSubscriptionStates({
|
||||
subscription,
|
||||
}: {
|
||||
@@ -75,6 +96,7 @@ function PersonalSubscription() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<RedirectAlerts />
|
||||
{personalSubscription.recurly.hasPastDueInvoice && (
|
||||
<PastDueSubscriptionAlert subscription={personalSubscription} />
|
||||
)}
|
||||
|
||||
@@ -884,6 +884,8 @@
|
||||
"go_to_previous_page": "Go to previous page",
|
||||
"go_to_settings": "Go to settings",
|
||||
"go_to_subscriptions": "Go to Subscriptions",
|
||||
"good_news_you_already_purchased_this_add_on": "Good news! You already have this add-on, so no need to pay again.",
|
||||
"good_news_you_are_already_receiving_this_add_on_via_writefull": "Good news! You already have this add-on via your Writefull subscription. No need to pay again.",
|
||||
"great_for_getting_started": "Great for getting started",
|
||||
"great_for_small_teams_and_departments": "Great for small teams and departments",
|
||||
"group": "Group",
|
||||
|
||||
Reference in New Issue
Block a user