diff --git a/services/web/app/src/Features/Subscription/PaymentService.js b/services/web/app/src/Features/Subscription/PaymentService.js deleted file mode 100644 index ad2e75e03e..0000000000 --- a/services/web/app/src/Features/Subscription/PaymentService.js +++ /dev/null @@ -1,81 +0,0 @@ -// @ts-check - -const RecurlyClient = require('./RecurlyClient.js') -const logger = require('@overleaf/logger') -const { callbackify } = require('util') - -/** - * @import { PaymentProviderSubscription, PaymentProviderAccount, PaymentProviderCoupon } from "./PaymentProviderEntities.js" - * @import { ObjectId } from 'mongodb' - */ - -/** - * this represents a subset of the Mongo Subscription record - * - * @typedef {object} MongoSubscription - * @property {ObjectId} admin_id - * @property {string} [recurlySubscription_id] - */ - -/** - * @typedef {object} PaymentRecord - * @property {PaymentProviderSubscription} subscription - * @property {PaymentProviderAccount | null} account - * @property {PaymentProviderCoupon[]} coupons - */ - -/** - * Get payment information from our Mongo record - * - * @param {MongoSubscription} subscription - * @return {Promise} - */ -async function getPaymentFromRecord(subscription) { - if (subscription == null) { - logger.debug('no subscription provided') - return null - } - const userId = (subscription.admin_id._id || subscription.admin_id).toString() - const recurlySubscriptionId = subscription.recurlySubscription_id - - // TODO: handle non-recurly payment records - if (recurlySubscriptionId == null || recurlySubscriptionId === '') { - logger.debug( - { userId }, - "no recurly subscription id found for user's subscription" - ) - return null - } - - const subscriptionResponse = await RecurlyClient.promises.getSubscription( - recurlySubscriptionId - ) - if (!subscriptionResponse) { - logger.debug( - { recurlySubscriptionId }, - 'no recurly subscription found for subscription id' - ) - return null - } - - const accountResponse = - await RecurlyClient.promises.getAccountForUserId(userId) - const accountCoupons = - await RecurlyClient.promises.getActiveCouponsForUserId(userId) - - // TODO: include account and coupons in subscription class instead of separately here - // if Recurly is removed (Recurly needs 2 extra requests to get account & coupon data) - return { - subscription: subscriptionResponse, - account: accountResponse, - coupons: accountCoupons, - } -} - -module.exports = { - getPaymentFromRecord: callbackify(getPaymentFromRecord), - - promises: { - getPaymentFromRecord, - }, -} diff --git a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js index 92d226bb25..4761222a2e 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js +++ b/services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js @@ -23,8 +23,8 @@ const { V1ConnectionError, } = require('../Errors/Errors') const FeaturesHelper = require('./FeaturesHelper') -const PaymentService = require('./PaymentService') const { formatCurrency } = require('../../util/currency') +const Modules = require('../../infrastructure/Modules') /** * @import { Subscription } from "../../../../types/project/dashboard/subscription" @@ -82,16 +82,16 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { currentInstitutionsWithLicence, managedInstitutions, managedPublishers, - paymentRecord, + fetchedPaymentRecord, plan, } = await async.auto({ personalSubscription(cb) { SubscriptionLocator.getUsersSubscription(user, cb) }, - paymentRecord: [ + fetchedPaymentRecord: [ 'personalSubscription', ({ personalSubscription }, cb) => { - PaymentService.getPaymentFromRecord(personalSubscription, cb) + Modules.hooks.fire('getPaymentFromRecord', personalSubscription, cb) }, ], plan: [ @@ -138,6 +138,8 @@ async function buildUsersSubscriptionViewModel(user, locale = 'en') { }, }) + const paymentRecord = fetchedPaymentRecord && fetchedPaymentRecord[0] + if (memberGroupSubscriptions == null) { memberGroupSubscriptions = [] } else { diff --git a/services/web/test/unit/src/Subscription/PaymentServiceTests.js b/services/web/test/unit/src/Subscription/PaymentServiceTests.js deleted file mode 100644 index cc58c071ed..0000000000 --- a/services/web/test/unit/src/Subscription/PaymentServiceTests.js +++ /dev/null @@ -1,117 +0,0 @@ -const sinon = require('sinon') -const { expect } = require('chai') -const SandboxedModule = require('sandboxed-module') -const { - PaymentProviderSubscription, - PaymentProviderAccount, - PaymentProviderCoupon, -} = require('../../../../app/src/Features/Subscription/PaymentProviderEntities') - -const MODULE_PATH = '../../../../app/src/Features/Subscription/PaymentService' - -describe('PaymentService', function () { - beforeEach(function () { - this.user = { - _id: '123456', - } - this.recurlySubscription = new PaymentProviderSubscription({ - id: 'subscription-id', - userId: this.user._id, - currency: 'EUR', - planCode: 'plan-code', - planName: 'plan-name', - planPrice: 13, - addOns: [], - subtotal: 15, - taxRate: 0.1, - taxAmount: 1.5, - total: 14.5, - periodStart: new Date(), - periodEnd: new Date(), - collectionMethod: 'automatic', - }) - this.recurlyAccount = new PaymentProviderAccount({ - code: this.user._id, - email: 'example@example.com', - hasPastDueInvoice: true, - }) - this.recurlyCoupons = [ - new PaymentProviderCoupon({ - code: 'coupon-code', - name: 'coupon name', - description: 'coupon description', - }), - ] - this.mongoSubscription = { - admin_id: this.user, - recurlySubscription_id: this.recurlySubscription.id, - } - this.RecurlyClient = { - promises: { - getSubscription: sinon.stub(), - getAccountForUserId: sinon.stub(), - getActiveCouponsForUserId: sinon.stub(), - }, - } - return (this.PaymentService = SandboxedModule.require(MODULE_PATH, { - requires: { - './RecurlyClient': this.RecurlyClient, - }, - })) - }) - - describe('getPaymentFromRecord', function () { - it('should return null for a missing subscription', async function () { - const response = - await this.PaymentService.promises.getPaymentFromRecord(null) - expect(response).to.equal(null) - }) - - it('should return null if payment service subscription id is missing', async function () { - this.mongoSubscription.recurlySubscription_id = null - const response = await this.PaymentService.promises.getPaymentFromRecord( - this.mongoSubscription - ) - expect(response).to.equal(null) - }) - - it('should return the subscription', async function () { - this.RecurlyClient.promises.getSubscription.returns( - this.recurlySubscription - ) - const response = await this.PaymentService.promises.getPaymentFromRecord( - this.mongoSubscription - ) - expect(response.subscription).to.deep.equal(this.recurlySubscription) - }) - - it('should return account information if found', async function () { - this.RecurlyClient.promises.getSubscription.returns( - this.recurlySubscription - ) - this.RecurlyClient.promises.getAccountForUserId.returns( - this.recurlyAccount - ) - const response = await this.PaymentService.promises.getPaymentFromRecord( - this.mongoSubscription - ) - expect(response.account.email).to.equal(this.recurlyAccount.email) - expect(response.account.hasPastDueInvoice).to.equal( - this.recurlyAccount.hasPastDueInvoice - ) - }) - - it('should include coupons if found', async function () { - this.RecurlyClient.promises.getSubscription.returns( - this.recurlySubscription - ) - this.RecurlyClient.promises.getActiveCouponsForUserId.returns( - this.recurlyCoupons - ) - const response = await this.PaymentService.promises.getPaymentFromRecord( - this.mongoSubscription - ) - expect(response.coupons).to.deep.equal(this.recurlyCoupons) - }) - }) -}) diff --git a/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js b/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js index ff89740a7b..dbfa775876 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionViewModelBuilderTests.js @@ -143,9 +143,6 @@ describe('SubscriptionViewModelBuilder', function () { this.PlansLocator = { findLocalPlanInSettings: sinon.stub(), } - this.PaymentService = { - getPaymentFromRecord: sinon.stub().yields(), - } this.SubscriptionViewModelBuilder = SandboxedModule.require(modulePath, { requires: { '@overleaf/settings': this.Settings, @@ -155,7 +152,11 @@ describe('SubscriptionViewModelBuilder', function () { './RecurlyWrapper': this.RecurlyWrapper, './SubscriptionUpdater': this.SubscriptionUpdater, './PlansLocator': this.PlansLocator, - './PaymentService': this.PaymentService, + '../../infrastructure/Modules': (this.Modules = { + hooks: { + fire: sinon.stub().yields(null, []), + }, + }), './V1SubscriptionManager': {}, '../Publishers/PublishersGetter': this.PublishersGetter, './SubscriptionHelper': {}, @@ -512,14 +513,18 @@ describe('SubscriptionViewModelBuilder', function () { null, this.individualSubscription ) - this.PaymentService.getPaymentFromRecord.yields(null, { - subscription: this.paymentRecord, - account: new PaymentProviderAccount({ - email: 'example@example.com', - hasPastDueInvoice: false, - }), - coupons: [], - }) + this.Modules.hooks.fire + .withArgs('getPaymentFromRecord', this.individualSubscription) + .yields(null, [ + { + subscription: this.paymentRecord, + account: new PaymentProviderAccount({ + email: 'example@example.com', + hasPastDueInvoice: false, + }), + coupons: [], + }, + ]) const result = await this.SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel( this.user @@ -585,11 +590,15 @@ describe('SubscriptionViewModelBuilder', function () { }), ], }) - this.PaymentService.getPaymentFromRecord.yields(null, { - subscription: this.paymentRecord, - account: {}, - coupons: [], - }) + this.Modules.hooks.fire + .withArgs('getPaymentFromRecord', this.individualSubscription) + .yields(null, [ + { + subscription: this.paymentRecord, + account: {}, + coupons: [], + }, + ]) const result = await this.SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel( this.user