Przeglądaj źródła

Merge pull request #22032 from overleaf/em-subscription-change-preview-404

Handle user without a subscription when asking for a change preview

GitOrigin-RevId: dab90720b292fd0cfb6dbb8a554797b9d2ac436b
Eric Mc Sween 1 rok temu
rodzic
commit
b0c309e993

+ 0 - 3
services/web/app/src/Features/Subscription/Errors.js

@@ -14,11 +14,8 @@ class DuplicateAddOnError extends OError {}
 
 
 class AddOnNotPresentError extends OError {}
 class AddOnNotPresentError extends OError {}
 
 
-class NoRecurlySubscriptionError extends OError {}
-
 module.exports = {
 module.exports = {
   RecurlyTransactionError,
   RecurlyTransactionError,
   DuplicateAddOnError,
   DuplicateAddOnError,
   AddOnNotPresentError,
   AddOnNotPresentError,
-  NoRecurlySubscriptionError,
 }
 }

+ 6 - 2
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -547,7 +547,11 @@ async function purchaseAddon(req, res, next) {
 
 
   logger.debug({ userId: user._id, addOnCode }, 'purchasing add-ons')
   logger.debug({ userId: user._id, addOnCode }, 'purchasing add-ons')
   try {
   try {
-    await SubscriptionHandler.promises.purchaseAddon(user, addOnCode, quantity)
+    await SubscriptionHandler.promises.purchaseAddon(
+      user._id,
+      addOnCode,
+      quantity
+    )
     return res.sendStatus(200)
     return res.sendStatus(200)
   } catch (err) {
   } catch (err) {
     if (err instanceof DuplicateAddOnError) {
     if (err instanceof DuplicateAddOnError) {
@@ -580,7 +584,7 @@ async function removeAddon(req, res, next) {
   logger.debug({ userId: user._id, addOnCode }, 'removing add-ons')
   logger.debug({ userId: user._id, addOnCode }, 'removing add-ons')
 
 
   try {
   try {
-    await SubscriptionHandler.promises.removeAddon(user, addOnCode)
+    await SubscriptionHandler.promises.removeAddon(user._id, addOnCode)
     res.sendStatus(200)
     res.sendStatus(200)
   } catch (err) {
   } catch (err) {
     if (err instanceof AddOnNotPresentError) {
     if (err instanceof AddOnNotPresentError) {

+ 46 - 43
services/web/app/src/Features/Subscription/SubscriptionHandler.js

@@ -11,7 +11,6 @@ const LimitationsManager = require('./LimitationsManager')
 const EmailHandler = require('../Email/EmailHandler')
 const EmailHandler = require('../Email/EmailHandler')
 const { callbackify } = require('@overleaf/promise-utils')
 const { callbackify } = require('@overleaf/promise-utils')
 const UserUpdater = require('../User/UserUpdater')
 const UserUpdater = require('../User/UserUpdater')
-const { NoRecurlySubscriptionError } = require('./Errors')
 const { NotFoundError } = require('../Errors/Errors')
 const { NotFoundError } = require('../Errors/Errors')
 
 
 /**
 /**
@@ -73,12 +72,7 @@ async function createSubscription(user, subscriptionDetails, recurlyTokenIds) {
  * @return {Promise<RecurlySubscriptionChange>}
  * @return {Promise<RecurlySubscriptionChange>}
  */
  */
 async function previewSubscriptionChange(userId, planCode) {
 async function previewSubscriptionChange(userId, planCode) {
-  const recurlyId = await getSubscriptionRecurlyId(userId)
-  if (recurlyId == null) {
-    throw new NoRecurlySubscriptionError('Subscription not found', { userId })
-  }
-
-  const subscription = await RecurlyClient.promises.getSubscription(recurlyId)
+  const subscription = await getSubscriptionForUser(userId)
   const changeRequest = subscription?.getRequestForPlanChange(planCode)
   const changeRequest = subscription?.getRequestForPlanChange(planCode)
   const change =
   const change =
     await RecurlyClient.promises.previewSubscriptionChange(changeRequest)
     await RecurlyClient.promises.previewSubscriptionChange(changeRequest)
@@ -284,26 +278,6 @@ async function _updateSubscriptionFromRecurly(subscription) {
   )
   )
 }
 }
 
 
-/**
- * @return {Promise<RecurlySubscription>}
- */
-async function _getSubscription(user) {
-  const { hasSubscription = false, subscription } =
-    await LimitationsManager.promises.userHasV2Subscription(user)
-
-  if (!hasSubscription || !subscription?.recurlySubscription_id) {
-    throw new NoRecurlySubscriptionError(
-      "could not fetch the user's Recurly subscription",
-      { userId: user._id }
-    )
-  }
-
-  const currentSub = await RecurlyClient.promises.getSubscription(
-    subscription.recurlySubscription_id
-  )
-  return currentSub
-}
-
 /**
 /**
  * Preview the effect of purchasing an add-on
  * Preview the effect of purchasing an add-on
  *
  *
@@ -312,12 +286,8 @@ async function _getSubscription(user) {
  * @return {Promise<RecurlySubscriptionChange>}
  * @return {Promise<RecurlySubscriptionChange>}
  */
  */
 async function previewAddonPurchase(userId, addOnCode) {
 async function previewAddonPurchase(userId, addOnCode) {
-  const recurlyId = await getSubscriptionRecurlyId(userId)
-  if (recurlyId == null) {
-    throw new NoRecurlySubscriptionError('Subscription not found', { userId })
-  }
+  const subscription = await getSubscriptionForUser(userId)
 
 
-  const subscription = await RecurlyClient.promises.getSubscription(recurlyId)
   try {
   try {
     await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode)
     await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode)
   } catch (err) {
   } catch (err) {
@@ -329,14 +299,22 @@ async function previewAddonPurchase(userId, addOnCode) {
     }
     }
     throw err
     throw err
   }
   }
+
   const changeRequest = subscription.getRequestForAddOnPurchase(addOnCode)
   const changeRequest = subscription.getRequestForAddOnPurchase(addOnCode)
   const change =
   const change =
     await RecurlyClient.promises.previewSubscriptionChange(changeRequest)
     await RecurlyClient.promises.previewSubscriptionChange(changeRequest)
   return change
   return change
 }
 }
 
 
-async function purchaseAddon(user, addOnCode, quantity) {
-  const subscription = await _getSubscription(user)
+/**
+ * Purchase an add-on for a user
+ *
+ * @param {string} userId
+ * @param {string} addOnCode
+ * @param {number} quantity
+ */
+async function purchaseAddon(userId, addOnCode, quantity) {
+  const subscription = await getSubscriptionForUser(userId)
   try {
   try {
     await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode)
     await RecurlyClient.promises.getAddOn(subscription.planCode, addOnCode)
   } catch (err) {
   } catch (err) {
@@ -353,29 +331,54 @@ async function purchaseAddon(user, addOnCode, quantity) {
     quantity
     quantity
   )
   )
   await RecurlyClient.promises.applySubscriptionChangeRequest(changeRequest)
   await RecurlyClient.promises.applySubscriptionChangeRequest(changeRequest)
-  await syncSubscription({ uuid: subscription.id }, user._id)
+  await syncSubscription({ uuid: subscription.id }, userId)
 }
 }
 
 
-async function removeAddon(user, addOnCode) {
-  const subscription = await _getSubscription(user)
+/**
+ * Cancels and add-on for a user
+ *
+ * @param {string} userId
+ * @param {string} addOnCode
+ */
+async function removeAddon(userId, addOnCode) {
+  const subscription = await getSubscriptionForUser(userId)
   const changeRequest = subscription.getRequestForAddOnRemoval(addOnCode)
   const changeRequest = subscription.getRequestForAddOnRemoval(addOnCode)
   await RecurlyClient.promises.applySubscriptionChangeRequest(changeRequest)
   await RecurlyClient.promises.applySubscriptionChangeRequest(changeRequest)
-  await syncSubscription({ uuid: subscription.id }, user._id)
+  await syncSubscription({ uuid: subscription.id }, userId)
 }
 }
 
 
 /**
 /**
  * Returns the Recurly UUID for the given user
  * Returns the Recurly UUID for the given user
  *
  *
+ * Throws a NotFoundError if the subscription can't be found
+ *
  * @param {string} userId
  * @param {string} userId
- * @return {Promise<string | null>} the Recurly UUID
+ * @return {Promise<RecurlySubscription>}
  */
  */
-async function getSubscriptionRecurlyId(userId) {
+async function getSubscriptionForUser(userId) {
   const subscription =
   const subscription =
     await SubscriptionLocator.promises.getUsersSubscription(userId)
     await SubscriptionLocator.promises.getUsersSubscription(userId)
-  if (subscription == null) {
-    return null
+  const recurlyId = subscription?.recurlySubscription_id
+  if (recurlyId == null) {
+    throw new NotFoundError({
+      message: 'Recurly subscription not found',
+      info: { userId },
+    })
+  }
+
+  try {
+    const subscription = await RecurlyClient.promises.getSubscription(recurlyId)
+    return subscription
+  } catch (err) {
+    if (err instanceof recurly.errors.NotFoundError) {
+      throw new NotFoundError({
+        message: 'Subscription not found',
+        info: { userId, recurlyId },
+      })
+    } else {
+      throw err
+    }
   }
   }
-  return subscription.recurlySubscription_id ?? null
 }
 }
 
 
 module.exports = {
 module.exports = {