From 36fcc401cc1a7fa1a4a9fa1b616afb2a70bd8cfe Mon Sep 17 00:00:00 2001 From: Kristina <7614497+khjrtbrg@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:27:09 +0200 Subject: [PATCH] Merge pull request #24680 from overleaf/kh-rename-recurly-namespace [web] rename recurly namespace GitOrigin-RevId: b7cfd26923d47bd7f3de4140be24d2d1ef20f6c8 --- .../src/Features/Project/ProjectController.js | 2 +- .../Features/Subscription/FeaturesUpdater.js | 2 +- ...Entities.js => PaymentProviderEntities.js} | 95 +++++++------- .../Features/Subscription/PaymentService.js | 8 +- .../Features/Subscription/RecurlyClient.js | 90 ++++++------- .../Subscription/RecurlyEventHandler.js | 2 +- .../Subscription/SubscriptionController.js | 12 +- .../Subscription/SubscriptionGroupHandler.js | 2 +- .../Subscription/SubscriptionHandler.js | 8 +- .../Subscription/SubscriptionLocator.js | 2 +- .../SubscriptionViewModelBuilder.js | 91 ++++++------- .../app/src/Features/Subscription/types.ts | 5 +- .../views/subscriptions/dashboard-react.pug | 4 +- .../components/dashboard/pause-modal.tsx | 10 +- ...rsonal-subscription-recurly-sync-email.tsx | 4 +- .../dashboard/personal-subscription.tsx | 16 +-- .../dashboard/states/active/active-new.tsx | 52 ++++---- .../dashboard/states/active/active.tsx | 32 ++--- .../dashboard/states/active/add-ons.tsx | 10 +- .../cancel-plan/cancel-subscription.tsx | 8 +- .../active/cancel-subscription-button.tsx | 18 +-- .../change-plan/change-to-group-plan.tsx | 10 +- .../modals/change-to-group-modal.tsx | 4 +- .../states/active/confirm-unpause-modal.tsx | 6 +- .../dashboard/states/active/flash-message.tsx | 6 +- .../dashboard/states/active/paused.tsx | 8 +- .../states/active/pending-plan-change.tsx | 18 +-- .../states/active/subscription-remainder.tsx | 14 +- .../components/dashboard/states/canceled.tsx | 8 +- .../components/dashboard/states/expired.tsx | 6 +- .../components/shared/price-exceptions.tsx | 6 +- .../successful-subscription.tsx | 12 +- .../subscription-dashboard-context.tsx | 24 ++-- .../dashboard/personal-subscription.test.tsx | 8 +- .../dashboard/states/active/active.test.tsx | 36 +++--- .../subscription/fixtures/subscriptions.ts | 42 +++--- .../helpers/render-active-subscription.tsx | 4 +- .../src/Subscription/FeaturesUpdaterTests.js | 2 +- ...Test.js => PaymentProviderEntitiesTest.js} | 120 ++++++++++-------- .../src/Subscription/PaymentServiceTests.js | 14 +- .../src/Subscription/RecurlyClientTests.js | 32 ++--- .../SubscriptionGroupHandlerTests.js | 33 ++--- .../Subscription/SubscriptionHandlerTests.js | 12 +- .../SubscriptionViewModelBuilderTests.js | 81 ++++++------ .../subscription/dashboard/subscription.ts | 23 ++-- services/web/types/subscription/plan.ts | 8 +- 46 files changed, 514 insertions(+), 496 deletions(-) rename services/web/app/src/Features/Subscription/{RecurlyEntities.js => PaymentProviderEntities.js} (82%) rename services/web/test/unit/src/Subscription/{RecurlyEntitiesTest.js => PaymentProviderEntitiesTest.js} (75%) diff --git a/services/web/app/src/Features/Project/ProjectController.js b/services/web/app/src/Features/Project/ProjectController.js index 0d991a5625..89b219ebe9 100644 --- a/services/web/app/src/Features/Project/ProjectController.js +++ b/services/web/app/src/Features/Project/ProjectController.js @@ -49,7 +49,7 @@ const Modules = require('../../infrastructure/Modules') const UserGetter = require('../User/UserGetter') const { isStandaloneAiAddOnPlanCode, -} = require('../Subscription/RecurlyEntities') +} = require('../Subscription/PaymentProviderEntities') const SubscriptionController = require('../Subscription/SubscriptionController.js') const { formatCurrency } = require('../../util/currency') diff --git a/services/web/app/src/Features/Subscription/FeaturesUpdater.js b/services/web/app/src/Features/Subscription/FeaturesUpdater.js index 3eae1942a6..eff88d45fb 100644 --- a/services/web/app/src/Features/Subscription/FeaturesUpdater.js +++ b/services/web/app/src/Features/Subscription/FeaturesUpdater.js @@ -14,7 +14,7 @@ const UserGetter = require('../User/UserGetter') const AnalyticsManager = require('../Analytics/AnalyticsManager') const Queues = require('../../infrastructure/Queues') const Modules = require('../../infrastructure/Modules') -const { AI_ADD_ON_CODE } = require('./RecurlyEntities') +const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities') /** * Enqueue a job for refreshing features for the given user diff --git a/services/web/app/src/Features/Subscription/RecurlyEntities.js b/services/web/app/src/Features/Subscription/PaymentProviderEntities.js similarity index 82% rename from services/web/app/src/Features/Subscription/RecurlyEntities.js rename to services/web/app/src/Features/Subscription/PaymentProviderEntities.js index 2acb7a6c80..472747e52d 100644 --- a/services/web/app/src/Features/Subscription/RecurlyEntities.js +++ b/services/web/app/src/Features/Subscription/PaymentProviderEntities.js @@ -9,7 +9,7 @@ const AI_ADD_ON_CODE = 'assistant' const MEMBERS_LIMIT_ADD_ON_CODE = 'additional-license' const STANDALONE_AI_ADD_ON_CODES = ['assistant', 'assistant-annual'] -class RecurlySubscription { +class PaymentProviderSubscription { /** * @param {object} props * @param {string} props.id @@ -17,7 +17,7 @@ class RecurlySubscription { * @param {string} props.planCode * @param {string} props.planName * @param {number} props.planPrice - * @param {RecurlySubscriptionAddOn[]} [props.addOns] + * @param {PaymentProviderSubscriptionAddOn[]} [props.addOns] * @param {number} props.subtotal * @param {number} [props.taxRate] * @param {number} [props.taxAmount] @@ -26,7 +26,7 @@ class RecurlySubscription { * @param {Date} props.periodStart * @param {Date} props.periodEnd * @param {string} props.collectionMethod - * @param {RecurlySubscriptionChange} [props.pendingChange] + * @param {PaymentProviderSubscriptionChange} [props.pendingChange] * @param {string} [props.service] * @param {string} [props.state] * @param {Date|null} [props.trialPeriodEnd] @@ -97,7 +97,7 @@ class RecurlySubscription { /** * Change this subscription's plan * - * @return {RecurlySubscriptionChangeRequest} + * @return {PaymentProviderSubscriptionChangeRequest} */ getRequestForPlanChange(planCode) { const currentPlan = PlansLocator.findLocalPlanInSettings(this.planCode) @@ -115,7 +115,7 @@ class RecurlySubscription { newPlan ) - const changeRequest = new RecurlySubscriptionChangeRequest({ + const changeRequest = new PaymentProviderSubscriptionChangeRequest({ subscription: this, timeframe: shouldChangeAtTermEnd ? 'term_end' : 'now', planCode, @@ -127,7 +127,7 @@ class RecurlySubscription { (!shouldChangeAtTermEnd && this.hasAddOn(AI_ADD_ON_CODE)) || (shouldChangeAtTermEnd && this.hasAddOnNextPeriod(AI_ADD_ON_CODE)) ) { - const addOnUpdate = new RecurlySubscriptionAddOnUpdate({ + const addOnUpdate = new PaymentProviderSubscriptionAddOnUpdate({ code: AI_ADD_ON_CODE, quantity: 1, }) @@ -143,7 +143,7 @@ class RecurlySubscription { * @param {string} code * @param {number} [quantity] * @param {number} [unitPrice] - * @return {RecurlySubscriptionChangeRequest} - the change request to send to + * @return {PaymentProviderSubscriptionChangeRequest} - the change request to send to * Recurly * * @throws {DuplicateAddOnError} if the add-on is already present on the subscription @@ -158,9 +158,9 @@ class RecurlySubscription { const addOnUpdates = this.addOns.map(addOn => addOn.toAddOnUpdate()) addOnUpdates.push( - new RecurlySubscriptionAddOnUpdate({ code, quantity, unitPrice }) + new PaymentProviderSubscriptionAddOnUpdate({ code, quantity, unitPrice }) ) - return new RecurlySubscriptionChangeRequest({ + return new PaymentProviderSubscriptionChangeRequest({ subscription: this, timeframe: 'now', addOnUpdates, @@ -172,7 +172,7 @@ class RecurlySubscription { * * @param {string} code * @param {number} quantity - * @return {RecurlySubscriptionChangeRequest} - the change request to send to + * @return {PaymentProviderSubscriptionChangeRequest} - the change request to send to * Recurly * * @throws {AddOnNotPresentError} if the subscription doesn't have the add-on @@ -198,7 +198,7 @@ class RecurlySubscription { return update }) - return new RecurlySubscriptionChangeRequest({ + return new PaymentProviderSubscriptionChangeRequest({ subscription: this, timeframe: 'now', addOnUpdates, @@ -209,7 +209,7 @@ class RecurlySubscription { * Remove an add-on from this subscription * * @param {string} code - * @return {RecurlySubscriptionChangeRequest} + * @return {PaymentProviderSubscriptionChangeRequest} * * @throws {AddOnNotPresentError} if the subscription doesn't have the add-on */ @@ -226,7 +226,7 @@ class RecurlySubscription { const addOnUpdates = this.addOns .filter(addOn => addOn.code !== code) .map(addOn => addOn.toAddOnUpdate()) - return new RecurlySubscriptionChangeRequest({ + return new PaymentProviderSubscriptionChangeRequest({ subscription: this, timeframe: 'term_end', addOnUpdates, @@ -237,19 +237,19 @@ class RecurlySubscription { * Upgrade group plan with the plan code provided * * @param {string} newPlanCode - * @return {RecurlySubscriptionChangeRequest} + * @return {PaymentProviderSubscriptionChangeRequest} */ getRequestForGroupPlanUpgrade(newPlanCode) { // Ensure all the existing add-ons are added to the new plan const addOns = this.addOns.map( addOn => - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: addOn.code, quantity: addOn.quantity, }) ) - return new RecurlySubscriptionChangeRequest({ + return new PaymentProviderSubscriptionChangeRequest({ subscription: this, timeframe: 'now', addOnUpdates: addOns, @@ -270,7 +270,7 @@ class RecurlySubscription { /** * An add-on attached to a subscription */ -class RecurlySubscriptionAddOn { +class PaymentProviderSubscriptionAddOn { /** * @param {object} props * @param {string} props.code @@ -290,7 +290,7 @@ class RecurlySubscriptionAddOn { * Return an add-on update that doesn't modify the add-on */ toAddOnUpdate() { - return new RecurlySubscriptionAddOnUpdate({ + return new PaymentProviderSubscriptionAddOnUpdate({ code: this.code, quantity: this.quantity, unitPrice: this.unitPrice, @@ -298,17 +298,19 @@ class RecurlySubscriptionAddOn { } } -class RecurlySubscriptionChangeRequest { +class PaymentProviderSubscriptionChangeRequest { /** * @param {object} props - * @param {RecurlySubscription} props.subscription + * @param {PaymentProviderSubscription} props.subscription * @param {"now" | "term_end"} props.timeframe * @param {string} [props.planCode] - * @param {RecurlySubscriptionAddOnUpdate[]} [props.addOnUpdates] + * @param {PaymentProviderSubscriptionAddOnUpdate[]} [props.addOnUpdates] */ constructor(props) { if (props.planCode == null && props.addOnUpdates == null) { - throw new OError('Invalid RecurlySubscriptionChangeRequest', { props }) + throw new OError('Invalid PaymentProviderSubscriptionChangeRequest', { + props, + }) } this.subscription = props.subscription this.timeframe = props.timeframe @@ -317,7 +319,7 @@ class RecurlySubscriptionChangeRequest { } } -class RecurlySubscriptionAddOnUpdate { +class PaymentProviderSubscriptionAddOnUpdate { /** * @param {object} props * @param {string} props.code @@ -331,15 +333,15 @@ class RecurlySubscriptionAddOnUpdate { } } -class RecurlySubscriptionChange { +class PaymentProviderSubscriptionChange { /** * @param {object} props - * @param {RecurlySubscription} props.subscription + * @param {PaymentProviderSubscription} props.subscription * @param {string} props.nextPlanCode * @param {string} props.nextPlanName * @param {number} props.nextPlanPrice - * @param {RecurlySubscriptionAddOn[]} props.nextAddOns - * @param {RecurlyImmediateCharge} [props.immediateCharge] + * @param {PaymentProviderSubscriptionAddOn[]} props.nextAddOns + * @param {PaymentProviderImmediateCharge} [props.immediateCharge] */ constructor(props) { this.subscription = props.subscription @@ -349,7 +351,12 @@ class RecurlySubscriptionChange { this.nextAddOns = props.nextAddOns this.immediateCharge = props.immediateCharge ?? - new RecurlyImmediateCharge({ subtotal: 0, tax: 0, total: 0, discount: 0 }) + new PaymentProviderImmediateCharge({ + subtotal: 0, + tax: 0, + total: 0, + discount: 0, + }) this.subtotal = this.nextPlanPrice for (const addOn of this.nextAddOns) { @@ -388,7 +395,7 @@ class CreditCardPaymentMethod { } } -class RecurlyImmediateCharge { +class PaymentProviderImmediateCharge { /** * @param {object} props * @param {number} props.subtotal @@ -407,7 +414,7 @@ class RecurlyImmediateCharge { /** * An add-on configuration, independent of any subscription */ -class RecurlyAddOn { +class PaymentProviderAddOn { /** * @param {object} props * @param {string} props.code @@ -422,7 +429,7 @@ class RecurlyAddOn { /** * A plan configuration */ -class RecurlyPlan { +class PaymentProviderPlan { /** * @param {object} props * @param {string} props.code @@ -437,7 +444,7 @@ class RecurlyPlan { /** * A coupon in the payment provider */ -class RecurlyCoupon { +class PaymentProviderCoupon { /** * @param {object} props * @param {string} props.code @@ -454,7 +461,7 @@ class RecurlyCoupon { /** * An account in the payment provider */ -class RecurlyAccount { +class PaymentProviderAccount { /** * @param {object} props * @param {string} props.code @@ -480,7 +487,7 @@ function isStandaloneAiAddOnPlanCode(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 + * @param {PaymentProviderSubscriptionChange} subscriptionChange The subscription change object coming from payment provider * * @return {boolean} */ @@ -497,18 +504,18 @@ module.exports = { AI_ADD_ON_CODE, MEMBERS_LIMIT_ADD_ON_CODE, STANDALONE_AI_ADD_ON_CODES, - RecurlySubscription, - RecurlySubscriptionAddOn, - RecurlySubscriptionChange, - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, + PaymentProviderSubscription, + PaymentProviderSubscriptionAddOn, + PaymentProviderSubscriptionChange, + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, PaypalPaymentMethod, CreditCardPaymentMethod, - RecurlyAddOn, - RecurlyPlan, - RecurlyCoupon, - RecurlyAccount, + PaymentProviderAddOn, + PaymentProviderPlan, + PaymentProviderCoupon, + PaymentProviderAccount, isStandaloneAiAddOnPlanCode, subscriptionChangeIsAiAssistUpgrade, - RecurlyImmediateCharge, + PaymentProviderImmediateCharge, } diff --git a/services/web/app/src/Features/Subscription/PaymentService.js b/services/web/app/src/Features/Subscription/PaymentService.js index 6f8a12e0a0..ad2e75e03e 100644 --- a/services/web/app/src/Features/Subscription/PaymentService.js +++ b/services/web/app/src/Features/Subscription/PaymentService.js @@ -5,7 +5,7 @@ const logger = require('@overleaf/logger') const { callbackify } = require('util') /** - * @import { RecurlySubscription, RecurlyAccount, RecurlyCoupon } from "./RecurlyEntities" + * @import { PaymentProviderSubscription, PaymentProviderAccount, PaymentProviderCoupon } from "./PaymentProviderEntities.js" * @import { ObjectId } from 'mongodb' */ @@ -19,9 +19,9 @@ const { callbackify } = require('util') /** * @typedef {object} PaymentRecord - * @property {RecurlySubscription} subscription - * @property {RecurlyAccount | null} account - * @property {RecurlyCoupon[]} coupons + * @property {PaymentProviderSubscription} subscription + * @property {PaymentProviderAccount | null} account + * @property {PaymentProviderCoupon[]} coupons */ /** diff --git a/services/web/app/src/Features/Subscription/RecurlyClient.js b/services/web/app/src/Features/Subscription/RecurlyClient.js index 91416ddfdd..ee56487b43 100644 --- a/services/web/app/src/Features/Subscription/RecurlyClient.js +++ b/services/web/app/src/Features/Subscription/RecurlyClient.js @@ -7,24 +7,24 @@ const OError = require('@overleaf/o-error') const { callbackify } = require('util') const UserGetter = require('../User/UserGetter') const { - RecurlySubscription, - RecurlySubscriptionAddOn, - RecurlySubscriptionChange, + PaymentProviderSubscription, + PaymentProviderSubscriptionAddOn, + PaymentProviderSubscriptionChange, PaypalPaymentMethod, CreditCardPaymentMethod, - RecurlyAddOn, - RecurlyPlan, - RecurlyCoupon, - RecurlyAccount, - RecurlyImmediateCharge, -} = require('./RecurlyEntities') + PaymentProviderAddOn, + PaymentProviderPlan, + PaymentProviderCoupon, + PaymentProviderAccount, + PaymentProviderImmediateCharge, +} = require('./PaymentProviderEntities') const { MissingBillingInfoError, SubtotalLimitExceededError, } = require('./Errors') /** - * @import { RecurlySubscriptionChangeRequest } from './RecurlyEntities' + * @import { PaymentProviderSubscriptionChangeRequest } from './PaymentProviderEntities' * @import { PaymentMethod } from './types' */ @@ -37,7 +37,7 @@ const client = new recurly.Client(recurlyApiKey) * Get account for a given user * * @param {string} userId - * @return {Promise} + * @return {Promise} */ async function getAccountForUserId(userId) { try { @@ -76,7 +76,7 @@ async function createAccountForUserId(userId) { * Get active coupons for a given user * * @param {string} userId - * @return {Promise} + * @return {Promise} */ async function getActiveCouponsForUserId(userId) { try { @@ -104,7 +104,7 @@ async function getActiveCouponsForUserId(userId) { * Get a subscription from Recurly * * @param {string} subscriptionId - * @return {Promise} + * @return {Promise} */ async function getSubscription(subscriptionId) { const subscription = await client.getSubscription(`uuid-${subscriptionId}`) @@ -118,7 +118,7 @@ async function getSubscription(subscriptionId) { * error if the user has more than one subscription. * * @param {string} userId - * @return {Promise} + * @return {Promise} */ async function getSubscriptionForUser(userId) { try { @@ -153,7 +153,7 @@ async function getSubscriptionForUser(userId) { /** * Request a susbcription change from Recurly * - * @param {RecurlySubscriptionChangeRequest} changeRequest + * @param {PaymentProviderSubscriptionChangeRequest} changeRequest */ async function applySubscriptionChangeRequest(changeRequest) { const body = subscriptionChangeRequestToApi(changeRequest) @@ -193,8 +193,8 @@ async function applySubscriptionChangeRequest(changeRequest) { /** * Preview a subscription change * - * @param {RecurlySubscriptionChangeRequest} changeRequest - * @return {Promise} + * @param {PaymentProviderSubscriptionChangeRequest} changeRequest + * @return {Promise} */ async function previewSubscriptionChange(changeRequest) { const body = subscriptionChangeRequestToApi(changeRequest) @@ -303,7 +303,7 @@ async function getPaymentMethod(userId) { * * @param {string} planCode * @param {string} addOnCode - * @return {Promise} + * @return {Promise} */ async function getAddOn(planCode, addOnCode) { const addOn = await client.getPlanAddOn( @@ -317,7 +317,7 @@ async function getAddOn(planCode, addOnCode) { * Get the configuration for a given plan * * @param {string} planCode - * @return {Promise} + * @return {Promise} */ async function getPlan(planCode) { const plan = await client.getPlan(`code-${planCode}`) @@ -330,10 +330,10 @@ function subscriptionIsCanceledOrExpired(subscription) { } /** - * Build a RecurlyAccount from Recurly API data + * Build a PaymentProviderAccount from Recurly API data * * @param {recurly.Account} apiAccount - * @return {RecurlyAccount} + * @return {PaymentProviderAccount} */ function accountFromApi(apiAccount) { if (apiAccount.code == null || apiAccount.email == null) { @@ -341,7 +341,7 @@ function accountFromApi(apiAccount) { account: apiAccount, }) } - return new RecurlyAccount({ + return new PaymentProviderAccount({ code: apiAccount.code, email: apiAccount.email, hasPastDueInvoice: apiAccount.hasPastDueInvoice ?? false, @@ -349,10 +349,10 @@ function accountFromApi(apiAccount) { } /** - * Build a RecurlyCoupon from Recurly API data + * Build a PaymentProviderCoupon from Recurly API data * * @param {recurly.CouponRedemption} apiRedemption - * @return {RecurlyCoupon} + * @return {PaymentProviderCoupon} */ function couponFromApi(apiRedemption) { if (apiRedemption.coupon == null || apiRedemption.coupon.code == null) { @@ -360,7 +360,7 @@ function couponFromApi(apiRedemption) { coupon: apiRedemption, }) } - return new RecurlyCoupon({ + return new PaymentProviderCoupon({ code: apiRedemption.coupon.code, name: apiRedemption.coupon.name ?? '', description: apiRedemption.coupon.hostedPageDescription ?? '', @@ -368,10 +368,10 @@ function couponFromApi(apiRedemption) { } /** - * Build a RecurlySubscription from Recurly API data + * Build a PaymentProviderSubscription from Recurly API data * * @param {recurly.Subscription} apiSubscription - * @return {RecurlySubscription} + * @return {PaymentProviderSubscription} */ function subscriptionFromApi(apiSubscription) { if ( @@ -394,7 +394,7 @@ function subscriptionFromApi(apiSubscription) { }) } - const subscription = new RecurlySubscription({ + const subscription = new PaymentProviderSubscription({ id: apiSubscription.uuid, userId: apiSubscription.account.code, planCode: apiSubscription.plan.code, @@ -427,10 +427,10 @@ function subscriptionFromApi(apiSubscription) { } /** - * Build a RecurlySubscriptionAddOn from Recurly API data + * Build a PaymentProviderSubscriptionAddOn from Recurly API data * * @param {recurly.SubscriptionAddOn} addOn - * @return {RecurlySubscriptionAddOn} + * @return {PaymentProviderSubscriptionAddOn} */ function subscriptionAddOnFromApi(addOn) { if ( @@ -442,7 +442,7 @@ function subscriptionAddOnFromApi(addOn) { throw new OError('Invalid Recurly add-on', { addOn }) } - return new RecurlySubscriptionAddOn({ + return new PaymentProviderSubscriptionAddOn({ code: addOn.addOn.code, name: addOn.addOn.name, quantity: addOn.quantity ?? 1, @@ -451,11 +451,11 @@ function subscriptionAddOnFromApi(addOn) { } /** - * Build a RecurlySubscriptionChange from Recurly API data + * Build a PaymentProviderSubscriptionChange from Recurly API data * - * @param {RecurlySubscription} subscription - the current subscription + * @param {PaymentProviderSubscription} subscription - the current subscription * @param {recurly.SubscriptionChange} subscriptionChange - the subscription change returned from the API - * @return {RecurlySubscriptionChange} + * @return {PaymentProviderSubscriptionChange} */ function subscriptionChangeFromApi(subscription, subscriptionChange) { if ( @@ -472,7 +472,7 @@ function subscriptionChangeFromApi(subscription, subscriptionChange) { subscriptionAddOnFromApi ) - return new RecurlySubscriptionChange({ + return new PaymentProviderSubscriptionChange({ subscription, nextPlanCode: subscriptionChange.plan.code, nextPlanName: subscriptionChange.plan.name, @@ -486,7 +486,7 @@ function subscriptionChangeFromApi(subscription, subscriptionChange) { * Compute immediate charge based on invoice collection * * @param {recurly.SubscriptionChange} subscriptionChange - the subscription change returned from the API - * @return {RecurlyImmediateCharge} + * @return {PaymentProviderImmediateCharge} */ function computeImmediateCharge(subscriptionChange) { const roundToTwoDecimal = (/** @type {number} */ num) => @@ -506,7 +506,7 @@ function computeImmediateCharge(subscriptionChange) { tax = roundToTwoDecimal(tax + (creditInvoice.tax ?? 0)) discount = roundToTwoDecimal(discount + (creditInvoice.discount ?? 0)) } - return new RecurlyImmediateCharge({ + return new PaymentProviderImmediateCharge({ subtotal, total, tax, @@ -540,41 +540,41 @@ function paymentMethodFromApi(billingInfo) { } /** - * Build a RecurlyAddOn from Recurly API data + * Build a PaymentProviderAddOn from Recurly API data * * @param {recurly.AddOn} addOn - * @return {RecurlyAddOn} + * @return {PaymentProviderAddOn} */ function addOnFromApi(addOn) { if (addOn.code == null || addOn.name == null) { throw new OError('Invalid Recurly add-on', { addOn }) } - return new RecurlyAddOn({ + return new PaymentProviderAddOn({ code: addOn.code, name: addOn.name, }) } /** - * Build a RecurlyPlan from Recurly API data + * Build a PaymentProviderPlan from Recurly API data * * @param {recurly.Plan} plan - * @return {RecurlyPlan} + * @return {PaymentProviderPlan} */ function planFromApi(plan) { if (plan.code == null || plan.name == null) { throw new OError('Invalid Recurly add-on', { plan }) } - return new RecurlyPlan({ + return new PaymentProviderPlan({ code: plan.code, name: plan.name, }) } /** - * Build an API request from a RecurlySubscriptionChangeRequest + * Build an API request from a PaymentProviderSubscriptionChangeRequest * - * @param {RecurlySubscriptionChangeRequest} changeRequest + * @param {PaymentProviderSubscriptionChangeRequest} changeRequest * @return {recurly.SubscriptionChangeCreate} */ function subscriptionChangeRequestToApi(changeRequest) { diff --git a/services/web/app/src/Features/Subscription/RecurlyEventHandler.js b/services/web/app/src/Features/Subscription/RecurlyEventHandler.js index 906e1258ea..d97d57ecba 100644 --- a/services/web/app/src/Features/Subscription/RecurlyEventHandler.js +++ b/services/web/app/src/Features/Subscription/RecurlyEventHandler.js @@ -1,7 +1,7 @@ const SplitTestHandler = require('../SplitTests/SplitTestHandler') const AnalyticsManager = require('../Analytics/AnalyticsManager') const SubscriptionEmailHandler = require('./SubscriptionEmailHandler') -const { AI_ADD_ON_CODE } = require('./RecurlyEntities') +const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities') const { ObjectId } = require('mongodb-legacy') const INVOICE_SUBSCRIPTION_LIMIT = 10 diff --git a/services/web/app/src/Features/Subscription/SubscriptionController.js b/services/web/app/src/Features/Subscription/SubscriptionController.js index c71413ad9e..07a5454be6 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionController.js +++ b/services/web/app/src/Features/Subscription/SubscriptionController.js @@ -22,14 +22,14 @@ const Modules = require('../../infrastructure/Modules') const async = require('async') const HttpErrorHandler = require('../Errors/HttpErrorHandler') const RecurlyClient = require('./RecurlyClient') -const { AI_ADD_ON_CODE } = require('./RecurlyEntities') +const { AI_ADD_ON_CODE } = require('./PaymentProviderEntities') const PlansLocator = require('./PlansLocator') -const RecurlyEntities = require('./RecurlyEntities') +const PaymentProviderEntities = require('./PaymentProviderEntities') /** * @import { SubscriptionChangeDescription } from '../../../../types/subscription/subscription-change-preview' * @import { SubscriptionChangePreview } from '../../../../types/subscription/subscription-change-preview' - * @import { RecurlySubscriptionChange } from './RecurlyEntities' + * @import { PaymentProviderSubscriptionChange } from './PaymentProviderEntities' * @import { PaymentMethod } from './types' */ @@ -325,7 +325,9 @@ async function previewAddonPurchase(req, res) { const hasBundleViaWritefull = await FeaturesUpdater.promises.hasFeaturesViaWritefull(userId) const isAiUpgrade = - RecurlyEntities.subscriptionChangeIsAiAssistUpgrade(subscriptionChange) + PaymentProviderEntities.subscriptionChangeIsAiAssistUpgrade( + subscriptionChange + ) if (hasBundleViaWritefull && isAiUpgrade) { return res.redirect( '/user/subscription?redirect-reason=writefull-entitled' @@ -724,7 +726,7 @@ function getPlanNameForDisplay(planName, planCode) { * Build a subscription change preview for display purposes * * @param {SubscriptionChangeDescription} subscriptionChangeDescription A description of the change for the frontend - * @param {RecurlySubscriptionChange} subscriptionChange The subscription change object coming from Recurly + * @param {PaymentProviderSubscriptionChange} subscriptionChange The subscription change object coming from Recurly * @param {PaymentMethod} paymentMethod The payment method associated to the user * @return {SubscriptionChangePreview} */ diff --git a/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js b/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js index 05befd6ca5..b313cb63ad 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js @@ -7,7 +7,7 @@ const RecurlyClient = require('./RecurlyClient') const PlansLocator = require('./PlansLocator') const SubscriptionHandler = require('./SubscriptionHandler') const GroupPlansData = require('./GroupPlansData') -const { MEMBERS_LIMIT_ADD_ON_CODE } = require('./RecurlyEntities') +const { MEMBERS_LIMIT_ADD_ON_CODE } = require('./PaymentProviderEntities') const { ManuallyCollectedError, PendingChangeError, diff --git a/services/web/app/src/Features/Subscription/SubscriptionHandler.js b/services/web/app/src/Features/Subscription/SubscriptionHandler.js index 4988bb5983..f0fdb22aa2 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionHandler.js +++ b/services/web/app/src/Features/Subscription/SubscriptionHandler.js @@ -14,7 +14,7 @@ const UserUpdater = require('../User/UserUpdater') const { NotFoundError } = require('../Errors/Errors') /** - * @import { RecurlySubscription, RecurlySubscriptionChange } from './RecurlyEntities' + * @import { PaymentProviderSubscription, PaymentProviderSubscriptionChange } from './PaymentProviderEntities' */ async function validateNoSubscriptionInRecurly(userId) { @@ -69,7 +69,7 @@ async function createSubscription(user, subscriptionDetails, recurlyTokenIds) { * * @param {string} userId * @param {string} planCode - * @return {Promise} + * @return {Promise} */ async function previewSubscriptionChange(userId, planCode) { const subscription = await getSubscriptionForUser(userId) @@ -283,7 +283,7 @@ async function _updateSubscriptionFromRecurly(subscription) { * * @param {string} userId * @param {string} addOnCode - * @return {Promise} + * @return {Promise} */ async function previewAddonPurchase(userId, addOnCode) { const subscription = await getSubscriptionForUser(userId) @@ -353,7 +353,7 @@ async function removeAddon(userId, addOnCode) { * Throws a NotFoundError if the subscription can't be found * * @param {string} userId - * @return {Promise} + * @return {Promise} */ async function getSubscriptionForUser(userId) { const subscription = diff --git a/services/web/app/src/Features/Subscription/SubscriptionLocator.js b/services/web/app/src/Features/Subscription/SubscriptionLocator.js index a980399c29..ac0fa5918a 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionLocator.js +++ b/services/web/app/src/Features/Subscription/SubscriptionLocator.js @@ -5,7 +5,7 @@ const logger = require('@overleaf/logger') const { AI_ADD_ON_CODE, isStandaloneAiAddOnPlanCode, -} = require('./RecurlyEntities') +} = require('./PaymentProviderEntities') require('./GroupPlansData') // make sure dynamic group plans are loaded const SubscriptionLocator = { diff --git a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js index 752eb5df98..92d226bb25 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js +++ b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js @@ -5,7 +5,7 @@ const PlansLocator = require('./PlansLocator') const { isStandaloneAiAddOnPlanCode, MEMBERS_LIMIT_ADD_ON_CODE, -} = require('./RecurlyEntities') +} = require('./PaymentProviderEntities') const SubscriptionFormatters = require('./SubscriptionFormatters') const SubscriptionLocator = require('./SubscriptionLocator') const SubscriptionUpdater = require('./SubscriptionUpdater') @@ -138,8 +138,6 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { }, }) - const recurlySubscription = paymentRecord && paymentRecord.subscription - if (memberGroupSubscriptions == null) { memberGroupSubscriptions = [] } else { @@ -214,14 +212,6 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { personalSubscription.plan = plan } - // Subscription DB object contains a recurly property, used to cache trial info - // on the project-list. However, this can cause the wrong template to render, - // if we do not have any subscription data from Recurly (recurlySubscription) - // TODO: Delete this workaround once recurly cache property name migration rolled out. - if (personalSubscription) { - delete personalSubscription.recurly - } - function getPlanOnlyDisplayPrice( totalPlanPriceInCents, taxRate, @@ -243,7 +233,7 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { return formatCurrency( totalPlanPriceInCents - allAddOnsTotalPriceInCentsExceptAdditionalLicensePrice, - recurlySubscription.currency, + paymentRecord.subscription.currency, locale ) } @@ -257,7 +247,7 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { if (totalPriceInCents > 0) { prev[curr.code] = formatCurrency( totalPriceInCents, - recurlySubscription.currency, + paymentRecord.subscription.currency, locale ) } @@ -267,15 +257,15 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { }, {}) } - if (personalSubscription && recurlySubscription) { - const tax = recurlySubscription.taxAmount || 0 + if (personalSubscription && paymentRecord && paymentRecord.subscription) { + const tax = paymentRecord.subscription.taxAmount || 0 // Some plans allow adding more seats than the base plan provides. // This is recorded as a subscription add on. // Note: taxAmount already includes the tax for any addon. let addOnPrice = 0 let additionalLicenses = 0 - const addOns = recurlySubscription.addOns || [] - const taxRate = recurlySubscription.taxRate + const addOns = paymentRecord.subscription.addOns || [] + const taxRate = paymentRecord.subscription.taxRate addOns.forEach(addOn => { addOnPrice += addOn.quantity * addOn.unitPrice if (addOn.code === plan.membersLimitAddOn) { @@ -283,7 +273,7 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { } }) const totalLicenses = (plan.membersLimit || 0) + additionalLicenses - personalSubscription.recurly = { + personalSubscription.payment = { taxRate, billingDetailsLink: buildHostedLink('billing-details'), accountManagementLink: buildHostedLink('account-management'), @@ -291,25 +281,26 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { addOns, totalLicenses, nextPaymentDueAt: SubscriptionFormatters.formatDateTime( - recurlySubscription.periodEnd + paymentRecord.subscription.periodEnd ), nextPaymentDueDate: SubscriptionFormatters.formatDate( - recurlySubscription.periodEnd + paymentRecord.subscription.periodEnd ), - currency: recurlySubscription.currency, - state: recurlySubscription.state, + currency: paymentRecord.subscription.currency, + state: paymentRecord.subscription.state, trialEndsAtFormatted: SubscriptionFormatters.formatDateTime( - recurlySubscription.trialPeriodEnd + paymentRecord.subscription.trialPeriodEnd ), - trialEndsAt: recurlySubscription.trialPeriodEnd, + trialEndsAt: paymentRecord.subscription.trialPeriodEnd, activeCoupons: paymentRecord.coupons, accountEmail: paymentRecord.account.email, hasPastDueInvoice: paymentRecord.account.hasPastDueInvoice, - pausedAt: recurlySubscription.pausePeriodStart, - remainingPauseCycles: recurlySubscription.remainingPauseCycles, + pausedAt: paymentRecord.subscription.pausePeriodStart, + remainingPauseCycles: paymentRecord.subscription.remainingPauseCycles, } - if (recurlySubscription.pendingChange) { - const pendingPlanCode = recurlySubscription.pendingChange.nextPlanCode + if (paymentRecord.subscription.pendingChange) { + const pendingPlanCode = + paymentRecord.subscription.pendingChange.nextPlanCode const pendingPlan = PlansLocator.findLocalPlanInSettings(pendingPlanCode) if (pendingPlan == null) { throw new Error(`No plan found for planCode '${pendingPlanCode}'`) @@ -317,10 +308,10 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { let pendingAdditionalLicenses = 0 let pendingAddOnTax = 0 let pendingAddOnPrice = 0 - if (recurlySubscription.pendingChange.nextAddOns) { - const pendingRecurlyAddons = - recurlySubscription.pendingChange.nextAddOns - pendingRecurlyAddons.forEach(addOn => { + if (paymentRecord.subscription.pendingChange.nextAddOns) { + const pendingAddOns = + paymentRecord.subscription.pendingChange.nextAddOns + pendingAddOns.forEach(addOn => { pendingAddOnPrice += addOn.quantity * addOn.unitPrice if (addOn.code === pendingPlan.membersLimitAddOn) { pendingAdditionalLicenses += addOn.quantity @@ -328,50 +319,50 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { }) // Need to calculate tax ourselves as we don't get tax amounts for pending subs pendingAddOnTax = - personalSubscription.recurly.taxRate * pendingAddOnPrice - pendingPlan.addOns = pendingRecurlyAddons + personalSubscription.payment.taxRate * pendingAddOnPrice + pendingPlan.addOns = pendingAddOns } const pendingSubscriptionTax = - personalSubscription.recurly.taxRate * - recurlySubscription.pendingChange.nextPlanPrice + personalSubscription.payment.taxRate * + paymentRecord.subscription.pendingChange.nextPlanPrice const totalPrice = - recurlySubscription.pendingChange.nextPlanPrice + + paymentRecord.subscription.pendingChange.nextPlanPrice + pendingAddOnPrice + pendingAddOnTax + pendingSubscriptionTax - personalSubscription.recurly.displayPrice = formatCurrency( + personalSubscription.payment.displayPrice = formatCurrency( totalPrice, - recurlySubscription.currency, + paymentRecord.subscription.currency, locale ) - personalSubscription.recurly.planOnlyDisplayPrice = + personalSubscription.payment.planOnlyDisplayPrice = getPlanOnlyDisplayPrice( totalPrice, taxRate, - recurlySubscription.pendingChange.nextAddOns + paymentRecord.subscription.pendingChange.nextAddOns ) - personalSubscription.recurly.addOnDisplayPricesWithoutAdditionalLicense = + personalSubscription.payment.addOnDisplayPricesWithoutAdditionalLicense = getAddOnDisplayPricesWithoutAdditionalLicense( taxRate, - recurlySubscription.pendingChange.nextAddOns + paymentRecord.subscription.pendingChange.nextAddOns ) const pendingTotalLicenses = (pendingPlan.membersLimit || 0) + pendingAdditionalLicenses - personalSubscription.recurly.pendingAdditionalLicenses = + personalSubscription.payment.pendingAdditionalLicenses = pendingAdditionalLicenses - personalSubscription.recurly.pendingTotalLicenses = pendingTotalLicenses + personalSubscription.payment.pendingTotalLicenses = pendingTotalLicenses personalSubscription.pendingPlan = pendingPlan } else { - const totalPrice = recurlySubscription.planPrice + addOnPrice + tax - personalSubscription.recurly.displayPrice = formatCurrency( + const totalPrice = paymentRecord.subscription.planPrice + addOnPrice + tax + personalSubscription.payment.displayPrice = formatCurrency( totalPrice, - recurlySubscription.currency, + paymentRecord.subscription.currency, locale ) - personalSubscription.recurly.planOnlyDisplayPrice = + personalSubscription.payment.planOnlyDisplayPrice = getPlanOnlyDisplayPrice(totalPrice, taxRate, addOns) - personalSubscription.recurly.addOnDisplayPricesWithoutAdditionalLicense = + personalSubscription.payment.addOnDisplayPricesWithoutAdditionalLicense = getAddOnDisplayPricesWithoutAdditionalLicense(taxRate, addOns) } } diff --git a/services/web/app/src/Features/Subscription/types.ts b/services/web/app/src/Features/Subscription/types.ts index b4989c49ea..59453ac47c 100644 --- a/services/web/app/src/Features/Subscription/types.ts +++ b/services/web/app/src/Features/Subscription/types.ts @@ -1,3 +1,6 @@ -import { PaypalPaymentMethod, CreditCardPaymentMethod } from './RecurlyEntities' +import { + PaypalPaymentMethod, + CreditCardPaymentMethod, +} from './PaymentProviderEntities' export type PaymentMethod = PaypalPaymentMethod | CreditCardPaymentMethod diff --git a/services/web/app/views/subscriptions/dashboard-react.pug b/services/web/app/views/subscriptions/dashboard-react.pug index aa3e597dc5..6fac08dd53 100644 --- a/services/web/app/views/subscriptions/dashboard-react.pug +++ b/services/web/app/views/subscriptions/dashboard-react.pug @@ -23,9 +23,9 @@ block append meta meta(name="ol-showGroupDiscount", data-type="boolean", content=showGroupDiscount) meta(name="ol-groupSettingsEnabledFor", data-type="json" content=groupSettingsEnabledFor) meta(name="ol-user" data-type="json" content=user) - if (personalSubscription && personalSubscription.recurly) + if (personalSubscription && personalSubscription.payment) meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey) - meta(name="ol-recommendedCurrency" content=personalSubscription.recurly.currency) + meta(name="ol-recommendedCurrency" content=personalSubscription.payment.currency) meta(name="ol-groupPlans" data-type="json" content=groupPlans) block content diff --git a/services/web/frontend/js/features/subscription/components/dashboard/pause-modal.tsx b/services/web/frontend/js/features/subscription/components/dashboard/pause-modal.tsx index cba3ecd9fd..fa47be6a5c 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/pause-modal.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/pause-modal.tsx @@ -15,7 +15,7 @@ import { debugConsole } from '@/utils/debugging' import * as eventTracking from '../../../../infrastructure/event-tracking' import PauseDuck from '../../images/pause-duck.svg' import GenericErrorAlert from './generic-error-alert' -import { RecurlySubscription } from '../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../types/subscription/dashboard/subscription' const pauseMonthDurationOptions = [1, 2, 3] @@ -35,13 +35,13 @@ export default function PauseSubscriptionModal() { const location = useLocation() function handleCancelSubscriptionClick() { - const subscription = personalSubscription as RecurlySubscription + const subscription = personalSubscription as PaidSubscription eventTracking.sendMB('subscription-page-cancel-button-click', { plan_code: subscription?.planCode, is_trial: - subscription?.recurly.trialEndsAtFormatted && - subscription?.recurly.trialEndsAt && - new Date(subscription.recurly.trialEndsAt).getTime() > Date.now(), + subscription?.payment.trialEndsAtFormatted && + subscription?.payment.trialEndsAt && + new Date(subscription.payment.trialEndsAt).getTime() > Date.now(), }) setShowCancellation(true) } diff --git a/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription-recurly-sync-email.tsx b/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription-recurly-sync-email.tsx index c69334cccd..c518b3ca8c 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription-recurly-sync-email.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription-recurly-sync-email.tsx @@ -18,9 +18,9 @@ function PersonalSubscriptionRecurlySyncEmail() { runAsync(postJSON('/user/subscription/account/email')) } - if (!personalSubscription || !('recurly' in personalSubscription)) return null + if (!personalSubscription || !('payment' in personalSubscription)) return null - const recurlyEmail = personalSubscription.recurly.accountEmail + const recurlyEmail = personalSubscription.payment.accountEmail if (!userEmail || recurlyEmail === userEmail) return null diff --git a/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx b/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx index b567a39667..d6a511af75 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/personal-subscription.tsx @@ -1,5 +1,5 @@ import { Trans, useTranslation } from 'react-i18next' -import { RecurlySubscription } from '../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../types/subscription/dashboard/subscription' import { PausedSubscription } from './states/active/paused' import { ActiveSubscriptionNew } from '@/features/subscription/components/dashboard/states/active/active-new' import { CanceledSubscription } from './states/canceled' @@ -11,7 +11,7 @@ import OLNotification from '@/features/ui/components/ol/ol-notification' function PastDueSubscriptionAlert({ subscription, }: { - subscription: RecurlySubscription + subscription: PaidSubscription }) { const { t } = useTranslation() return ( @@ -21,7 +21,7 @@ function PastDueSubscriptionAlert({ <> {t('account_has_past_due_invoice_change_plan_warning')}{' '} @@ -57,10 +57,10 @@ function RedirectAlerts() { function PersonalSubscriptionStates({ subscription, }: { - subscription: RecurlySubscription + subscription: PaidSubscription }) { const { t } = useTranslation() - const state = subscription?.recurly.state + const state = subscription?.payment.state if (state === 'active') { // This version handles subscriptions with and without addons @@ -83,7 +83,7 @@ function PersonalSubscription() { if (!personalSubscription) return null - if (!('recurly' in personalSubscription)) { + if (!('payment' in personalSubscription)) { return (

