diff --git a/services/web/app/src/Features/Subscription/FeaturesUpdater.js b/services/web/app/src/Features/Subscription/FeaturesUpdater.js index c76934ed84..f4bfbcd492 100644 --- a/services/web/app/src/Features/Subscription/FeaturesUpdater.js +++ b/services/web/app/src/Features/Subscription/FeaturesUpdater.js @@ -14,8 +14,7 @@ const UserGetter = require('../User/UserGetter') const AnalyticsManager = require('../Analytics/AnalyticsManager') const Queues = require('../../infrastructure/Queues') const Modules = require('../../infrastructure/Modules') - -const AI_ADDON_CODE = 'assistant' +const { AI_ADD_ON_CODE } = require('./RecurlyEntities') /** * Enqueue a job for refreshing features for the given user @@ -174,7 +173,7 @@ function _subscriptionAddonsToFeatures(addOns) { if (!addOns) { return {} } - const hasAiAddon = addOns.some(addOn => addOn.addOnCode === AI_ADDON_CODE) + const hasAiAddon = addOns.some(addOn => addOn.addOnCode === AI_ADD_ON_CODE) if (hasAiAddon) { return { aiErrorAssistant: true } } else { diff --git a/services/web/app/src/Features/Subscription/RecurlyEntities.js b/services/web/app/src/Features/Subscription/RecurlyEntities.js index a488b4578a..c0f8eb944e 100644 --- a/services/web/app/src/Features/Subscription/RecurlyEntities.js +++ b/services/web/app/src/Features/Subscription/RecurlyEntities.js @@ -5,6 +5,8 @@ const { DuplicateAddOnError, AddOnNotPresentError } = require('./Errors') const PlansLocator = require('./PlansLocator') const SubscriptionHelper = require('./SubscriptionHelper') +const AI_ADD_ON_CODE = 'assistant' + class RecurlySubscription { /** * @param {object} props @@ -261,6 +263,7 @@ class RecurlyAddOn { } module.exports = { + AI_ADD_ON_CODE, RecurlySubscription, RecurlySubscriptionAddOn, RecurlySubscriptionChange, diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index 5e04711b38..6982bea794 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -28,13 +28,12 @@ const SubscriptionFormatters = require('./SubscriptionFormatters') const HttpErrorHandler = require('../Errors/HttpErrorHandler') const { URLSearchParams } = require('url') const RecurlyClient = require('./RecurlyClient') +const { AI_ADD_ON_CODE } = require('./RecurlyEntities') /** * @import { SubscriptionChangePreview } from '../../../../types/subscription/subscription-change-preview' */ -const AI_ADDON_CODES = ['assistant', 'assistant-annual'] - const groupPlanModalOptions = Settings.groupPlanModalOptions const validGroupPlanModalOptions = { plan_code: groupPlanModalOptions.plan_codes.map(item => item.code), @@ -489,7 +488,7 @@ async function previewAddonPurchase(req, res) { const userId = SessionManager.getLoggedInUserId(req.session) const addOnCode = req.params.addOnCode - if (!AI_ADDON_CODES.includes(addOnCode)) { + if (addOnCode !== AI_ADD_ON_CODE) { return HttpErrorHandler.notFound(req, res, `Unknown add-on: ${addOnCode}`) } @@ -559,7 +558,7 @@ async function purchaseAddon(req, res, next) { // currently we only support having a quantity of 1 const quantity = 1 // currently we only support one add-on, the Ai add-on - if (!AI_ADDON_CODES.includes(addOnCode)) { + if (addOnCode !== AI_ADD_ON_CODE) { return res.sendStatus(404) } @@ -591,7 +590,7 @@ async function removeAddon(req, res, next) { const user = SessionManager.getSessionUser(req.session) const addOnCode = req.params.addOnCode - if (!AI_ADDON_CODES.includes(addOnCode)) { + if (addOnCode !== AI_ADD_ON_CODE) { return res.sendStatus(404) } diff --git a/services/web/app/src/Features/Subscription/SubscriptionHandler.js b/services/web/app/src/Features/Subscription/SubscriptionHandler.js index 777ccffa9e..9592c96412 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionHandler.js @@ -1,5 +1,6 @@ // @ts-check +const recurly = require('recurly') const RecurlyWrapper = require('./RecurlyWrapper') const RecurlyClient = require('./RecurlyClient') const { User } = require('../../models/User') @@ -11,9 +12,9 @@ const EmailHandler = require('../Email/EmailHandler') const { callbackify } = require('@overleaf/promise-utils') const UserUpdater = require('../User/UserUpdater') const { NoRecurlySubscriptionError } = require('./Errors') +const { NotFoundError } = require('../Errors/Errors') /** - * @import recurly from 'recurly' * @import { RecurlySubscription, RecurlySubscriptionChange } from './RecurlyEntities' */ @@ -297,6 +298,17 @@ async function previewAddonPurchase(userId, addOnCode) { } const subscription = await RecurlyClient.promises.getSubscription(recurlyId) + try { + await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode) + } catch (err) { + if (err instanceof recurly.errors.NotFoundError) { + throw new NotFoundError({ + message: 'Add-on not found', + info: { addOnCode }, + }) + } + throw err + } const changeRequest = subscription.getRequestForAddOnPurchase(addOnCode) const change = await RecurlyClient.promises.previewSubscriptionChange(changeRequest) @@ -305,6 +317,17 @@ async function previewAddonPurchase(userId, addOnCode) { async function purchaseAddon(user, addOnCode, quantity) { const subscription = await _getSubscription(user) + try { + await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode) + } catch (err) { + if (err instanceof recurly.errors.NotFoundError) { + throw new NotFoundError({ + message: 'Add-on not found', + info: { addOnCode }, + }) + } + throw err + } const changeRequest = subscription.getRequestForAddOnPurchase( addOnCode, quantity