- {personalSubscription.recurly.hasPastDueInvoice && ( + {personalSubscription.payment.hasPastDueInvoice && ( )} {recurlyLoadError && ( 0 + subscription.payment.state === 'active' && + subscription.payment.remainingPauseCycles && + subscription.payment.remainingPauseCycles > 0 ) const isLegacyPlan = - subscription.recurly.totalLicenses !== - subscription.recurly.additionalLicenses + subscription.payment.totalLicenses !== + subscription.payment.additionalLicenses return ( <> @@ -103,7 +103,7 @@ export function ActiveSubscriptionNew({ {subscription.plan.annual ? ( ]} // eslint-disable-line react/jsx-key @@ -139,7 +139,7 @@ export function ActiveSubscriptionNew({

@@ -171,21 +171,21 @@ export function ActiveSubscriptionNew({ subscription.pendingPlan.name !== subscription.plan.name && (

{t('want_change_to_apply_before_plan_end')}

)} - {isInFreeTrial(subscription.recurly.trialEndsAt) && - subscription.recurly.trialEndsAtFormatted && ( + {isInFreeTrial(subscription.payment.trialEndsAt) && + subscription.payment.trialEndsAtFormatted && ( )} - {subscription.recurly.totalLicenses > 0 && ( + {subscription.payment.totalLicenses > 0 && (

- {isLegacyPlan && subscription.recurly.additionalLicenses > 0 ? ( + {isLegacyPlan && subscription.payment.additionalLicenses > 0 ? ( ]} // eslint-disable-line react/jsx-key @@ -209,7 +209,7 @@ export function ActiveSubscriptionNew({ i18nKey="your_subscription_will_pause_on" values={{ planName: subscription.plan.name, - pauseDate: subscription.recurly.nextPaymentDueAt, + pauseDate: subscription.payment.nextPaymentDueAt, reactivationDate: getFormattedRenewalDate(), }} shouldUnescape @@ -227,10 +227,10 @@ export function ActiveSubscriptionNew({

{subscription.plan.annual ? t('x_price_per_year', { - price: subscription.recurly.planOnlyDisplayPrice, + price: subscription.payment.planOnlyDisplayPrice, }) : t('x_price_per_month', { - price: subscription.recurly.planOnlyDisplayPrice, + price: subscription.payment.planOnlyDisplayPrice, })}

)} @@ -261,7 +261,7 @@ export function ActiveSubscriptionNew({ } type PlanActionsProps = { - subscription: RecurlySubscription + subscription: PaidSubscription onStandalonePlan: boolean handlePlanChange: () => void hasPendingPause: boolean @@ -301,7 +301,7 @@ function PlanActions({ ) : ( <> - {!hasPendingPause && !subscription.recurly.hasPastDueInvoice && ( + {!hasPendingPause && !subscription.payment.hasPastDueInvoice && ( {t('change_plan')} @@ -334,7 +334,7 @@ function PlanActions({ function FlexibleGroupLicensingActions({ subscription, }: { - subscription: RecurlySubscription + subscription: PaidSubscription }) { const { t } = useTranslation() diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/active.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/active.tsx index d7c9f509a5..2fdffac311 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/active.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/active.tsx @@ -1,7 +1,7 @@ import { useTranslation, Trans } from 'react-i18next' import { PriceExceptions } from '../../../shared/price-exceptions' import { useSubscriptionDashboardContext } from '../../../../context/subscription-dashboard-context' -import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription' import { CancelSubscriptionButton } from './cancel-subscription-button' import { CancelSubscription } from './cancel-plan/cancel-subscription' import { PendingPlanChange } from './pending-plan-change' @@ -27,7 +27,7 @@ import LoadingSpinner from '@/shared/components/loading-spinner' export function ActiveSubscription({ subscription, }: { - subscription: RecurlySubscription + subscription: PaidSubscription }) { const { t } = useTranslation() const { @@ -46,9 +46,9 @@ export function ActiveSubscription({ if (showCancellation) return const hasPendingPause = - subscription.recurly.state === 'active' && - subscription.recurly.remainingPauseCycles && - subscription.recurly.remainingPauseCycles > 0 + subscription.payment.state === 'active' && + subscription.payment.remainingPauseCycles && + subscription.payment.remainingPauseCycles > 0 const handleCancelPendingPauseClick = async () => { try { @@ -96,19 +96,19 @@ export function ActiveSubscription({ )} {!subscription.pendingPlan && - subscription.recurly.additionalLicenses > 0 && ( + subscription.payment.additionalLicenses > 0 && ( <> {' '} )} {!recurlyLoadError && !subscription.groupPlan && !hasPendingPause && - !subscription.recurly.hasPastDueInvoice && ( + !subscription.payment.hasPastDueInvoice && ( <> {' '} } - {isInFreeTrial(subscription.recurly.trialEndsAt) && - subscription.recurly.trialEndsAtFormatted && ( + {isInFreeTrial(subscription.payment.trialEndsAt) && + subscription.payment.trialEndsAtFormatted && ( )} @@ -142,7 +142,7 @@ export function ActiveSubscription({ i18nKey="your_subscription_will_pause_on" values={{ planName: subscription.plan.name, - pauseDate: subscription.recurly.nextPaymentDueAt, + pauseDate: subscription.payment.nextPaymentDueAt, reactivationDate: getFormattedRenewalDate(), }} shouldUnescape @@ -174,7 +174,7 @@ export function ActiveSubscription({

{' '} void } @@ -106,9 +106,9 @@ function AddOns({ const { t } = useTranslation() const addOnsDisplayPrices = onStandalonePlan ? { - [AI_STANDALONE_PLAN_CODE]: subscription.recurly.displayPrice, + [AI_STANDALONE_PLAN_CODE]: subscription.payment.displayPrice, } - : subscription.recurly.addOnDisplayPricesWithoutAdditionalLicense + : subscription.payment.addOnDisplayPricesWithoutAdditionalLicense const addOnsToDisplay = onStandalonePlan ? [{ addOnCode: AI_STANDALONE_PLAN_CODE }] : subscription.addOns?.filter(addOn => addOn.addOnCode !== LICENSE_ADD_ON) @@ -130,7 +130,7 @@ function AddOns({ ) } displayPrice={addOnsDisplayPrices[addOn.addOnCode]} - nextBillingDate={subscription.recurly.nextPaymentDueDate} + nextBillingDate={subscription.payment.nextPaymentDueDate} /> )) ) : ( diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx index d5f8b5408e..dc34d38693 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx @@ -155,14 +155,14 @@ export function CancelSubscription() { isSuccessSecondaryAction || isSuccessCancel - if (!personalSubscription || !('recurly' in personalSubscription)) return null + if (!personalSubscription || !('payment' in personalSubscription)) return null const showDowngrade = showDowngradeOption( personalSubscription.plan.planCode, personalSubscription.plan.groupPlan, - personalSubscription.recurly.trialEndsAt, - personalSubscription.recurly.pausedAt, - personalSubscription.recurly.remainingPauseCycles + personalSubscription.payment.trialEndsAt, + personalSubscription.payment.pausedAt, + personalSubscription.payment.remainingPauseCycles ) const planToDowngradeTo = plans.find( plan => plan.planCode === planCodeToDowngradeTo diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-subscription-button.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-subscription-button.tsx index cf4fc39690..9ce46288f8 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-subscription-button.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-subscription-button.tsx @@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next' import * as eventTracking from '../../../../../../infrastructure/event-tracking' import { useSubscriptionDashboardContext } from '../../../../context/subscription-dashboard-context' import OLButton from '@/features/ui/components/ol/ol-button' -import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription' import { useFeatureFlag } from '@/shared/context/split-test-context' export function CancelSubscriptionButton() { @@ -14,16 +14,16 @@ export function CancelSubscriptionButton() { setShowCancellation, } = useSubscriptionDashboardContext() - const subscription = personalSubscription as RecurlySubscription + const subscription = personalSubscription as PaidSubscription const isInTrial = - subscription?.recurly.trialEndsAtFormatted && - subscription?.recurly.trialEndsAt && - new Date(subscription.recurly.trialEndsAt).getTime() > Date.now() + subscription?.payment.trialEndsAtFormatted && + subscription?.payment.trialEndsAt && + new Date(subscription.payment.trialEndsAt).getTime() > Date.now() const hasPendingOrActivePause = - subscription.recurly.state === 'paused' || - (subscription.recurly.state === 'active' && - subscription.recurly.remainingPauseCycles && - subscription.recurly.remainingPauseCycles > 0) + subscription.payment.state === 'paused' || + (subscription.payment.state === 'active' && + subscription.payment.remainingPauseCycles && + subscription.payment.remainingPauseCycles > 0) const planIsEligibleForPause = !subscription.pendingPlan && !subscription.groupPlan && diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-to-group-plan.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-to-group-plan.tsx index 08034b103b..12303af6b0 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-to-group-plan.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/change-plan/change-to-group-plan.tsx @@ -3,17 +3,17 @@ import { useSubscriptionDashboardContext } from '../../../../../context/subscrip import OLButton from '@/features/ui/components/ol/ol-button' import OLTooltip from '@/features/ui/components/ol/ol-tooltip' import isInFreeTrial from '../../../../../util/is-in-free-trial' -import { RecurlySubscription } from '../../../../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../../../../types/subscription/dashboard/subscription' export function ChangeToGroupPlan() { const { t } = useTranslation() const { handleOpenModal, personalSubscription } = useSubscriptionDashboardContext() - // TODO: Better way to get RecurlySubscription/trialEndsAt + // TODO: Better way to get PaidSubscription/trialEndsAt const subscription = - personalSubscription && 'recurly' in personalSubscription - ? (personalSubscription as RecurlySubscription) + personalSubscription && 'payment' in personalSubscription + ? (personalSubscription as PaidSubscription) : null const handleClick = () => { @@ -25,7 +25,7 @@ export function ChangeToGroupPlan() {

{t('looking_multiple_licenses')}

{t('reduce_costs_group_licenses')}


- {isInFreeTrial(subscription?.recurly?.trialEndsAt) ? ( + {isInFreeTrial(subscription?.payment?.trialEndsAt) ? ( <> { // clear any flash message IDs so they only show once if (location.toString()) { @@ -36,7 +36,7 @@ export function FlashMessage() {
{' '} addOn.addOnCode === AI_ADD_ON_CODE @@ -21,9 +21,9 @@ export function PendingPlanChange({ !pendingPlan.addOns?.some(addOn => addOn.code === AI_ADD_ON_CODE) const pendingAdditionalLicenses = - (subscription.recurly.pendingAdditionalLicenses && - subscription.recurly.pendingAdditionalLicenses > 0) || - subscription.recurly.additionalLicenses > 0 + (subscription.payment.pendingAdditionalLicenses && + subscription.payment.pendingAdditionalLicenses > 0) || + subscription.payment.additionalLicenses > 0 return ( <> @@ -49,8 +49,8 @@ export function PendingPlanChange({ i18nKey="pending_additional_licenses" values={{ pendingAdditionalLicenses: - subscription.recurly.pendingAdditionalLicenses, - pendingTotalLicenses: subscription.recurly.pendingTotalLicenses, + subscription.payment.pendingAdditionalLicenses, + pendingTotalLicenses: subscription.payment.pendingTotalLicenses, }} shouldUnescape tOptions={{ interpolation: { escapeValue: true } }} diff --git a/services/web/frontend/js/features/subscription/components/dashboard/states/active/subscription-remainder.tsx b/services/web/frontend/js/features/subscription/components/dashboard/states/active/subscription-remainder.tsx index bba4cb5c44..3a7b3aa2e3 100644 --- a/services/web/frontend/js/features/subscription/components/dashboard/states/active/subscription-remainder.tsx +++ b/services/web/frontend/js/features/subscription/components/dashboard/states/active/subscription-remainder.tsx @@ -1,8 +1,8 @@ import { Trans } from 'react-i18next' -import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription' type SubscriptionRemainderProps = { - subscription: RecurlySubscription + subscription: PaidSubscription hideTime?: boolean } @@ -11,13 +11,13 @@ function SubscriptionRemainder({ hideTime, }: SubscriptionRemainderProps) { const stillInATrial = - subscription.recurly.trialEndsAtFormatted && - subscription.recurly.trialEndsAt && - new Date(subscription.recurly.trialEndsAt).getTime() > Date.now() + subscription.payment.trialEndsAtFormatted && + subscription.payment.trialEndsAt && + new Date(subscription.payment.trialEndsAt).getTime() > Date.now() const terminationDate = hideTime - ? subscription.recurly.nextPaymentDueDate - : subscription.recurly.nextPaymentDueAt + ? subscription.payment.nextPaymentDueDate + : subscription.payment.nextPaymentDueAt return stillInATrial ? (

{t('your_subscription_has_expired')}

diff --git a/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx b/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx index 3a116f9324..f211c85f10 100644 --- a/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx +++ b/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx @@ -12,7 +12,7 @@ import { ADD_ON_NAME, isStandaloneAiPlanCode, } from '../../data/add-on-codes' -import { RecurlySubscription } from '../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../types/subscription/dashboard/subscription' function SuccessfulSubscription() { const { t } = useTranslation() @@ -21,7 +21,7 @@ function SuccessfulSubscription() { const postCheckoutRedirect = getMeta('ol-postCheckoutRedirect') const { appName, adminEmail } = getMeta('ol-ExposedSettings') - if (!subscription || !('recurly' in subscription)) return null + if (!subscription || !('payment' in subscription)) return null const onAiStandalonePlan = isStandaloneAiPlanCode(subscription.planCode) @@ -37,15 +37,15 @@ function SuccessfulSubscription() { type="success" content={ <> - {subscription.recurly.trialEndsAt && ( + {subscription.payment.trialEndsAt && ( <>

void modalIdShown?: SubscriptionDashModalIds - personalSubscription?: RecurlySubscription | CustomSubscription + personalSubscription?: PaidSubscription | CustomSubscription hasSubscription: boolean plans: Plan[] planCodeToChangeTo?: string @@ -136,21 +136,21 @@ export function SubscriptionDashboardProvider({ ) const hasValidActiveSubscription = Boolean( - ['active', 'canceled'].includes(personalSubscription?.recurly?.state) || + ['active', 'canceled'].includes(personalSubscription?.payment?.state) || institutionMemberships?.length > 0 || memberGroupSubscriptions?.length > 0 ) const getFormattedRenewalDate = useCallback(() => { if ( - !personalSubscription.recurly.pausedAt || - !personalSubscription.recurly.remainingPauseCycles + !personalSubscription.payment.pausedAt || + !personalSubscription.payment.remainingPauseCycles ) { - return personalSubscription.recurly.nextPaymentDueAt + return personalSubscription.payment.nextPaymentDueAt } - const pausedDate = new Date(personalSubscription.recurly.pausedAt) + const pausedDate = new Date(personalSubscription.payment.pausedAt) pausedDate.setMonth( - pausedDate.getMonth() + personalSubscription.recurly.remainingPauseCycles + pausedDate.getMonth() + personalSubscription.payment.remainingPauseCycles ) return formatTime(pausedDate, 'MMMM Do, YYYY') }, [personalSubscription]) @@ -167,9 +167,9 @@ export function SubscriptionDashboardProvider({ if ( isRecurlyLoaded() && plansWithoutDisplayPrice && - personalSubscription?.recurly + personalSubscription?.payment ) { - const { currency, taxRate } = personalSubscription.recurly + const { currency, taxRate } = personalSubscription.payment const fetchPlansDisplayPrices = async () => { for (const plan of plansWithoutDisplayPrice) { try { @@ -203,11 +203,11 @@ export function SubscriptionDashboardProvider({ groupPlanToChangeToCode && groupPlanToChangeToSize && groupPlanToChangeToUsage && - personalSubscription?.recurly + personalSubscription?.payment ) { setQueryingGroupPlanToChangeToPrice(true) - const { currency, taxRate } = personalSubscription.recurly + const { currency, taxRate } = personalSubscription.payment const fetchGroupDisplayPrice = async () => { setGroupPlanToChangeToPriceError(false) let priceData diff --git a/services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx b/services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx index bf30f519ec..29dcd83b11 100644 --- a/services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx +++ b/services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx @@ -83,7 +83,7 @@ describe('', function () { 'Your subscription has been canceled and will terminate on', { exact: false } ) - screen.getByText(canceledSubscription.recurly!.nextPaymentDueAt, { + screen.getByText(canceledSubscription.payment!.nextPaymentDueAt, { exact: false, }) @@ -134,7 +134,7 @@ describe('', function () { {}, JSON.parse(JSON.stringify(annualActiveSubscription)) ) - withStateDeleted.recurly.state = undefined + withStateDeleted.payment.state = undefined renderWithSubscriptionDashContext(, { metaTags: [{ name: 'ol-subscription', value: withStateDeleted }], }) @@ -194,7 +194,7 @@ describe('', function () { }) }) - it('shows different recurly email address section', async function () { + it('shows different payment email address section', async function () { fetchMock.post('/user/subscription/account/email', 200) const usersEmail = 'foo@example.com' renderWithSubscriptionDashContext(, { @@ -208,7 +208,7 @@ describe('', function () { /your billing email address is currently/i ).textContent expect(billingText).to.contain( - `Your billing email address is currently ${annualActiveSubscription.recurly.accountEmail}.` + + `Your billing email address is currently ${annualActiveSubscription.payment.accountEmail}.` + ` If needed you can update your billing address to ${usersEmail}` ) diff --git a/services/web/test/frontend/features/subscription/components/dashboard/states/active/active.test.tsx b/services/web/test/frontend/features/subscription/components/dashboard/states/active/active.test.tsx index 34e6d28c3a..0c54486228 100644 --- a/services/web/test/frontend/features/subscription/components/dashboard/states/active/active.test.tsx +++ b/services/web/test/frontend/features/subscription/components/dashboard/states/active/active.test.tsx @@ -1,7 +1,7 @@ import { expect } from 'chai' import { fireEvent, screen, waitFor } from '@testing-library/react' import * as eventTracking from '@/infrastructure/event-tracking' -import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../../../../types/subscription/dashboard/subscription' import { annualActiveSubscription, groupActiveSubscription, @@ -36,7 +36,7 @@ describe('', function () { sendMBSpy.restore() }) - function expectedInActiveSubscription(subscription: RecurlySubscription) { + function expectedInActiveSubscription(subscription: PaidSubscription) { // sentence broken up by bolding screen.getByText('You are currently subscribed to the', { exact: false }) screen.getByText(subscription.plan.name, { exact: false }) @@ -45,11 +45,11 @@ describe('', function () { // sentence broken up by bolding screen.getByText('The next payment of', { exact: false }) - screen.getByText(subscription.recurly.displayPrice, { + screen.getByText(subscription.payment.displayPrice, { exact: false, }) screen.getByText('will be collected on', { exact: false }) - const dates = screen.getAllByText(subscription.recurly.nextPaymentDueAt, { + const dates = screen.getAllByText(subscription.payment.nextPaymentDueAt, { exact: false, }) expect(dates.length).to.equal(2) @@ -110,7 +110,7 @@ describe('', function () { JSON.parse(JSON.stringify(annualActiveSubscription)) ) - activePastDueSubscription.recurly.hasPastDueInvoice = true + activePastDueSubscription.payment.hasPastDueInvoice = true renderActiveSubscription(activePastDueSubscription) @@ -126,14 +126,14 @@ describe('', function () { }) screen.getByText( - groupActiveSubscriptionWithPendingLicenseChange.recurly + groupActiveSubscriptionWithPendingLicenseChange.payment .pendingAdditionalLicenses! ) screen.getByText('additional license(s) for a total of', { exact: false }) screen.getByText( - groupActiveSubscriptionWithPendingLicenseChange.recurly + groupActiveSubscriptionWithPendingLicenseChange.payment .pendingTotalLicenses! ) @@ -146,10 +146,10 @@ describe('', function () { it('shows the pending license change message when plan change is not pending', function () { const subscription = Object.assign({}, groupActiveSubscription) - subscription.recurly.additionalLicenses = 4 - subscription.recurly.totalLicenses = - subscription.recurly.totalLicenses + - subscription.recurly.additionalLicenses + subscription.payment.additionalLicenses = 4 + subscription.payment.totalLicenses = + subscription.payment.totalLicenses + + subscription.payment.additionalLicenses renderActiveSubscription(subscription) @@ -157,11 +157,11 @@ describe('', function () { exact: false, }) - screen.getByText(subscription.recurly.additionalLicenses) + screen.getByText(subscription.payment.additionalLicenses) screen.getByText('additional license(s) for a total of', { exact: false }) - screen.getByText(subscription.recurly.totalLicenses) + screen.getByText(subscription.payment.totalLicenses) }) it('shows when trial ends and first payment collected and when subscription would become inactive if cancelled', function () { @@ -169,14 +169,14 @@ describe('', function () { screen.getByText('You’re on a free trial which ends on', { exact: false }) const endDate = screen.getAllByText( - trialSubscription.recurly.trialEndsAtFormatted! + trialSubscription.payment.trialEndsAtFormatted! ) expect(endDate.length).to.equal(3) }) it('shows current discounts', function () { const subscriptionWithActiveCoupons = cloneDeep(annualActiveSubscription) - subscriptionWithActiveCoupons.recurly.activeCoupons = [ + subscriptionWithActiveCoupons.payment.activeCoupons = [ { name: 'fake coupon name', code: 'fake-coupon', @@ -188,7 +188,7 @@ describe('', function () { /this does not include your current discounts, which will be applied automatically before your next payment/i ) screen.getByText( - subscriptionWithActiveCoupons.recurly.activeCoupons[0].name + subscriptionWithActiveCoupons.payment.activeCoupons[0].name ) }) @@ -225,7 +225,7 @@ describe('', function () { { exact: false } ) const dates = screen.getAllByText( - annualActiveSubscription.recurly.nextPaymentDueAt, + annualActiveSubscription.payment.nextPaymentDueAt, { exact: false, } @@ -244,7 +244,7 @@ describe('', function () { { exact: false } ) const dates = screen.getAllByText( - trialSubscription.recurly.trialEndsAtFormatted! + trialSubscription.payment.trialEndsAtFormatted! ) expect(dates.length).to.equal(3) const button = screen.getByRole('button', { diff --git a/services/web/test/frontend/features/subscription/fixtures/subscriptions.ts b/services/web/test/frontend/features/subscription/fixtures/subscriptions.ts index 62597e395e..3a05c87761 100644 --- a/services/web/test/frontend/features/subscription/fixtures/subscriptions.ts +++ b/services/web/test/frontend/features/subscription/fixtures/subscriptions.ts @@ -1,7 +1,7 @@ import { CustomSubscription, GroupSubscription, - RecurlySubscription, + PaidSubscription, } from '../../../../../types/subscription/dashboard/subscription' import dateformat from 'dateformat' @@ -15,7 +15,7 @@ const sevenDaysFromTodayFormatted = dateformat( 'dS mmmm yyyy' ) -export const annualActiveSubscription: RecurlySubscription = { +export const annualActiveSubscription: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -33,7 +33,7 @@ export const annualActiveSubscription: RecurlySubscription = { annual: true, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -55,7 +55,7 @@ export const annualActiveSubscription: RecurlySubscription = { }, } -export const annualActiveSubscriptionEuro: RecurlySubscription = { +export const annualActiveSubscriptionEuro: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -73,7 +73,7 @@ export const annualActiveSubscriptionEuro: RecurlySubscription = { annual: true, featureDescription: [], }, - recurly: { + payment: { taxRate: 0.24, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -95,7 +95,7 @@ export const annualActiveSubscriptionEuro: RecurlySubscription = { }, } -export const annualActiveSubscriptionPro: RecurlySubscription = { +export const annualActiveSubscriptionPro: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -112,7 +112,7 @@ export const annualActiveSubscriptionPro: RecurlySubscription = { price_in_cents: 4500, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -134,7 +134,7 @@ export const annualActiveSubscriptionPro: RecurlySubscription = { }, } -export const pastDueExpiredSubscription: RecurlySubscription = { +export const pastDueExpiredSubscription: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -152,7 +152,7 @@ export const pastDueExpiredSubscription: RecurlySubscription = { annual: true, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -174,7 +174,7 @@ export const pastDueExpiredSubscription: RecurlySubscription = { }, } -export const canceledSubscription: RecurlySubscription = { +export const canceledSubscription: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -192,7 +192,7 @@ export const canceledSubscription: RecurlySubscription = { annual: true, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -214,7 +214,7 @@ export const canceledSubscription: RecurlySubscription = { }, } -export const pendingSubscriptionChange: RecurlySubscription = { +export const pendingSubscriptionChange: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -232,7 +232,7 @@ export const pendingSubscriptionChange: RecurlySubscription = { annual: true, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -283,7 +283,7 @@ export const groupActiveSubscription: GroupSubscription = { membersLimit: 10, membersLimitAddOn: 'additional-license', }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -328,7 +328,7 @@ export const groupActiveSubscriptionWithPendingLicenseChange: GroupSubscription membersLimit: 10, membersLimitAddOn: 'additional-license', }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -362,7 +362,7 @@ export const groupActiveSubscriptionWithPendingLicenseChange: GroupSubscription }, } -export const trialSubscription: RecurlySubscription = { +export const trialSubscription: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -380,7 +380,7 @@ export const trialSubscription: RecurlySubscription = { featureDescription: [], hideFromUsers: true, }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -423,7 +423,7 @@ export const customSubscription: CustomSubscription = { customAccount: true, } -export const trialCollaboratorSubscription: RecurlySubscription = { +export const trialCollaboratorSubscription: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -441,7 +441,7 @@ export const trialCollaboratorSubscription: RecurlySubscription = { featureDescription: [], hideFromUsers: true, }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', @@ -463,7 +463,7 @@ export const trialCollaboratorSubscription: RecurlySubscription = { }, } -export const monthlyActiveCollaborator: RecurlySubscription = { +export const monthlyActiveCollaborator: PaidSubscription = { manager_ids: ['abc123'], member_ids: [], invited_emails: [], @@ -480,7 +480,7 @@ export const monthlyActiveCollaborator: RecurlySubscription = { price_in_cents: 212300900, featureDescription: [], }, - recurly: { + payment: { taxRate: 0, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: '/user/subscription/recurly/account-management', diff --git a/services/web/test/frontend/features/subscription/helpers/render-active-subscription.tsx b/services/web/test/frontend/features/subscription/helpers/render-active-subscription.tsx index 19566a8945..110c6c4f74 100644 --- a/services/web/test/frontend/features/subscription/helpers/render-active-subscription.tsx +++ b/services/web/test/frontend/features/subscription/helpers/render-active-subscription.tsx @@ -1,12 +1,12 @@ import { ActiveSubscription } from '../../../../../frontend/js/features/subscription/components/dashboard/states/active/active' -import { RecurlySubscription } from '../../../../../types/subscription/dashboard/subscription' +import { PaidSubscription } from '../../../../../types/subscription/dashboard/subscription' import { groupPlans, plans } from '../fixtures/plans' import { renderWithSubscriptionDashContext } from './render-with-subscription-dash-context' import { MetaTag } from '@/utils/meta' import { CurrencyCode } from '../../../../../types/subscription/currency' export function renderActiveSubscription( - subscription: RecurlySubscription, + subscription: PaidSubscription, tags: MetaTag[] = [], currencyCode?: CurrencyCode ) { diff --git a/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js b/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js index cf7d8d8683..8cdf313395 100644 --- a/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js +++ b/services/web/test/unit/src/Subscription/FeaturesUpdaterTests.js @@ -4,7 +4,7 @@ const sinon = require('sinon') const { ObjectId } = require('mongodb-legacy') const { AI_ADD_ON_CODE, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') const MODULE_PATH = '../../../../app/src/Features/Subscription/FeaturesUpdater' diff --git a/services/web/test/unit/src/Subscription/RecurlyEntitiesTest.js b/services/web/test/unit/src/Subscription/PaymentProviderEntitiesTest.js similarity index 75% rename from services/web/test/unit/src/Subscription/RecurlyEntitiesTest.js rename to services/web/test/unit/src/Subscription/PaymentProviderEntitiesTest.js index 22811ac04f..3c18c6b04b 100644 --- a/services/web/test/unit/src/Subscription/RecurlyEntitiesTest.js +++ b/services/web/test/unit/src/Subscription/PaymentProviderEntitiesTest.js @@ -5,16 +5,17 @@ const { expect } = require('chai') const Errors = require('../../../../app/src/Features/Subscription/Errors') const { AI_ADD_ON_CODE, - RecurlySubscriptionChangeRequest, - RecurlySubscriptionChange, - RecurlySubscription, - RecurlySubscriptionAddOnUpdate, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionChange, + PaymentProviderSubscription, + PaymentProviderSubscriptionAddOnUpdate, +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') -const MODULE_PATH = '../../../../app/src/Features/Subscription/RecurlyEntities' +const MODULE_PATH = + '../../../../app/src/Features/Subscription/PaymentProviderEntities' -describe('RecurlyEntities', function () { - describe('RecurlySubscription', function () { +describe('PaymentProviderEntities', function () { + describe('PaymentProviderSubscription', function () { beforeEach(function () { this.Settings = { plans: [ @@ -26,7 +27,7 @@ describe('RecurlyEntities', function () { features: [], } - this.RecurlyEntities = SandboxedModule.require(MODULE_PATH, { + this.PaymentProviderEntities = SandboxedModule.require(MODULE_PATH, { requires: { '@overleaf/settings': this.Settings, './Errors': Errors, @@ -36,15 +37,17 @@ describe('RecurlyEntities', function () { describe('with add-ons', function () { beforeEach(function () { - const { RecurlySubscription, RecurlySubscriptionAddOn } = - this.RecurlyEntities - this.addOn = new RecurlySubscriptionAddOn({ + const { + PaymentProviderSubscription, + PaymentProviderSubscriptionAddOn, + } = this.PaymentProviderEntities + this.addOn = new PaymentProviderSubscriptionAddOn({ code: 'add-on-code', name: 'My Add-On', quantity: 1, unitPrice: 2, }) - this.subscription = new RecurlySubscription({ + this.subscription = new PaymentProviderSubscription({ id: 'subscription-id', userId: 'user-id', planCode: 'regular-plan', @@ -71,11 +74,12 @@ describe('RecurlyEntities', function () { describe('getRequestForPlanChange()', function () { it('returns a change request for upgrades', function () { - const { RecurlySubscriptionChangeRequest } = this.RecurlyEntities + const { PaymentProviderSubscriptionChangeRequest } = + this.PaymentProviderEntities const changeRequest = this.subscription.getRequestForPlanChange('premium-plan') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'premium-plan', @@ -84,11 +88,12 @@ describe('RecurlyEntities', function () { }) it('returns a change request for downgrades', function () { - const { RecurlySubscriptionChangeRequest } = this.RecurlyEntities + const { PaymentProviderSubscriptionChangeRequest } = + this.PaymentProviderEntities const changeRequest = this.subscription.getRequestForPlanChange('cheap-plan') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'term_end', planCode: 'cheap-plan', @@ -97,17 +102,18 @@ describe('RecurlyEntities', function () { }) it('preserves the AI add-on on upgrades', function () { - const { RecurlySubscriptionChangeRequest } = this.RecurlyEntities + const { PaymentProviderSubscriptionChangeRequest } = + this.PaymentProviderEntities this.addOn.code = AI_ADD_ON_CODE const changeRequest = this.subscription.getRequestForPlanChange('premium-plan') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'premium-plan', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: AI_ADD_ON_CODE, quantity: 1, }), @@ -117,17 +123,18 @@ describe('RecurlyEntities', function () { }) it('preserves the AI add-on on downgrades', function () { - const { RecurlySubscriptionChangeRequest } = this.RecurlyEntities + const { PaymentProviderSubscriptionChangeRequest } = + this.PaymentProviderEntities this.addOn.code = AI_ADD_ON_CODE const changeRequest = this.subscription.getRequestForPlanChange('cheap-plan') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'term_end', planCode: 'cheap-plan', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: AI_ADD_ON_CODE, quantity: 1, }), @@ -137,18 +144,19 @@ describe('RecurlyEntities', function () { }) it('preserves the AI add-on on upgrades from the standalone AI plan', function () { - const { RecurlySubscriptionChangeRequest } = this.RecurlyEntities + const { PaymentProviderSubscriptionChangeRequest } = + this.PaymentProviderEntities this.subscription.planCode = 'assistant-annual' this.subscription.addOns = [] const changeRequest = this.subscription.getRequestForPlanChange('cheap-plan') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'term_end', planCode: 'cheap-plan', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: AI_ADD_ON_CODE, quantity: 1, }), @@ -161,22 +169,22 @@ describe('RecurlyEntities', function () { describe('getRequestForAddOnPurchase()', function () { it('returns a change request', function () { const { - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, - } = this.RecurlyEntities + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, + } = this.PaymentProviderEntities const changeRequest = this.subscription.getRequestForAddOnPurchase('another-add-on') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: this.addOn.code, quantity: this.addOn.quantity, unitPrice: this.addOn.unitPrice, }), - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: 'another-add-on', quantity: 1, }), @@ -187,9 +195,9 @@ describe('RecurlyEntities', function () { it('returns a change request with quantity and unit price specified', function () { const { - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, - } = this.RecurlyEntities + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, + } = this.PaymentProviderEntities const quantity = 5 const unitPrice = 10 const changeRequest = this.subscription.getRequestForAddOnPurchase( @@ -198,16 +206,16 @@ describe('RecurlyEntities', function () { unitPrice ) expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: this.addOn.code, quantity: this.addOn.quantity, unitPrice: this.addOn.unitPrice, }), - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: 'another-add-on', quantity, unitPrice, @@ -227,20 +235,20 @@ describe('RecurlyEntities', function () { describe('getRequestForAddOnUpdate()', function () { it('returns a change request', function () { const { - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, - } = this.RecurlyEntities + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, + } = this.PaymentProviderEntities const newQuantity = 2 const changeRequest = this.subscription.getRequestForAddOnUpdate( 'add-on-code', newQuantity ) expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: this.addOn.code, quantity: newQuantity, unitPrice: this.addOn.unitPrice, @@ -263,7 +271,7 @@ describe('RecurlyEntities', function () { this.addOn.code ) expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'term_end', addOnUpdates: [], @@ -283,13 +291,13 @@ describe('RecurlyEntities', function () { const changeRequest = this.subscription.getRequestForGroupPlanUpgrade('test_plan_code') const addOns = [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: 'add-on-code', quantity: 1, }), ] expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: addOns, @@ -301,8 +309,8 @@ describe('RecurlyEntities', function () { describe('without add-ons', function () { beforeEach(function () { - const { RecurlySubscription } = this.RecurlyEntities - this.subscription = new RecurlySubscription({ + const { PaymentProviderSubscription } = this.PaymentProviderEntities + this.subscription = new PaymentProviderSubscription({ id: 'subscription-id', userId: 'user-id', planCode: 'regular-plan', @@ -325,17 +333,17 @@ describe('RecurlyEntities', function () { describe('getRequestForAddOnPurchase()', function () { it('returns a change request', function () { const { - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, - } = this.RecurlyEntities + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, + } = this.PaymentProviderEntities const changeRequest = this.subscription.getRequestForAddOnPurchase('some-add-on') expect(changeRequest).to.deep.equal( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: 'some-add-on', quantity: 1, }), @@ -356,10 +364,10 @@ describe('RecurlyEntities', function () { }) }) - describe('RecurlySubscriptionChange', function () { + describe('PaymentProviderSubscriptionChange', function () { describe('constructor', function () { it('rounds the amounts when calculating the taxes', function () { - const subscription = new RecurlySubscription({ + const subscription = new PaymentProviderSubscription({ id: 'subscription-id', userId: 'user-id', planCode: 'premium-plan', @@ -374,7 +382,7 @@ describe('RecurlyEntities', function () { periodEnd: new Date(), collectionMethod: 'automatic', }) - const change = new RecurlySubscriptionChange({ + const change = new PaymentProviderSubscriptionChange({ subscription, nextPlanCode: 'promotional-plan', nextPlanName: 'Promotial plan', diff --git a/services/web/test/unit/src/Subscription/PaymentServiceTests.js b/services/web/test/unit/src/Subscription/PaymentServiceTests.js index d836d01bef..cc58c071ed 100644 --- a/services/web/test/unit/src/Subscription/PaymentServiceTests.js +++ b/services/web/test/unit/src/Subscription/PaymentServiceTests.js @@ -2,10 +2,10 @@ const sinon = require('sinon') const { expect } = require('chai') const SandboxedModule = require('sandboxed-module') const { - RecurlySubscription, - RecurlyAccount, - RecurlyCoupon, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') + PaymentProviderSubscription, + PaymentProviderAccount, + PaymentProviderCoupon, +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') const MODULE_PATH = '../../../../app/src/Features/Subscription/PaymentService' @@ -14,7 +14,7 @@ describe('PaymentService', function () { this.user = { _id: '123456', } - this.recurlySubscription = new RecurlySubscription({ + this.recurlySubscription = new PaymentProviderSubscription({ id: 'subscription-id', userId: this.user._id, currency: 'EUR', @@ -30,13 +30,13 @@ describe('PaymentService', function () { periodEnd: new Date(), collectionMethod: 'automatic', }) - this.recurlyAccount = new RecurlyAccount({ + this.recurlyAccount = new PaymentProviderAccount({ code: this.user._id, email: 'example@example.com', hasPastDueInvoice: true, }) this.recurlyCoupons = [ - new RecurlyCoupon({ + new PaymentProviderCoupon({ code: 'coupon-code', name: 'coupon name', description: 'coupon description', diff --git a/services/web/test/unit/src/Subscription/RecurlyClientTests.js b/services/web/test/unit/src/Subscription/RecurlyClientTests.js index 36ef8a5b01..dde1c53fd7 100644 --- a/services/web/test/unit/src/Subscription/RecurlyClientTests.js +++ b/services/web/test/unit/src/Subscription/RecurlyClientTests.js @@ -3,12 +3,12 @@ const { expect } = require('chai') const recurly = require('recurly') const SandboxedModule = require('sandboxed-module') const { - RecurlySubscription, - RecurlySubscriptionChangeRequest, - RecurlySubscriptionAddOnUpdate, - RecurlyAccount, - RecurlyCoupon, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') + PaymentProviderSubscription, + PaymentProviderSubscriptionChangeRequest, + PaymentProviderSubscriptionAddOnUpdate, + PaymentProviderAccount, + PaymentProviderCoupon, +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') const MODULE_PATH = '../../../../app/src/Features/Subscription/RecurlyClient' @@ -41,7 +41,7 @@ describe('RecurlyClient', function () { preTaxTotal: 2, } - this.subscription = new RecurlySubscription({ + this.subscription = new PaymentProviderSubscription({ id: 'subscription-id', userId: 'user-id', currency: 'EUR', @@ -156,7 +156,7 @@ describe('RecurlyClient', function () { const account = await this.RecurlyClient.promises.getAccountForUserId( this.user._id ) - const expectedAccount = new RecurlyAccount({ + const expectedAccount = new PaymentProviderAccount({ code: this.user._id, email: this.user.email, hasPastDueInvoice: false, @@ -226,7 +226,7 @@ describe('RecurlyClient', function () { const coupons = await this.RecurlyClient.promises.getActiveCouponsForUserId('some-user') const expectedCoupons = [ - new RecurlyCoupon({ + new PaymentProviderCoupon({ code: 'coupon-code', name: 'Coupon Name', description: 'hosted page description', @@ -329,7 +329,7 @@ describe('RecurlyClient', function () { it('handles plan changes', async function () { await this.RecurlyClient.promises.applySubscriptionChangeRequest( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'new-plan', @@ -343,11 +343,11 @@ describe('RecurlyClient', function () { it('handles add-on changes', async function () { await this.RecurlyClient.promises.applySubscriptionChangeRequest( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', addOnUpdates: [ - new RecurlySubscriptionAddOnUpdate({ + new PaymentProviderSubscriptionAddOnUpdate({ code: 'new-add-on', quantity: 2, unitPrice: 8.99, @@ -514,7 +514,7 @@ describe('RecurlyClient', function () { }) const { immediateCharge } = await this.RecurlyClient.promises.previewSubscriptionChange( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'new-plan', @@ -546,7 +546,7 @@ describe('RecurlyClient', function () { }) const { immediateCharge } = await this.RecurlyClient.promises.previewSubscriptionChange( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'new-plan', @@ -570,7 +570,7 @@ describe('RecurlyClient', function () { .throws(new ValidationError()) await expect( this.RecurlyClient.promises.previewSubscriptionChange( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'new-plan', @@ -583,7 +583,7 @@ describe('RecurlyClient', function () { this.client.previewSubscriptionChange = sinon.stub().throws(new Error()) await expect( this.RecurlyClient.promises.previewSubscriptionChange( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.subscription, timeframe: 'now', planCode: 'new-plan', diff --git a/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js b/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js index cdb3c15089..239971560b 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionGroupHandlerTests.js @@ -15,12 +15,12 @@ describe('SubscriptionGroupHandler', function () { this.subscription_id = '31DSd1123D' this.adding = 1 this.paymentMethod = { cardType: 'Visa', lastFour: '1111' } - this.RecurlyEntities = { + this.PaymentProviderEntities = { MEMBERS_LIMIT_ADD_ON_CODE: 'additional-license', } this.localPlanInSettings = { membersLimit: 5, - membersLimitAddOn: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + membersLimitAddOn: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, } this.subscription = { @@ -40,7 +40,7 @@ describe('SubscriptionGroupHandler', function () { id: 123, addOns: [ { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: 1, }, ], @@ -98,7 +98,7 @@ describe('SubscriptionGroupHandler', function () { this.previewSubscriptionChange = { nextAddOns: [ { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: this.recurlySubscription.addOns[0].quantity + this.adding, }, ], @@ -166,7 +166,7 @@ describe('SubscriptionGroupHandler', function () { }, './RecurlyClient': this.RecurlyClient, './PlansLocator': this.PlansLocator, - './RecurlyEntities': this.RecurlyEntities, + './PaymentProviderEntities': this.PaymentProviderEntities, '../Authentication/SessionManager': this.SessionManager, './GroupPlansData': this.GroupPlansData, }, @@ -325,7 +325,8 @@ describe('SubscriptionGroupHandler', function () { }, plan: { membersLimit: 5, - membersLimitAddOn: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + membersLimitAddOn: + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, canUseFlexibleLicensing: true, }, recurlySubscription: this.recurlySubscription, @@ -347,14 +348,14 @@ describe('SubscriptionGroupHandler', function () { beforeEach(function () { this.recurlySubscription.addOns = [ { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: 6, }, ] this.prevQuantity = this.recurlySubscription.addOns[0].quantity this.previewSubscriptionChange.nextAddOns = [ { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: this.prevQuantity + this.adding, }, ] @@ -367,7 +368,7 @@ describe('SubscriptionGroupHandler', function () { this.recurlySubscription.getRequestForAddOnUpdate .calledWith( - this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, this.recurlySubscription.addOns[0].quantity + this.adding ) .should.equal(true) @@ -391,7 +392,7 @@ describe('SubscriptionGroupHandler', function () { { type: 'add-on-update', addOn: { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: this.previewSubscriptionChange.nextAddOns[0].quantity, prevQuantity: this.prevQuantity, @@ -435,7 +436,7 @@ describe('SubscriptionGroupHandler', function () { this.prevQuantity = this.recurlySubscription.addOns[0]?.quantity ?? 0 this.previewSubscriptionChange.nextAddOns = [ { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: this.prevQuantity + this.adding, }, ] @@ -467,7 +468,7 @@ describe('SubscriptionGroupHandler', function () { { type: 'add-on-update', addOn: { - code: this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + code: this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, quantity: this.previewSubscriptionChange.nextAddOns[0].quantity, prevQuantity: this.prevQuantity, @@ -493,7 +494,7 @@ describe('SubscriptionGroupHandler', function () { ) this.recurlySubscription.getRequestForAddOnPurchase .calledWithExactly( - this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, this.adding, this.GroupPlansData.enterprise.collaborator.USD[5] .additional_license_legacy_price_in_cents / 100 @@ -513,7 +514,7 @@ describe('SubscriptionGroupHandler', function () { ) this.recurlySubscription.getRequestForAddOnPurchase .calledWithExactly( - this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, this.adding, undefined ) @@ -538,7 +539,7 @@ describe('SubscriptionGroupHandler', function () { ) this.recurlySubscription.getRequestForAddOnPurchase .calledWithExactly( - this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, this.adding, this.GroupPlansData.enterprise.collaborator.USD[5] .additional_license_legacy_price_in_cents / 100 @@ -563,7 +564,7 @@ describe('SubscriptionGroupHandler', function () { ) this.recurlySubscription.getRequestForAddOnPurchase .calledWithExactly( - this.RecurlyEntities.MEMBERS_LIMIT_ADD_ON_CODE, + this.PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE, this.adding, undefined ) diff --git a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js index 64e9381b49..0bd0d24558 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionHandlerTests.js @@ -3,9 +3,9 @@ const sinon = require('sinon') const chai = require('chai') const { expect } = chai const { - RecurlySubscription, - RecurlySubscriptionChangeRequest, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') + PaymentProviderSubscription, + PaymentProviderSubscriptionChangeRequest, +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') const MODULE_PATH = '../../../../app/src/Features/Subscription/SubscriptionHandler' @@ -27,7 +27,7 @@ const mockRecurlySubscriptions = { } const mockRecurlyClientSubscriptions = { - 'subscription-123-active': new RecurlySubscription({ + 'subscription-123-active': new PaymentProviderSubscription({ id: 'subscription-123-active', userId: 'user-id', planCode: 'collaborator', @@ -275,7 +275,7 @@ describe('SubscriptionHandler', function () { expect( this.RecurlyClient.promises.applySubscriptionChangeRequest ).to.have.been.calledWith( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.activeRecurlyClientSubscription, timeframe: 'now', planCode: this.plan_code, @@ -388,7 +388,7 @@ describe('SubscriptionHandler', function () { expect( this.RecurlyClient.promises.applySubscriptionChangeRequest ).to.be.calledWith( - new RecurlySubscriptionChangeRequest({ + new PaymentProviderSubscriptionChangeRequest({ subscription: this.activeRecurlyClientSubscription, timeframe: 'now', planCode: this.plan_code, diff --git a/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js b/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js index 1e0c6457df..ff89740a7b 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js @@ -2,11 +2,11 @@ const SandboxedModule = require('sandboxed-module') const sinon = require('sinon') const { assert } = require('chai') const { - RecurlyAccount, - RecurlySubscription, - RecurlySubscriptionAddOn, - RecurlySubscriptionChange, -} = require('../../../../app/src/Features/Subscription/RecurlyEntities') + PaymentProviderAccount, + PaymentProviderSubscription, + PaymentProviderSubscriptionAddOn, + PaymentProviderSubscriptionChange, +} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') const modulePath = '../../../../app/src/Features/Subscription/SubscriptionViewModelBuilder' @@ -33,7 +33,7 @@ describe('SubscriptionViewModelBuilder', function () { state: 'active', }, } - this.recurlySubscription = new RecurlySubscription({ + this.paymentRecord = new PaymentProviderSubscription({ id: this.recurlySubscription_id, userId: this.user._id, currency: 'EUR', @@ -41,7 +41,7 @@ describe('SubscriptionViewModelBuilder', function () { planName: 'plan-name', planPrice: 13, addOns: [ - new RecurlySubscriptionAddOn({ + new PaymentProviderSubscriptionAddOn({ code: 'addon-code', name: 'addon name', quantity: 1, @@ -265,7 +265,7 @@ describe('SubscriptionViewModelBuilder', function () { plan: this.plan, recurlySubscription_id: this.recurlySubscription_id, } - this.recurlySubscription = { + this.paymentRecord = { state: 'active', } this.SubscriptionLocator.promises.getUsersSubscription @@ -279,7 +279,7 @@ describe('SubscriptionViewModelBuilder', function () { .withArgs(this.individualSubscription.recurlySubscription_id, { includeAccount: true, }) - .resolves(this.recurlySubscription) + .resolves(this.paymentRecord) const usersBestSubscription = await this.SubscriptionViewModelBuilder.promises.getBestSubscription( @@ -293,7 +293,7 @@ describe('SubscriptionViewModelBuilder', function () { ) sinon.assert.calledWith( this.SubscriptionUpdater.promises.updateSubscriptionFromRecurly, - this.recurlySubscription, + this.paymentRecord, this.individualSubscriptionWithoutRecurly ) assert.deepEqual(usersBestSubscription, { @@ -507,14 +507,14 @@ describe('SubscriptionViewModelBuilder', function () { describe('buildUsersSubscriptionViewModel', function () { describe('with a recurly subscription', function () { - it('adds recurly data to the personal subscription', async function () { + it('adds payment data to the personal subscription', async function () { this.SubscriptionLocator.getUsersSubscription.yields( null, this.individualSubscription ) this.PaymentService.getPaymentFromRecord.yields(null, { - subscription: this.recurlySubscription, - account: new RecurlyAccount({ + subscription: this.paymentRecord, + account: new PaymentProviderAccount({ email: 'example@example.com', hasPastDueInvoice: false, }), @@ -524,7 +524,7 @@ describe('SubscriptionViewModelBuilder', function () { await this.SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel( this.user ) - assert.deepEqual(result.personalSubscription.recurly, { + assert.deepEqual(result.personalSubscription.payment, { taxRate: 0.1, billingDetailsLink: '/user/subscription/recurly/billing-details', accountManagementLink: @@ -564,28 +564,29 @@ describe('SubscriptionViewModelBuilder', function () { null, this.individualSubscription ) - this.recurlySubscription.pendingChange = new RecurlySubscriptionChange({ - subscription: this.recurlySubscription, - nextPlanCode: this.groupPlanCode, - nextPlanName: 'Group Collaborator (Annual) 4 licenses', - nextPlanPrice: 1400, - nextAddOns: [ - new RecurlySubscriptionAddOn({ - code: 'additional-license', - name: 'additional license', - quantity: 8, - unitPrice: 24.4, - }), - new RecurlySubscriptionAddOn({ - code: 'addon-code', - name: 'addon name', - quantity: 1, - unitPrice: 2, - }), - ], - }) + this.paymentRecord.pendingChange = + new PaymentProviderSubscriptionChange({ + subscription: this.paymentRecord, + nextPlanCode: this.groupPlanCode, + nextPlanName: 'Group Collaborator (Annual) 4 licenses', + nextPlanPrice: 1400, + nextAddOns: [ + new PaymentProviderSubscriptionAddOn({ + code: 'additional-license', + name: 'additional license', + quantity: 8, + unitPrice: 24.4, + }), + new PaymentProviderSubscriptionAddOn({ + code: 'addon-code', + name: 'addon name', + quantity: 1, + unitPrice: 2, + }), + ], + }) this.PaymentService.getPaymentFromRecord.yields(null, { - subscription: this.recurlySubscription, + subscription: this.paymentRecord, account: {}, coupons: [], }) @@ -594,24 +595,24 @@ describe('SubscriptionViewModelBuilder', function () { this.user ) assert.equal( - result.personalSubscription.recurly.displayPrice, + result.personalSubscription.payment.displayPrice, '€1,756.92' ) assert.equal( - result.personalSubscription.recurly.planOnlyDisplayPrice, + result.personalSubscription.payment.planOnlyDisplayPrice, '€1,754.72' ) assert.deepEqual( - result.personalSubscription.recurly + result.personalSubscription.payment .addOnDisplayPricesWithoutAdditionalLicense, { 'addon-code': '€2.20' } ) assert.equal( - result.personalSubscription.recurly.pendingAdditionalLicenses, + result.personalSubscription.payment.pendingAdditionalLicenses, 8 ) assert.equal( - result.personalSubscription.recurly.pendingTotalLicenses, + result.personalSubscription.payment.pendingTotalLicenses, 12 ) }) diff --git a/services/web/types/subscription/dashboard/subscription.ts b/services/web/types/subscription/dashboard/subscription.ts index bf274fe283..b43fc8a81e 100644 --- a/services/web/types/subscription/dashboard/subscription.ts +++ b/services/web/types/subscription/dashboard/subscription.ts @@ -1,6 +1,11 @@ import { CurrencyCode } from '../currency' import { Nullable } from '../../utils' -import { Plan, AddOn, RecurlyAddOn, PendingRecurlyPlan } from '../plan' +import { + Plan, + AddOn, + PaymentProviderAddOn, + PendingPaymentProviderPlan, +} from '../plan' import { User } from '../../user' type SubscriptionState = 'active' | 'canceled' | 'expired' | 'paused' @@ -10,18 +15,18 @@ export type PurchasingAddOnCode = { code: string } -type RecurlyCoupon = { +type PaymentProviderCoupon = { code: string name: string description: string } -type Recurly = { +type PaymentProviderRecord = { taxRate: number billingDetailsLink: string accountManagementLink: string additionalLicenses: number - addOns: RecurlyAddOn[] + addOns: PaymentProviderAddOn[] totalLicenses: number nextPaymentDueAt: string nextPaymentDueDate: string @@ -29,7 +34,7 @@ type Recurly = { state?: SubscriptionState trialEndsAtFormatted: Nullable trialEndsAt: Nullable - activeCoupons: RecurlyCoupon[] + activeCoupons: PaymentProviderCoupon[] accountEmail: string hasPastDueInvoice: boolean displayPrice: string @@ -58,19 +63,19 @@ export type Subscription = { planCode: string recurlySubscription_id: string plan: Plan - pendingPlan?: PendingRecurlyPlan + pendingPlan?: PendingPaymentProviderPlan addOns?: AddOn[] } -export type RecurlySubscription = Subscription & { - recurly: Recurly +export type PaidSubscription = Subscription & { + payment: PaymentProviderRecord } export type CustomSubscription = Subscription & { customAccount: boolean } -export type GroupSubscription = RecurlySubscription & { +export type GroupSubscription = PaidSubscription & { teamName: string teamNotice?: string } diff --git a/services/web/types/subscription/plan.ts b/services/web/types/subscription/plan.ts index 820dc8f341..a1b0f7b5d6 100644 --- a/services/web/types/subscription/plan.ts +++ b/services/web/types/subscription/plan.ts @@ -22,8 +22,8 @@ export type AddOn = { unitAmountInCents: number } -// add-ons directly accessed through recurly -export type RecurlyAddOn = { +// add-ons directly accessed through payment +export type PaymentProviderAddOn = { code: string name: string quantity: number @@ -32,11 +32,11 @@ export type RecurlyAddOn = { displayPrice?: string } -export type PendingRecurlyPlan = { +export type PendingPaymentProviderPlan = { annual?: boolean displayPrice?: string featureDescription?: Record[] - addOns?: RecurlyAddOn[] + addOns?: PaymentProviderAddOn[] features?: Features groupPlan?: boolean hideFromUsers?: boolean