diff --git a/src/controller/checkout.controller.ts b/src/controller/checkout.controller.ts index c6e2c9b1..1c279204 100644 --- a/src/controller/checkout.controller.ts +++ b/src/controller/checkout.controller.ts @@ -12,7 +12,7 @@ import { getAllowedCurrencies, isValidCurrency } from '../utils/currency'; import { signUserToken } from '../utils/signUserToken'; import { verifyRecaptcha } from '../utils/verifyRecaptcha'; import { setupAuth } from '../plugins/auth'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; import { UserType } from '../core/users/User'; export function checkoutController(usersService: UsersService, paymentsService: PaymentService) { @@ -85,7 +85,7 @@ export function checkoutController(usersService: UsersService, paymentsService: const userExists = await usersService.findUserByUuid(userUuid).catch(() => null); if (userExists) { - await stripePaymentsAdapter.updateCustomer(userExists.customerId, { + await stripeAdapter.updateCustomer(userExists.customerId, { name: customerName, email, address: { @@ -99,7 +99,7 @@ export function checkoutController(usersService: UsersService, paymentsService: }); customerId = userExists.customerId; } else { - const { id } = await stripePaymentsAdapter.createCustomer({ + const { id } = await stripeAdapter.createCustomer({ name: customerName, email, address: { @@ -190,7 +190,7 @@ export function checkoutController(usersService: UsersService, paymentsService: throw new ForbiddenError(); } - const price = await paymentsService.getPriceById(priceId); + const price = await stripeAdapter.getPriceById(priceId); if (price.type === UserType.Business) throw new BadRequestError('Business plan is no longer available'); @@ -286,7 +286,7 @@ export function checkoutController(usersService: UsersService, paymentsService: throw new ForbiddenError(); } - const price = await paymentsService.getPriceById(priceId); + const price = await stripeAdapter.getPriceById(priceId); if (price.interval !== 'lifetime') { throw new BadRequestError('Only lifetime plans are supported'); @@ -362,12 +362,11 @@ export function checkoutController(usersService: UsersService, paymentsService: const userUuid = req.user?.payload?.uuid; const user = await usersService.findUserByUuid(userUuid).catch(() => null); - const price = await paymentsService.getPriceById(priceId, currency); - + const price = await stripeAdapter.getPriceById(priceId, currency); let amount = price.amount; - if (promoCodeName) { - const couponCode = await paymentsService.getPromoCodeByName(price.product, promoCodeName); + if (promoCodeName && price.productId) { + const couponCode = await paymentsService.getPromoCodeByName(price.productId, promoCodeName); if (couponCode.amountOff) { amount = Math.max(0, price.amount - couponCode.amountOff); } else if (couponCode.percentOff) { diff --git a/src/controller/object-storage.controller.ts b/src/controller/object-storage.controller.ts index 37e7857c..c9c4876e 100644 --- a/src/controller/object-storage.controller.ts +++ b/src/controller/object-storage.controller.ts @@ -7,7 +7,7 @@ import { ForbiddenError, UnauthorizedError } from '../errors/Errors'; import config from '../config'; import Stripe from 'stripe'; import { setupAuth } from '../plugins/auth'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; function signUserToken(customerId: string) { return jwt.sign({ customerId }, config.JWT_SECRET); @@ -58,7 +58,7 @@ export function objectStorageController(paymentService: PaymentService) { if (userExists) { customerId = userExists.id; } else { - const { id } = await stripePaymentsAdapter.createCustomer({ + const { id } = await stripeAdapter.createCustomer({ name: customerName, email, address: { @@ -186,7 +186,7 @@ export function objectStorageController(paymentService: PaymentService) { const { planId, currency } = req.query; try { - const planObject = await paymentService.getObjectStoragePlanById(planId, currency); + const planObject = await stripeAdapter.getPriceById(planId, currency); return rep.status(200).send(planObject); } catch (error) { diff --git a/src/controller/payments.controller.ts b/src/controller/payments.controller.ts index 71db6772..4737a1e9 100644 --- a/src/controller/payments.controller.ts +++ b/src/controller/payments.controller.ts @@ -20,7 +20,7 @@ import { VERIFICATION_CHARGE } from '../constants'; import { setupAuth } from '../plugins/auth'; import { PaymentService } from '../services/payment.service'; import { InvalidLicenseCodeError } from '../errors/LicenseCodeErrors'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; const allowedCurrency = ['eur', 'usd']; @@ -206,7 +206,7 @@ export function paymentsController( async (req, rep) => { const user = await assertUser(req, rep, usersService); const { address, phoneNumber } = req.body; - await stripePaymentsAdapter.updateCustomer(user.customerId, { + await stripeAdapter.updateCustomer(user.customerId, { address: { line1: address, }, @@ -425,7 +425,6 @@ export function paymentsController( }, async (req, rep) => { const { currency } = req.query; - const userType = (req.query.userType as UserType) || UserType.Individual; const { currencyValue, isError, errorMessage } = checkCurrency(currency); @@ -433,7 +432,7 @@ export function paymentsController( return rep.status(400).send({ message: errorMessage }); } - return paymentService.getPrices(currencyValue, userType); + return stripeAdapter.getPrices(currencyValue); }, ); diff --git a/src/core/users/DetermineLifetimeConditions.ts b/src/core/users/DetermineLifetimeConditions.ts index 95f324bf..2712f7e5 100644 --- a/src/core/users/DetermineLifetimeConditions.ts +++ b/src/core/users/DetermineLifetimeConditions.ts @@ -6,7 +6,7 @@ import { User, UserType } from './User'; import { FREE_PLAN_BYTES_SPACE } from '../../constants'; import { BadRequestError } from '../../errors/Errors'; import Logger from '../../Logger'; -import { stripePaymentsAdapter } from '../../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../infrastructure/adapters/stripe.adapter'; export class DetermineLifetimeConditions { constructor( @@ -73,7 +73,7 @@ export class DetermineLifetimeConditions { maxSpaceBytes: number; tier: Tier; }> { - const customer = await stripePaymentsAdapter.getCustomer(user.customerId); + const customer = await stripeAdapter.getCustomer(user.customerId); const { email } = customer; const customersRelatedToUser = await this.paymentsService.getCustomersByEmail(email); diff --git a/src/core/users/DisplayPrice.ts b/src/core/users/DisplayPrice.ts index d5962470..b2a8c089 100644 --- a/src/core/users/DisplayPrice.ts +++ b/src/core/users/DisplayPrice.ts @@ -5,4 +5,7 @@ export interface DisplayPrice { interval: 'year' | 'month' | 'lifetime'; amount: number; currency: string; + commitmentPlan?: boolean; + minimumSeats?: number; + maximumSeats?: number; } diff --git a/src/infrastructure/adapters/stripe.adapter.ts b/src/infrastructure/adapters/stripe.adapter.ts index 2983ef42..32e1ed5f 100644 --- a/src/infrastructure/adapters/stripe.adapter.ts +++ b/src/infrastructure/adapters/stripe.adapter.ts @@ -5,16 +5,14 @@ import { PaymentsAdapter } from '../domain/ports/payments.adapter'; import { Customer, CreateCustomerParams, UpdateCustomerParams } from '../domain/entities/customer'; import envVariablesConfig from '../../config'; import { PaymentMethod } from '../domain/entities/paymentMethod'; +import { Price } from '../domain/entities/price'; +import { UserType } from '../../core/users/User'; -export class StripePaymentsAdapter implements PaymentsAdapter { - private readonly provider: Stripe = new Stripe(envVariablesConfig.STRIPE_SECRET_KEY, { +export class StripeAdapter implements PaymentsAdapter { + readonly provider: Stripe = new Stripe(envVariablesConfig.STRIPE_SECRET_KEY, { apiVersion: '2025-02-24.acacia', }); - getInstance(): Stripe { - return this.provider; - } - async createCustomer(params: Partial): Promise { const stripeCustomer = await this.provider.customers.create(this.toStripeCustomerParams(params)); @@ -55,6 +53,50 @@ export class StripePaymentsAdapter implements PaymentsAdapter { return PaymentMethod.toDomain(paymentMethods); } + async getPrices(currency: string = 'eur'): Promise { + const prices = await this.provider.prices.search({ + query: `metadata["show"]:"1" active:"true" currency:"${currency}"`, + expand: ['data.currency_options', 'data.product'], + limit: 100, + }); + + return prices.data.map((price) => + Price.toDomain({ + id: price.id, + productId: (price.product as Stripe.Product).id, + bytes: Number.parseInt(price.metadata.bytes), + interval: this.getInterval(price.recurring!.interval), + commitmentPlan: this.hasAnnualCommitment(price), + amount: price.currency_options![currency].unit_amount as number, + currency: price.currency, + decimalAmount: (price.currency_options![currency].unit_amount as number) / 100, + type: price.metadata.type === 'business' ? UserType.Business : UserType.Individual, + }), + ); + } + + async getPriceById(priceId: Price['id'], currency: string = 'eur'): Promise { + const price = await this.provider.prices.retrieve(priceId, { + expand: ['currency_options', 'product'], + }); + + const isBusinessPlan = price.metadata?.type === 'business'; + + const businessSeats = isBusinessPlan ? this.getBusinessSeats(price) : undefined; + + return Price.toDomain({ + id: price.id, + productId: (price.product as Stripe.Product).id, + bytes: Number.parseInt(price.metadata.bytes), + interval: this.getInterval(price.recurring!.interval), + commitmentPlan: this.hasAnnualCommitment(price), + amount: price.currency_options![currency].unit_amount as number, + currency: price.currency, + decimalAmount: (price.currency_options![currency].unit_amount as number) / 100, + ...businessSeats, + }); + } + private toStripeCustomerParams(params: Partial): Stripe.CustomerCreateParams { return { ...(params.name && { name: params.name }), @@ -73,6 +115,30 @@ export class StripePaymentsAdapter implements PaymentsAdapter { ...(params.metadata && { metadata: params.metadata }), }; } + + private hasAnnualCommitment(price: Stripe.Price): boolean { + return price?.metadata.annualCommitment === 'true'; + } + + private getInterval(interval: Stripe.Price.Recurring.Interval): 'year' | 'month' | 'lifetime' { + switch (interval) { + case 'year': + return 'year'; + case 'month': + return 'month'; + default: + return 'lifetime'; + } + } + + private getBusinessSeats(price: Stripe.Price): { + minimumSeats: number; + maximumSeats: number; + } { + const minimumSeats = Number.parseInt(price.metadata.minimumSeats); + const maximumSeats = Number.parseInt(price.metadata.maximumSeats); + return { minimumSeats, maximumSeats }; + } } -export const stripePaymentsAdapter = new StripePaymentsAdapter(); +export const stripeAdapter = new StripeAdapter(); diff --git a/src/infrastructure/domain/entities/price.ts b/src/infrastructure/domain/entities/price.ts new file mode 100644 index 00000000..484836fa --- /dev/null +++ b/src/infrastructure/domain/entities/price.ts @@ -0,0 +1,52 @@ +import { RequestedPlanData } from '../../../types/subscription'; + +export class Price { + constructor(public readonly price: RequestedPlanData) {} + + static toDomain(price: RequestedPlanData) { + return new Price(price); + } + + get id() { + return this.price.id; + } + + get productId() { + return this.price.productId; + } + + get bytes() { + return this.price.bytes; + } + + get amount() { + return this.price.amount; + } + + get currency() { + return this.price.currency; + } + + get interval() { + return this.price.interval; + } + + get businessSeats() { + return { + minimumSeats: this.price.minimumSeats, + maximumSeats: this.price.maximumSeats, + }; + } + + get decimalAmount() { + return this.price.decimalAmount; + } + + get type() { + return this.price.type; + } + + get commitmentPlan() { + return this.price.commitmentPlan; + } +} diff --git a/src/infrastructure/domain/ports/payments.adapter.ts b/src/infrastructure/domain/ports/payments.adapter.ts index 592e66f4..49f6452f 100644 --- a/src/infrastructure/domain/ports/payments.adapter.ts +++ b/src/infrastructure/domain/ports/payments.adapter.ts @@ -1,5 +1,6 @@ import { Customer, CreateCustomerParams, UpdateCustomerParams } from '../entities/customer'; import { PaymentMethod } from '../entities/paymentMethod'; +import { Price } from '../entities/price'; export interface PaymentsAdapter { createCustomer: (params: CreateCustomerParams) => Promise; @@ -7,4 +8,6 @@ export interface PaymentsAdapter { getCustomer: (customerId: Customer['id']) => Promise; searchCustomer: (email: Customer['email']) => Promise; retrievePaymentMethod: (paymentMethodId: PaymentMethod['id']) => Promise; + getPriceById: (priceId: Price['id'], currency: string) => Promise; + getPrices: (currency: string) => Promise; } diff --git a/src/services/licenseCodes.service.ts b/src/services/licenseCodes.service.ts index 2c8464cd..8dd944b6 100644 --- a/src/services/licenseCodes.service.ts +++ b/src/services/licenseCodes.service.ts @@ -3,7 +3,7 @@ import { LicenseCodesRepository } from '../core/users/LicenseCodeRepository'; import { User } from '../core/users/User'; import { PaymentService } from './payment.service'; import { UsersService } from './users.service'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; import { Customer } from '../infrastructure/domain/entities/customer'; import { InvalidLicenseCodeError } from '../errors/LicenseCodeErrors'; @@ -73,9 +73,9 @@ export class LicenseCodesService { let customer: Customer; if (maybeExistingUser) { - customer = await stripePaymentsAdapter.getCustomer(maybeExistingUser.customerId); + customer = await stripeAdapter.getCustomer(maybeExistingUser.customerId); } else { - customer = await stripePaymentsAdapter.createCustomer({ + customer = await stripeAdapter.createCustomer({ name: user.name || 'Internxt User', email: user.email, }); diff --git a/src/services/payment.service.ts b/src/services/payment.service.ts index 0ca3412a..ebf11503 100644 --- a/src/services/payment.service.ts +++ b/src/services/payment.service.ts @@ -1,7 +1,6 @@ import Stripe from 'stripe'; import dayjs from 'dayjs'; -import { DisplayPrice } from '../core/users/DisplayPrice'; import { ProductsRepository } from '../core/users/ProductsRepository'; import { UserSubscription, UserType } from '../core/users/User'; import { Bit2MeService } from './bit2me.service'; @@ -12,7 +11,6 @@ import { IncompatibleSubscriptionTypesError, CustomerNotFoundError, MissingParametersError, - NotFoundPlanByIdError, PromoCodeIsNotValidError, ExistingSubscriptionError, InvalidTaxIdError, @@ -28,7 +26,6 @@ import { CustomerEmail, ExtendedSubscription, PriceId, - PlanId, Subscription, SubscriptionId, Invoice, @@ -37,9 +34,9 @@ import { CustomerSource, Customer as StripeCustomer, } from '../types/stripe'; -import { PaymentIntent, PromotionCode, PriceByIdResponse } from '../types/payment'; -import { RenewalPeriod, PlanSubscription, SubscriptionCreated, RequestedPlanData } from '../types/subscription'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { PaymentIntent, PromotionCode } from '../types/payment'; +import { RenewalPeriod, PlanSubscription, SubscriptionCreated } from '../types/subscription'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; export class PaymentService { constructor( @@ -269,7 +266,7 @@ export class PaymentService { if (isLifetime && isCryptoCurrency(currency)) { const normalizedCurrencyForBit2Me = normalizeForBit2Me(currency); - const customer = await stripePaymentsAdapter.getCustomer(customerId); + const customer = await stripeAdapter.getCustomer(customerId); const customerName = customer.name; const customerAddress = customer.address?.line1; @@ -905,138 +902,6 @@ export class PaymentService { }; } - async getPrices(currency?: string, userType: UserType = UserType.Individual): Promise { - const currencyValue = currency ?? 'eur'; - - const res = await this.provider.prices.search({ - query: `metadata["show"]:"1" active:"true" currency:"${currencyValue}"`, - expand: ['data.currency_options', 'data.product'], - limit: 100, - }); - - return res.data - .filter((price) => { - const priceProductType = ((price.product as Stripe.Product).metadata.type as UserType) || UserType.Individual; - return ( - price.metadata.maxSpaceBytes && - price.currency_options && - price.currency_options[currencyValue].unit_amount && - priceProductType === userType - ); - }) - .map((price) => { - const hasAnnualCommitment = this.hasAnnualCommitment(price); - const recurringInterval = hasAnnualCommitment ? 'year' : (price.recurring?.interval as 'year' | 'month'); - - return { - id: price.id, - productId: (price.product as Stripe.Product).id, - currency: currencyValue, - amount: price.currency_options![currencyValue].unit_amount as number, - bytes: parseInt(price.metadata.maxSpaceBytes), - interval: price.type === 'one_time' ? 'lifetime' : recurringInterval, - }; - }); - } - - async getPricesRaw(currency?: string, expandProduct = false): Promise { - const currencyValue = currency ?? 'eur'; - - const expandOptions = ['data.currency_options']; - - if (expandProduct) { - expandOptions.push('data.product'); - } - - //!TODO: add metadata["show"]:"1" in query param - const res = await this.provider.prices.search({ - query: `active:"true" currency:"${currencyValue}"`, - expand: expandOptions, - limit: 100, - }); - - return res.data.filter( - (price) => - price.metadata.maxSpaceBytes && price.currency_options && price.currency_options[currencyValue].unit_amount, - ); - } - - async getUpsellProduct(productId: Stripe.Product['id'], currency: string): Promise { - const productData = await this.provider.prices.list({ - active: true, - product: productId, - currency: currency, - expand: ['data.currency_options'], - }); - - const upsellProduct = productData.data.find((productItem) => productItem.recurring?.interval === 'year'); - - return upsellProduct; - } - - async getObjectStoragePlanById(priceId: PlanId, currency?: string) { - const currencyValue = currency ?? 'eur'; - try { - const price = await this.provider.prices.retrieve(priceId, {}); - - const { id, metadata, type, recurring } = price; - - const selectedPlan: RequestedPlanData = { - id: id, - currency: currencyValue, - amount: price.unit_amount as number, - bytes: parseInt(metadata?.maxSpaceBytes), - interval: type === 'one_time' ? 'lifetime' : (recurring?.interval as 'year' | 'month'), - decimalAmount: (price.unit_amount as number) / 100, - }; - - return selectedPlan; - } catch (err) { - const error = err as Error; - if (error.message.includes('No such price')) throw new NotFoundPlanByIdError(priceId); - throw new Error(error.message); - } - } - - /** - * Returns the requested price if exists - * @param priceId - The id of the requested price - * @param currency - The currency of the requested price - * @returns - The selected price if it exists and it is active - */ - async getPriceById(priceId: string, currency = 'eur'): Promise { - const availablePrices = await this.getPricesRaw(currency); - - const selectedPrice = availablePrices.find((price) => price.id === priceId && price.active); - - if (!selectedPrice) { - throw new NotFoundError('The requested price does not exist'); - } - - let businessSeats; - const { currency_options, recurring, metadata, type, product } = selectedPrice; - const isBusinessPrice = metadata?.type === 'business'; - - if (isBusinessPrice) { - businessSeats = { - minimumSeats: Number(metadata.minimumSeats), - maximumSeats: Number(metadata.maximumSeats), - }; - } - - return { - id: priceId, - currency, - amount: currency_options![currency].unit_amount as number, - bytes: Number.parseInt(metadata?.maxSpaceBytes), - interval: type === 'one_time' ? 'lifetime' : recurring?.interval, - decimalAmount: (currency_options![currency].unit_amount as number) / 100, - type: isBusinessPrice ? UserType.Business : UserType.Individual, - product: product as string, - ...businessSeats, - }; - } - /** * Returns the tax for a given price * @param priceId - The Id of the price that we want to calculate the tax for diff --git a/src/webhooks/handleFundsCaptured.ts b/src/webhooks/handleFundsCaptured.ts index b993fbb1..a8cf9a2d 100644 --- a/src/webhooks/handleFundsCaptured.ts +++ b/src/webhooks/handleFundsCaptured.ts @@ -7,7 +7,7 @@ import { ConflictError } from '../errors/Errors'; import { UserType } from '../core/users/User'; import axios from 'axios'; import { VERIFICATION_CHARGE } from '../constants'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; export default async function handleFundsCaptured( paymentIntent: Stripe.PaymentIntent, @@ -32,7 +32,7 @@ export default async function handleFundsCaptured( `Received successful payment intent ${paymentIntent.id} from customer ${paymentIntent.customer as string}`, ); - const customer = await stripePaymentsAdapter.getCustomer(paymentIntent.customer as string); + const customer = await stripeAdapter.getCustomer(paymentIntent.customer as string); logger.info(`Object Storage for user ${customer.email} (customer ${customer.id}) is being initialized...`); diff --git a/src/webhooks/handleInvoicePaymentFailed.ts b/src/webhooks/handleInvoicePaymentFailed.ts index df50b81d..2a3a8a16 100644 --- a/src/webhooks/handleInvoicePaymentFailed.ts +++ b/src/webhooks/handleInvoicePaymentFailed.ts @@ -3,7 +3,7 @@ import { FastifyLoggerInstance } from 'fastify'; import { PaymentService } from '../services/payment.service'; import { ObjectStorageService } from '../services/objectStorage.service'; import { UsersService } from '../services/users.service'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; function isProduct(product: Stripe.Product | Stripe.DeletedProduct): product is Stripe.Product { return ( @@ -51,7 +51,7 @@ export default async function handleInvoicePaymentFailed( throw new Error('No customer found for this payment'); } - const customer = await stripePaymentsAdapter.getCustomer(invoice.customer as string); + const customer = await stripeAdapter.getCustomer(invoice.customer as string); const relevantLineItem = await findObjectStorageLineItem(invoice, paymentService); diff --git a/src/webhooks/handleSubscriptionCanceled.ts b/src/webhooks/handleSubscriptionCanceled.ts index f4fd03e5..6f8a7848 100644 --- a/src/webhooks/handleSubscriptionCanceled.ts +++ b/src/webhooks/handleSubscriptionCanceled.ts @@ -10,7 +10,7 @@ import { ObjectStorageService } from '../services/objectStorage.service'; import { handleCancelPlan } from './utils/handleCancelPlan'; import { TierNotFoundError, TiersService } from '../services/tiers.service'; import { Service } from '../core/users/Tier'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; import { Customer } from '../infrastructure/domain/entities/customer'; import { klaviyoService } from '../services/klaviyo.service'; import Logger from '../Logger'; @@ -61,8 +61,8 @@ export default async function handleSubscriptionCanceled( const customerId = subscription.customer as string; const productId = subscription.items.data[0].price.product as string; const { metadata: productMetadata } = await paymentService.getProduct(productId); - const customer = await stripePaymentsAdapter.getCustomer(customerId); - + const customer = await stripeAdapter.getCustomer(customerId); + if (isObjectStorageProduct(productMetadata)) { await handleObjectStorageSubscriptionCancelled(customer, subscription, objectStorageService, paymentService, log); return; @@ -103,7 +103,7 @@ export default async function handleSubscriptionCanceled( } catch (error) { const err = error as Error; log.error(`[SUB CANCEL/ERROR]: Error canceling tier product. ERROR: ${err.stack ?? err.message}`); - + if (!(error instanceof TierNotFoundError)) { throw error; } diff --git a/src/webhooks/handleSubscriptionUpdated.ts b/src/webhooks/handleSubscriptionUpdated.ts index e042ed25..53418f7d 100644 --- a/src/webhooks/handleSubscriptionUpdated.ts +++ b/src/webhooks/handleSubscriptionUpdated.ts @@ -7,7 +7,7 @@ import { UsersService } from '../services/users.service'; import { AppConfig } from '../config'; import { UserType } from '../core/users/User'; import { ObjectStorageService } from '../services/objectStorage.service'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; import { Customer } from '../infrastructure/domain/entities/customer'; function isObjectStorageProduct(meta: Stripe.Metadata): boolean { @@ -57,7 +57,7 @@ export default async function handleSubscriptionUpdated( log.info(`Object storage customer ${customerId} with sub ${subscription.id} has been scheduled for cancelation`); await handleObjectStorageScheduledForCancelation( - await stripePaymentsAdapter.getCustomer(customerId), + await stripeAdapter.getCustomer(customerId), subscription, objectStorageService, log, diff --git a/src/webhooks/index.ts b/src/webhooks/index.ts index 3452c3fb..5a6a4e92 100644 --- a/src/webhooks/index.ts +++ b/src/webhooks/index.ts @@ -18,7 +18,7 @@ import { DetermineLifetimeConditions } from '../core/users/DetermineLifetimeCond import { ObjectStorageWebhookHandler } from './events/ObjectStorageWebhookHandler'; import { InvoiceCompletedHandler } from './events/invoices/InvoiceCompletedHandler'; import Logger from '../Logger'; -import { stripePaymentsAdapter } from '../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../infrastructure/adapters/stripe.adapter'; export default function ( stripe: Stripe, @@ -97,11 +97,11 @@ export default function ( case 'payment_intent.succeeded': { const eventData = event.data.object; - const paymentMethod = await stripePaymentsAdapter.retrievePaymentMethod(eventData.payment_method as string); + const paymentMethod = await stripeAdapter.retrievePaymentMethod(eventData.payment_method as string); const userAddressBillingDetails = paymentMethod.getAddress(); if (userAddressBillingDetails) { - await stripePaymentsAdapter.updateCustomer(eventData.customer as string, { + await stripeAdapter.updateCustomer(eventData.customer as string, { address: { city: userAddressBillingDetails.city, line1: userAddressBillingDetails.line1, @@ -117,7 +117,7 @@ export default function ( case 'invoice.paid': { const invoice = event.data.object; - const customer = await stripePaymentsAdapter.getCustomer(invoice.customer as string); + const customer = await stripeAdapter.getCustomer(invoice.customer as string); const determineLifetimeConditions = new DetermineLifetimeConditions(paymentService, tiersService); const objectStorageWebhookHandler = new ObjectStorageWebhookHandler(objectStorageService, paymentService); diff --git a/src/webhooks/providers/bit2me/index.ts b/src/webhooks/providers/bit2me/index.ts index 7e405074..d77cdf64 100644 --- a/src/webhooks/providers/bit2me/index.ts +++ b/src/webhooks/providers/bit2me/index.ts @@ -13,7 +13,7 @@ import { InvoiceCompletedHandler } from '../../events/invoices/InvoiceCompletedH import { DetermineLifetimeConditions } from '../../../core/users/DetermineLifetimeConditions'; import { ObjectStorageWebhookHandler } from '../../events/ObjectStorageWebhookHandler'; import Logger from '../../../Logger'; -import { stripePaymentsAdapter } from '../../../infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../infrastructure/adapters/stripe.adapter'; export interface CryptoWebhookDependencies { storageService: StorageService; @@ -94,7 +94,7 @@ export default function ({ return rep.status(200).send(); } - const customer = await stripePaymentsAdapter.getCustomer(customerId); + const customer = await stripeAdapter.getCustomer(customerId); await paymentService.markInvoiceAsPaid(stripeInvoiceId); diff --git a/tests/src/controller/checkout.controller.test.ts b/tests/src/controller/checkout.controller.test.ts index ddd9d230..91b1f1f2 100644 --- a/tests/src/controller/checkout.controller.test.ts +++ b/tests/src/controller/checkout.controller.test.ts @@ -5,6 +5,7 @@ import { getCustomer, getInvoice, getPrice, + getPriceEntity, getRawCryptoInvoiceResponse, getTaxes, getUser, @@ -22,7 +23,7 @@ import Stripe from 'stripe'; import { AllowedCryptoCurrencies } from '../../../src/utils/currency'; import { Bit2MeService } from '../../../src/services/bit2me.service'; import * as verifyRecaptcha from '../../../src/utils/verifyRecaptcha'; -import { StripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { StripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; import { UserType } from '../../../src/core/users/User'; @@ -77,9 +78,7 @@ describe('Checkout controller', () => { jest.spyOn(verifyRecaptcha, 'verifyRecaptcha').mockResolvedValue(true); jest.spyOn(UsersService.prototype, 'findUserByUuid').mockResolvedValue(mockedUser); - const updateCustomerSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'updateCustomer') - .mockResolvedValue({} as any); + const updateCustomerSpy = jest.spyOn(StripeAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); const response = await app.inject({ path: '/checkout/customer', @@ -133,7 +132,7 @@ describe('Checkout controller', () => { jest.spyOn(verifyRecaptcha, 'verifyRecaptcha').mockResolvedValue(true); jest.spyOn(UsersService.prototype, 'findUserByUuid').mockRejectedValue(new Error('User not found')); const createCustomerSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'createCustomer') + .spyOn(StripeAdapter.prototype, 'createCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const insertUserSpy = jest.spyOn(UsersService.prototype, 'insertUser').mockResolvedValue(); @@ -188,7 +187,7 @@ describe('Checkout controller', () => { jest.spyOn(verifyRecaptcha, 'verifyRecaptcha').mockResolvedValue(true); jest.spyOn(UsersService.prototype, 'findUserByUuid').mockResolvedValue(mockedUser); - jest.spyOn(StripePaymentsAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); + jest.spyOn(StripeAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); const attachVatIdSpy = jest .spyOn(PaymentService.prototype, 'getVatIdAndAttachTaxIdToCustomer') .mockResolvedValue(); @@ -231,7 +230,7 @@ describe('Checkout controller', () => { jest.spyOn(verifyRecaptcha, 'verifyRecaptcha').mockResolvedValue(true); jest.spyOn(UsersService.prototype, 'findUserByUuid').mockResolvedValue(mockedUser); - jest.spyOn(StripePaymentsAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); + jest.spyOn(StripeAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); const attachVatIdSpy = jest.spyOn(PaymentService.prototype, 'getVatIdAndAttachTaxIdToCustomer'); const response = await app.inject({ @@ -288,7 +287,7 @@ describe('Checkout controller', () => { const authToken = getValidAuthToken(mockedUser.uuid); const userToken = getValidUserToken({ customerId: mockedUser.customerId }); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue({ + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue({ type: UserType.Individual, } as any); jest.spyOn(PaymentService.prototype, 'createSubscription').mockResolvedValue(mockedSubscriptionResponse); @@ -325,7 +324,7 @@ describe('Checkout controller', () => { const authToken = getValidAuthToken(mockedUser.uuid); const userToken = getValidUserToken({ customerId: mockedUser.customerId }); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue({ + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue({ type: UserType.Business, } as any); jest.spyOn(verifyRecaptcha, 'verifyRecaptcha').mockResolvedValue(true); @@ -492,7 +491,7 @@ describe('Checkout controller', () => { test('When the user wants to pay a one time plan, then an invoice is created and the client secret is returned', async () => { const mockedUser = getUser(); const mockedInvoice = getInvoice(); - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'lifetime', }); @@ -505,7 +504,7 @@ describe('Checkout controller', () => { } as const; const mockedCaptchaToken = 'captcha_token'; - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); (fetchUserStorage as jest.Mock).mockResolvedValue({ canExpand: true, }); @@ -536,7 +535,7 @@ describe('Checkout controller', () => { test('when the user want to pay a one time plan using crypto currencies, then an invoice is created and the specific payload containing the QR Link is returned', async () => { const mockedUser = getUser(); const mockedInvoice = getInvoice(); - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'lifetime', }); @@ -557,7 +556,7 @@ describe('Checkout controller', () => { token: getValidUserToken({ invoiceId: 'invoice_id' }), } as const; - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); (fetchUserStorage as jest.Mock).mockResolvedValue({ canExpand: true, }); @@ -588,13 +587,13 @@ describe('Checkout controller', () => { test('When the user wants to pay a subscription plan creating an invoice, then an error indicating so is thrown', async () => { const mockedUser = getUser(); - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'year', }); const authToken = getValidAuthToken(mockedUser.uuid); const userToken = getValidUserToken({ customerId: mockedUser.customerId }); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); const response = await app.inject({ path: '/checkout/payment-intent', @@ -616,14 +615,14 @@ describe('Checkout controller', () => { test('When the user already has the max storage allowed, then an error indicating so is thrown', async () => { const mockedUser = getUser(); const mockedInvoice = getInvoice(); - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'lifetime', }); const authToken = getValidAuthToken(mockedUser.uuid); const userToken = getValidUserToken({ customerId: mockedUser.customerId }); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); (fetchUserStorage as jest.Mock).mockResolvedValue({ canExpand: false, }); @@ -826,13 +825,13 @@ describe('Checkout controller', () => { describe('Get Price by its ID', () => { test('When the user wants to get a price by its ID, then the price is returned with its taxes', async () => { - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'year', }); const taxes = mockCalculateTaxFor(mockedPrice.amount); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); jest .spyOn(PaymentService.prototype, 'calculateTax') .mockResolvedValueOnce(taxes as unknown as Stripe.Tax.Calculation); @@ -850,7 +849,7 @@ describe('Checkout controller', () => { expect(response.statusCode).toBe(200); expect(responseBody).toStrictEqual({ - price: mockedPrice, + price: JSON.parse(JSON.stringify(mockedPrice)), taxes: { tax: taxes.tax_amount_exclusive, decimalTax: taxes.tax_amount_exclusive / 100, @@ -862,7 +861,7 @@ describe('Checkout controller', () => { describe('Handling promo codes', () => { test('When the user provides a promo code with amount off, then the price is returned with the discount applied', async () => { - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'year', }); @@ -875,7 +874,7 @@ describe('Checkout controller', () => { const discountedAmount = mockedPrice.amount - promoCode.amountOff; const taxes = mockCalculateTaxFor(discountedAmount); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); jest.spyOn(PaymentService.prototype, 'getPromoCodeByName').mockResolvedValue(promoCode); jest .spyOn(PaymentService.prototype, 'calculateTax') @@ -895,7 +894,7 @@ describe('Checkout controller', () => { expect(response.statusCode).toBe(200); expect(responseBody).toStrictEqual({ - price: mockedPrice, + price: JSON.parse(JSON.stringify(mockedPrice)), taxes: { tax: taxes.tax_amount_exclusive, decimalTax: taxes.tax_amount_exclusive / 100, @@ -906,7 +905,7 @@ describe('Checkout controller', () => { }); test('When the user provides a promo code with percent off, then the price is returned with the discount applied', async () => { - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'year', }); @@ -920,7 +919,7 @@ describe('Checkout controller', () => { const discountedAmount = mockedPrice.amount - discount; const taxes = mockCalculateTaxFor(discountedAmount); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); jest.spyOn(PaymentService.prototype, 'getPromoCodeByName').mockResolvedValue(promoCode); jest .spyOn(PaymentService.prototype, 'calculateTax') @@ -940,7 +939,7 @@ describe('Checkout controller', () => { expect(response.statusCode).toBe(200); expect(responseBody).toStrictEqual({ - price: mockedPrice, + price: JSON.parse(JSON.stringify(mockedPrice)), taxes: { tax: taxes.tax_amount_exclusive, decimalTax: taxes.tax_amount_exclusive / 100, @@ -951,14 +950,14 @@ describe('Checkout controller', () => { }); test('When the user provides a promo code with a discount that is more than the product price, then the price should be 0 instead of a negative price', async () => { - const mockedPrice = { + const mockedPrice = getPriceEntity({ ...priceById({ bytes: 123456789, interval: 'year', }), amount: 14000, decimalAmount: 140, - }; + }); const promoCode = { promoCodeName: 'promo_code_name', amountOff: 15000, @@ -969,7 +968,7 @@ describe('Checkout controller', () => { const discountedAmount = 0; const taxes = mockCalculateTaxFor(discountedAmount); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); jest.spyOn(PaymentService.prototype, 'getPromoCodeByName').mockResolvedValue(promoCode); jest .spyOn(PaymentService.prototype, 'calculateTax') @@ -989,7 +988,7 @@ describe('Checkout controller', () => { expect(response.statusCode).toBe(200); expect(responseBody).toStrictEqual({ - price: mockedPrice, + price: JSON.parse(JSON.stringify(mockedPrice)), taxes: { tax: taxes.tax_amount_exclusive, decimalTax: taxes.tax_amount_exclusive / 100, @@ -1013,13 +1012,13 @@ describe('Checkout controller', () => { describe('User address, country and postal code are not provided', () => { test('When any of user location params are provided, then the price is returned with taxes to 0', async () => { - const mockedPrice = priceById({ + const mockedPrice = getPriceEntity({ bytes: 123456789, interval: 'year', }); const mockedTaxes = getTaxes(); - jest.spyOn(PaymentService.prototype, 'getPriceById').mockResolvedValue(mockedPrice); + jest.spyOn(StripeAdapter.prototype, 'getPriceById').mockResolvedValue(mockedPrice); jest.spyOn(PaymentService.prototype, 'calculateTax').mockResolvedValue(mockedTaxes); const response = await app.inject({ @@ -1034,7 +1033,7 @@ describe('Checkout controller', () => { expect(response.statusCode).toBe(200); expect(responseBody).toStrictEqual({ - price: mockedPrice, + price: JSON.parse(JSON.stringify(mockedPrice)), taxes: { tax: 0, decimalTax: 0, diff --git a/tests/src/controller/payments.controller.test.ts b/tests/src/controller/payments.controller.test.ts index 078424bd..106d93af 100644 --- a/tests/src/controller/payments.controller.test.ts +++ b/tests/src/controller/payments.controller.test.ts @@ -21,7 +21,7 @@ import { TierNotFoundError, TiersService } from '../../../src/services/tiers.ser import CacheService from '../../../src/services/cache.service'; import Stripe from 'stripe'; import { LicenseCodesService } from '../../../src/services/licenseCodes.service'; -import { StripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { StripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; jest.mock('../../../src/utils/assertUser'); @@ -196,7 +196,7 @@ describe('Payment controller e2e tests', () => { .spyOn(PaymentService.prototype, 'getCustomerIdByEmail') .mockRejectedValue(new CustomerNotFoundError('Customer not found')); const createdCustomerSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'createCustomer') + .spyOn(StripeAdapter.prototype, 'createCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const response = await app.inject({ @@ -253,9 +253,7 @@ describe('Payment controller e2e tests', () => { jest .spyOn(PaymentService.prototype, 'getCustomerIdByEmail') .mockRejectedValue(new CustomerNotFoundError('Customer not found')); - jest - .spyOn(StripePaymentsAdapter.prototype, 'createCustomer') - .mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(StripeAdapter.prototype, 'createCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); const attachVatIdSpy = jest .spyOn(PaymentService.prototype, 'getVatIdAndAttachTaxIdToCustomer') .mockImplementation(voidPromise); diff --git a/tests/src/core/users/DetermineLifetimeConditions.test.ts b/tests/src/core/users/DetermineLifetimeConditions.test.ts index ba6ec204..df3b99f5 100644 --- a/tests/src/core/users/DetermineLifetimeConditions.test.ts +++ b/tests/src/core/users/DetermineLifetimeConditions.test.ts @@ -3,7 +3,7 @@ import { getUser, newTier, getCustomer, getInvoice, getSubscription, getPrice, g import { Service } from '../../../../src/core/users/Tier'; import { BadRequestError, InternalServerError } from '../../../../src/errors/Errors'; import { createTestServices } from '../../helpers/services-factory'; -import { stripePaymentsAdapter } from '../../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../../src/infrastructure/domain/entities/customer'; describe('Determining Lifetime conditions', () => { @@ -113,7 +113,7 @@ describe('Determining Lifetime conditions', () => { jest.spyOn(tiersService, 'getTierProductsByProductsId').mockResolvedValue(mockedTier); jest.spyOn(tiersService, 'getTiersProductsByUserId').mockResolvedValue([mockedTier]); jest.spyOn(paymentService, 'getInvoicesFromUser').mockResolvedValue(mockedInvoices); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getCustomersByEmail').mockResolvedValue([getCustomer({ id: mockedUser.customerId })]); //@ts-ignore jest.spyOn(determineLifetimeConditions, 'getPaidInvoices').mockResolvedValue(mockedInvoices); @@ -133,7 +133,7 @@ describe('Determining Lifetime conditions', () => { const customer = getCustomer({ id: user.customerId }); const tierNotFoundError = new TierNotFoundError(`Tier not found for user ${user.uuid} when stacking lifetime`); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(customer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(customer)); jest.spyOn(paymentService, 'getCustomersByEmail').mockResolvedValue([customer]); jest.spyOn(paymentService, 'getInvoicesFromUser').mockResolvedValue([]); jest.spyOn(tiersService, 'getTiersProductsByUserId').mockRejectedValue(tierNotFoundError); @@ -150,7 +150,7 @@ describe('Determining Lifetime conditions', () => { const invoice = getInvoice(); const mockedTier = newTier({ billingType: 'lifetime' }); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(customer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(customer)); jest.spyOn(paymentService, 'getCustomersByEmail').mockResolvedValue([customer]); jest.spyOn(paymentService, 'getInvoicesFromUser').mockResolvedValue([invoice]); //@ts-ignore diff --git a/tests/src/fixtures.ts b/tests/src/fixtures.ts index dbfcc5fe..0b8a03e4 100644 --- a/tests/src/fixtures.ts +++ b/tests/src/fixtures.ts @@ -36,6 +36,24 @@ import { COUPON_BASE, } from './fixtures/stripe-base.generated'; import { HealthStatus } from '../../src/services/health.service'; +import { Price } from '../../src/infrastructure/domain/entities/price'; +import { RequestedPlanData } from '../../src/types/subscription'; + +export const getPriceEntity = (params?: Partial): Price => { + const mockedPrice = getPrice(); + const data: RequestedPlanData = { + id: mockedPrice.id, + productId: mockedPrice.product as string, + bytes: 1099511627776, + interval: 'year', + amount: mockedPrice.unit_amount as number, + currency: mockedPrice.currency, + decimalAmount: (mockedPrice.unit_amount as number) / 100, + type: UserType.Individual, + ...params, + }; + return Price.toDomain(data); +}; const randomDataGenerator = new Chance(); diff --git a/tests/src/infrastructure/adapters/stripe.adapter.test.ts b/tests/src/infrastructure/adapters/stripe.adapter.test.ts index fa9c8998..fa1bc600 100644 --- a/tests/src/infrastructure/adapters/stripe.adapter.test.ts +++ b/tests/src/infrastructure/adapters/stripe.adapter.test.ts @@ -1,5 +1,5 @@ import { getCustomer, getPaymentMethod } from '../../fixtures'; -import { stripePaymentsAdapter } from '../../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../../src/infrastructure/adapters/stripe.adapter'; import Stripe from 'stripe'; import { Customer } from '../../../../src/infrastructure/domain/entities/customer'; import { UserNotFoundError } from '../../../../src/errors/PaymentErrors'; @@ -11,12 +11,12 @@ describe('Stripe Adapter', () => { const mockedCustomer = getCustomer(); jest - .spyOn(stripePaymentsAdapter.getInstance().customers, 'create') + .spyOn(stripeAdapter.provider.customers, 'create') .mockResolvedValue(mockedCustomer as Stripe.Response); const metadata = { referralCode: 'ABC123' }; - const createdCustomer = await stripePaymentsAdapter.createCustomer({ + const createdCustomer = await stripeAdapter.createCustomer({ email: mockedCustomer.email as string, name: mockedCustomer.name as string, address: { @@ -39,10 +39,10 @@ describe('Stripe Adapter', () => { const mockedCustomer = getCustomer(); jest - .spyOn(stripePaymentsAdapter.getInstance().customers, 'update') + .spyOn(stripeAdapter.provider.customers, 'update') .mockResolvedValue(mockedCustomer as Stripe.Response); - const updatedCustomer = await stripePaymentsAdapter.updateCustomer(mockedCustomer.id, { + const updatedCustomer = await stripeAdapter.updateCustomer(mockedCustomer.id, { email: mockedCustomer.email as string, name: mockedCustomer.name as string, address: { @@ -82,10 +82,10 @@ describe('Stripe Adapter', () => { }); const updateSpy = jest - .spyOn(stripePaymentsAdapter.getInstance().customers, 'update') + .spyOn(stripeAdapter.provider.customers, 'update') .mockResolvedValue(updatedCustomer as Stripe.Response); - const result = await stripePaymentsAdapter.updateCustomer(initialCustomer.id, { + const result = await stripeAdapter.updateCustomer(initialCustomer.id, { name: 'Updated Name', }); @@ -114,10 +114,10 @@ describe('Stripe Adapter', () => { const mockedCustomer = getCustomer(); jest - .spyOn(stripePaymentsAdapter.getInstance().customers, 'retrieve') + .spyOn(stripeAdapter.provider.customers, 'retrieve') .mockResolvedValue(mockedCustomer as Stripe.Response); - const customer = await stripePaymentsAdapter.getCustomer(mockedCustomer.id); + const customer = await stripeAdapter.getCustomer(mockedCustomer.id); expect(customer).toStrictEqual(Customer.toDomain(mockedCustomer)); }); @@ -128,9 +128,9 @@ describe('Stripe Adapter', () => { }; const mockedError = new UserNotFoundError(); - jest.spyOn(stripePaymentsAdapter.getInstance().customers, 'retrieve').mockResolvedValue(mockedCustomer as any); + jest.spyOn(stripeAdapter.provider.customers, 'retrieve').mockResolvedValue(mockedCustomer as any); - await expect(stripePaymentsAdapter.getCustomer('')).rejects.toThrow(mockedError); + await expect(stripeAdapter.getCustomer('')).rejects.toThrow(mockedError); }); }); @@ -138,11 +138,11 @@ describe('Stripe Adapter', () => { test('When searching a customer, then the customer is returned', async () => { const mockedCustomer = getCustomer(); - jest.spyOn(stripePaymentsAdapter.getInstance().customers, 'search').mockResolvedValue({ + jest.spyOn(stripeAdapter.provider.customers, 'search').mockResolvedValue({ data: [mockedCustomer], } as any); - const customer = await stripePaymentsAdapter.searchCustomer(mockedCustomer.email as string); + const customer = await stripeAdapter.searchCustomer(mockedCustomer.email as string); expect(customer).toStrictEqual([Customer.toDomain(mockedCustomer)]); }); @@ -151,12 +151,12 @@ describe('Stripe Adapter', () => { const mockedError = new UserNotFoundError(); const mockedCustomer = getCustomer(); - jest.spyOn(stripePaymentsAdapter.getInstance().customers, 'search').mockResolvedValue({ + jest.spyOn(stripeAdapter.provider.customers, 'search').mockResolvedValue({ data: [], total_count: 0, } as any); - await expect(stripePaymentsAdapter.searchCustomer(mockedCustomer.email as string)).rejects.toThrow(mockedError); + await expect(stripeAdapter.searchCustomer(mockedCustomer.email as string)).rejects.toThrow(mockedError); }); }); @@ -165,10 +165,10 @@ describe('Stripe Adapter', () => { const mockedPaymentMethod = getPaymentMethod(); jest - .spyOn(stripePaymentsAdapter.getInstance().paymentMethods, 'retrieve') + .spyOn(stripeAdapter.provider.paymentMethods, 'retrieve') .mockResolvedValue(mockedPaymentMethod as Stripe.Response); - const paymentMethod = await stripePaymentsAdapter.retrievePaymentMethod(mockedPaymentMethod.id); + const paymentMethod = await stripeAdapter.retrievePaymentMethod(mockedPaymentMethod.id); expect(paymentMethod).toStrictEqual(PaymentMethod.toDomain(mockedPaymentMethod)); }); diff --git a/tests/src/services/licenseCodes.service.test.ts b/tests/src/services/licenseCodes.service.test.ts index 7359d95f..87da0858 100644 --- a/tests/src/services/licenseCodes.service.test.ts +++ b/tests/src/services/licenseCodes.service.test.ts @@ -2,7 +2,7 @@ import { LicenseCodeAlreadyAppliedError } from '../../../src/services/licenseCod import { getCustomer, getLicenseCode, getUser } from '../fixtures'; import { createTestServices } from '../helpers/services-factory'; import { UserNotFoundError } from '../../../src/errors/PaymentErrors'; -import { stripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; import { InvalidLicenseCodeError } from '../../../src/errors/LicenseCodeErrors'; @@ -54,7 +54,7 @@ describe('Tests for License Codes service', () => { .spyOn(licenseCodesRepository, 'findOne') .mockResolvedValue(mockedLicenseCode); const getCustomerSpy = jest - .spyOn(stripePaymentsAdapter, 'getCustomer') + .spyOn(stripeAdapter, 'getCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const findUserByUuidSpy = jest.spyOn(usersService, 'findUserByUuid').mockResolvedValue(mockedUser); const subscribeSpy = jest.spyOn(paymentService, 'subscribe').mockResolvedValue({ @@ -95,7 +95,7 @@ describe('Tests for License Codes service', () => { .mockResolvedValue(mockedLicenseCode); const findUserByUuidSpy = jest.spyOn(usersService, 'findUserByUuid').mockRejectedValue(new UserNotFoundError()); const createCustomerSpy = jest - .spyOn(stripePaymentsAdapter, 'createCustomer') + .spyOn(stripeAdapter, 'createCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const subscribeSpy = jest.spyOn(paymentService, 'subscribe').mockResolvedValue({ maxSpaceBytes: 100, diff --git a/tests/src/services/payment.service.test.ts b/tests/src/services/payment.service.test.ts index 2d52cdb9..75a37133 100644 --- a/tests/src/services/payment.service.test.ts +++ b/tests/src/services/payment.service.test.ts @@ -30,7 +30,7 @@ import { stripeNewVersion } from '../../../src/services/stripe'; import config from '../../../src/config'; import { generateQrCodeUrl } from '../../../src/utils/generateQrCodeUrl'; import jwt from 'jsonwebtoken'; -import { stripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; describe('Payments Service tests', () => { @@ -320,7 +320,7 @@ describe('Payments Service tests', () => { }); jest.spyOn(paymentService, 'getPrice').mockResolvedValue(mockedPrice); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockRejectedValue(new BadRequestError()); + jest.spyOn(stripeAdapter, 'getCustomer').mockRejectedValue(new BadRequestError()); jest .spyOn(stripeNewVersion.invoices, 'create') .mockResolvedValueOnce(mockedInvoice as unknown as Stripe.Response); @@ -396,7 +396,7 @@ describe('Payments Service tests', () => { config.JWT_SECRET, ); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValueOnce(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValueOnce(Customer.toDomain(mockedCustomer)); jest .spyOn(stripeNewVersion.invoices, 'create') .mockResolvedValueOnce(mockedInvoice as unknown as Stripe.Response); @@ -708,73 +708,6 @@ describe('Payments Service tests', () => { }); }); - describe('Fetch a price by its ID', () => { - it('When the price does not exist, an error indicating so is thrown', async () => { - const mockedPrices = getPrice(); - const invalidPriceId = 'invalid_price_id'; - - jest.spyOn(paymentService, 'getPricesRaw').mockResolvedValue([mockedPrices]); - - await expect(paymentService.getPriceById(invalidPriceId)).rejects.toThrow(NotFoundError); - }); - - it('When the price exists and belongs to a business product, then the price is returned with minimum and maximum seats', async () => { - const businessSeats = { - minimumSeats: 1, - maximumSeats: 3, - }; - const mockedPrice = getPrice({ - metadata: { - type: 'business', - maxSpaceBytes: '123456789', - minimumSeats: businessSeats.minimumSeats.toString(), - maximumSeats: businessSeats.maximumSeats.toString(), - }, - }); - const validPriceId = mockedPrice.id; - const priceResponse = { - id: validPriceId, - currency: mockedPrice.currency, - amount: mockedPrice.currency_options![mockedPrice.currency].unit_amount as number, - bytes: parseInt(mockedPrice.metadata?.maxSpaceBytes), - interval: mockedPrice.type === 'one_time' ? 'lifetime' : mockedPrice.recurring?.interval, - decimalAmount: (mockedPrice.currency_options![mockedPrice.currency].unit_amount as number) / 100, - product: mockedPrice.product as string, - type: UserType.Business, - ...businessSeats, - }; - jest.spyOn(paymentService, 'getPricesRaw').mockResolvedValue([mockedPrice]); - - const price = await paymentService.getPriceById(validPriceId); - - expect(price).toStrictEqual(priceResponse); - }); - - it('When the price exists, then the correct price object is returned', async () => { - const mockedPrice = getPrice({ - metadata: { - maxSpaceBytes: '123456789', - }, - }); - const validPriceId = mockedPrice.id; - const priceResponse = { - id: validPriceId, - currency: mockedPrice.currency, - amount: mockedPrice.currency_options![mockedPrice.currency].unit_amount as number, - bytes: parseInt(mockedPrice.metadata?.maxSpaceBytes), - interval: mockedPrice.type === 'one_time' ? 'lifetime' : mockedPrice.recurring?.interval, - decimalAmount: (mockedPrice.currency_options![mockedPrice.currency].unit_amount as number) / 100, - product: mockedPrice.product as string, - type: UserType.Individual, - }; - jest.spyOn(paymentService, 'getPricesRaw').mockResolvedValue([mockedPrice]); - - const price = await paymentService.getPriceById(validPriceId); - - expect(price).toStrictEqual(priceResponse); - }); - }); - describe('Get tax for a price', () => { it('When the params are correct, then a tax object is returned for the requested price', async () => { const mockedPrice = getPrice(); diff --git a/tests/src/utils/preloadMongoDBData.ts b/tests/src/utils/preloadMongoDBData.ts index f025627b..34739bb5 100644 --- a/tests/src/utils/preloadMongoDBData.ts +++ b/tests/src/utils/preloadMongoDBData.ts @@ -1,5 +1,5 @@ import { MongoClient } from 'mongodb'; -import { randomUUID } from 'crypto'; +import { randomUUID } from 'node:crypto'; import { UserType } from '../../../src/core/users/User'; import { getPrices, getUniqueCodes } from '../fixtures'; diff --git a/tests/src/webhooks/handleFundsCaptured.test.ts b/tests/src/webhooks/handleFundsCaptured.test.ts index bb674771..abdc798b 100644 --- a/tests/src/webhooks/handleFundsCaptured.test.ts +++ b/tests/src/webhooks/handleFundsCaptured.test.ts @@ -5,7 +5,7 @@ import handleFundsCaptured from '../../../src/webhooks/handleFundsCaptured'; import { ConflictError, InternalServerError } from '../../../src/errors/Errors'; import { UserSubscription, UserType } from '../../../src/core/users/User'; import { createTestServices } from '../helpers/services-factory'; -import { stripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; const logger: jest.Mocked = getLogger(); @@ -28,7 +28,7 @@ describe('Handling captured funds from a payment method', () => { describe('Checking the metadata', () => { it('When the payment intent does not contains the object-storage type in the metadata, then do nothing', async () => { const mockPaymentIntent = getPaymentIntent(); - const getCustomerSpy = jest.spyOn(stripePaymentsAdapter, 'getCustomer'); + const getCustomerSpy = jest.spyOn(stripeAdapter, 'getCustomer'); await handleFundsCaptured(mockPaymentIntent, paymentService, objectStorageService, stripe, logger); @@ -41,7 +41,7 @@ describe('Handling captured funds from a payment method', () => { type: 'business', }, }); - const getCustomerSpy = jest.spyOn(stripePaymentsAdapter, 'getCustomer'); + const getCustomerSpy = jest.spyOn(stripeAdapter, 'getCustomer'); await handleFundsCaptured(mockPaymentIntent, paymentService, objectStorageService, stripe, logger); @@ -54,7 +54,7 @@ describe('Handling captured funds from a payment method', () => { type: 'object-storage', }, }); - const getCustomerSpy = jest.spyOn(stripePaymentsAdapter, 'getCustomer'); + const getCustomerSpy = jest.spyOn(stripeAdapter, 'getCustomer'); await handleFundsCaptured(mockPaymentIntent, paymentService, objectStorageService, stripe, logger); @@ -72,7 +72,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); const cancelPaymentIntentSpy = jest.spyOn(stripe.paymentIntents, 'cancel'); const getUserSubscriptionSpy = jest .spyOn(paymentService, 'getUserSubscription') @@ -114,7 +114,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); jest.spyOn(stripe.paymentIntents, 'cancel').mockRejectedValue(unexpectedError); const getUserSubscriptionSpy = jest .spyOn(paymentService, 'getUserSubscription') @@ -145,7 +145,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); const cancelPaymentIntentSpy = jest .spyOn(stripe.paymentIntents, 'cancel') .mockResolvedValue(mockPaymentIntent as Stripe.Response); @@ -179,7 +179,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); const cancelPaymentIntentSpy = jest .spyOn(stripe.paymentIntents, 'cancel') .mockResolvedValue(mockPaymentIntent as Stripe.Response); @@ -212,7 +212,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); const cancelPaymentIntentSpy = jest .spyOn(stripe.paymentIntents, 'cancel') .mockResolvedValue(mockPaymentIntent as Stripe.Response); @@ -254,7 +254,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); jest .spyOn(stripe.paymentIntents, 'cancel') .mockResolvedValue(mockPaymentIntent as Stripe.Response); @@ -284,7 +284,7 @@ describe('Handling captured funds from a payment method', () => { const mockCustomer = getCustomer(); const mockSubscription = getCreateSubscriptionResponse(); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockCustomer)); jest .spyOn(stripe.paymentIntents, 'cancel') .mockResolvedValue(mockPaymentIntent as Stripe.Response); diff --git a/tests/src/webhooks/handleInvoicePaymentFailed.test.ts b/tests/src/webhooks/handleInvoicePaymentFailed.test.ts index eb45b27a..fa9a4c6b 100644 --- a/tests/src/webhooks/handleInvoicePaymentFailed.test.ts +++ b/tests/src/webhooks/handleInvoicePaymentFailed.test.ts @@ -2,7 +2,7 @@ import { FastifyBaseLogger } from 'fastify'; import { getCustomer, getInvoice, getLogger, getProduct } from '../fixtures'; import handleInvoicePaymentFailed from '../../../src/webhooks/handleInvoicePaymentFailed'; import { createTestServices } from '../helpers/services-factory'; -import { stripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; const logger: jest.Mocked = getLogger(); @@ -23,7 +23,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'object-storage' } } }); const getCustomerSpy = jest - .spyOn(stripePaymentsAdapter, 'getCustomer') + .spyOn(stripeAdapter, 'getCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); const findUserByCustomerIDSpy = jest.spyOn(usersService, 'findUserByCustomerID'); @@ -52,7 +52,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'non-object-storage' } } }); const mockedUser = { uuid: 'test-uuid-123', email: 'test@internxt.com' }; - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser as any); jest.spyOn(usersService, 'notifyFailedPayment').mockRejectedValue(new Error('Gateway error')); @@ -70,7 +70,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'non-object-storage' } } }); const mockedUser = { uuid: 'test-uuid-123', email: 'test@internxt.com' }; - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser as any); jest.spyOn(usersService, 'notifyFailedPayment').mockRejectedValue(new Error(errorMessage)); @@ -91,7 +91,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'non-object-storage' } } }); const mockedUser = { uuid: 'test-uuid-123', email: 'test@internxt.com' }; - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser as any); jest.spyOn(usersService, 'notifyFailedPayment').mockRejectedValue(nonErrorObject); @@ -111,7 +111,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedInvoice = getInvoice({ customer: customerId }); const mockedProduct = getProduct({ params: { metadata: { type: 'drive-product' } } }); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockRejectedValue(new Error(errorMessage)); const loggerErrorSpy = jest.spyOn(logger, 'error'); @@ -137,7 +137,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedInvoice = getInvoice({ customer: customerId }); const mockedProduct = getProduct({ params: { metadata: { type: 'object-storage' } } }); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); const findUserByCustomerIDSpy = jest.spyOn(usersService, 'findUserByCustomerID'); const notifyFailedPaymentSpy = jest.spyOn(usersService, 'notifyFailedPayment'); @@ -157,7 +157,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'drive-product' } } }); const mockedUser = { uuid: 'test-uuid-123', email: 'test@internxt.com' }; - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser as any); jest.spyOn(usersService, 'notifyFailedPayment').mockResolvedValue(); @@ -176,7 +176,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedInvoice = getInvoice({ customer: customerId }); const mockedProduct = getProduct({ params: { metadata: { type: 'drive-product' } } }); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(null as any); const loggerWarnSpy = jest.spyOn(logger, 'warn'); @@ -195,7 +195,7 @@ describe('Handle Invoice Payment Failed', () => { const mockedProduct = getProduct({ params: { metadata: { type: 'regular-product' } } }); const mockedUser = { uuid: 'test-uuid-123', email: 'test@internxt.com' }; - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser as any); const notifyFailedPaymentSpy = jest.spyOn(usersService, 'notifyFailedPayment'); diff --git a/tests/src/webhooks/handleSubscriptionCanceled.test.ts b/tests/src/webhooks/handleSubscriptionCanceled.test.ts index 0cc16688..04572dc0 100644 --- a/tests/src/webhooks/handleSubscriptionCanceled.test.ts +++ b/tests/src/webhooks/handleSubscriptionCanceled.test.ts @@ -6,7 +6,7 @@ import handleSubscriptionCanceled from '../../../src/webhooks/handleSubscription import { handleCancelPlan } from '../../../src/webhooks/utils/handleCancelPlan'; import { FREE_PLAN_BYTES_SPACE } from '../../../src/constants'; import { createTestServices } from '../helpers/services-factory'; -import { stripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { stripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; jest.mock('../../../src/webhooks/utils/handleCancelPlan'); @@ -30,7 +30,7 @@ describe('Process when a subscription is cancelled', () => { const getProductSpy = jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); const getCustomerSPy = jest - .spyOn(stripePaymentsAdapter, 'getCustomer') + .spyOn(stripeAdapter, 'getCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const findUserByCustomerIdSpy = jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser); await handleSubscriptionCanceled( @@ -76,7 +76,7 @@ describe('Process when a subscription is cancelled', () => { jest.spyOn(tiersService, 'getTierProductsByProductsId').mockResolvedValue(mockedFreeTier); const getProductSpy = jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); const getCustomerSPy = jest - .spyOn(stripePaymentsAdapter, 'getCustomer') + .spyOn(stripeAdapter, 'getCustomer') .mockResolvedValue(Customer.toDomain(mockedCustomer)); const findUserByCustomerIdSpy = jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser); const changeStorageSpy = jest.spyOn(storageService, 'updateUserStorageAndTier').mockResolvedValue(); @@ -113,7 +113,7 @@ describe('Process when a subscription is cancelled', () => { const randomError = new Error('Tier not found'); jest.spyOn(paymentService, 'getProduct').mockResolvedValue(mockedProduct as any); - jest.spyOn(stripePaymentsAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(stripeAdapter, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest.spyOn(usersService, 'findUserByCustomerID').mockResolvedValue(mockedUser); (handleCancelPlan as jest.Mock).mockRejectedValue(randomError); diff --git a/tests/src/webhooks/providers/bit2me/index.test.ts b/tests/src/webhooks/providers/bit2me/index.test.ts index 9f1380e0..39230966 100644 --- a/tests/src/webhooks/providers/bit2me/index.test.ts +++ b/tests/src/webhooks/providers/bit2me/index.test.ts @@ -7,7 +7,7 @@ import config from '../../../../../src/config'; import { PaymentService } from '../../../../../src/services/payment.service'; import Stripe from 'stripe'; import { InvoiceCompletedHandler } from '../../../../../src/webhooks/events/invoices/InvoiceCompletedHandler'; -import { StripePaymentsAdapter } from '../../../../../src/infrastructure/adapters/stripe.adapter'; +import { StripeAdapter } from '../../../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../../../src/infrastructure/domain/entities/customer'; let app: FastifyInstance; @@ -95,7 +95,7 @@ describe('Handling webhook for crypto payments', () => { status: 'pending', }); - const getCustomerSpy = jest.spyOn(StripePaymentsAdapter.prototype, 'getCustomer'); + const getCustomerSpy = jest.spyOn(StripeAdapter.prototype, 'getCustomer'); const response = await app.inject({ method: 'POST', @@ -126,7 +126,7 @@ describe('Handling webhook for crypto payments', () => { token: decodedToken, }); - jest.spyOn(StripePaymentsAdapter.prototype, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); + jest.spyOn(StripeAdapter.prototype, 'getCustomer').mockResolvedValue(Customer.toDomain(mockedCustomer)); jest .spyOn(PaymentService.prototype, 'getInvoice') .mockResolvedValue(mockedInvoice as Stripe.Response); diff --git a/tests/src/webhooks/webhook.test.ts b/tests/src/webhooks/webhook.test.ts index 0165a5e6..61139e1a 100644 --- a/tests/src/webhooks/webhook.test.ts +++ b/tests/src/webhooks/webhook.test.ts @@ -8,7 +8,7 @@ import { ObjectStorageService } from '../../../src/services/objectStorage.servic import { UsersService } from '../../../src/services/users.service'; import handleInvoicePaymentFailed from '../../../src/webhooks/handleInvoicePaymentFailed'; import { InvoiceCompletedHandler } from '../../../src/webhooks/events/invoices/InvoiceCompletedHandler'; -import { StripePaymentsAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; +import { StripeAdapter } from '../../../src/infrastructure/adapters/stripe.adapter'; import { Customer } from '../../../src/infrastructure/domain/entities/customer'; import { PaymentMethod } from '../../../src/infrastructure/domain/entities/paymentMethod'; @@ -115,11 +115,9 @@ describe('Webhook events', () => { const payloadToString = JSON.stringify(event); const getPaymentMethodSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'retrievePaymentMethod') + .spyOn(StripeAdapter.prototype, 'retrievePaymentMethod') .mockResolvedValue(mockedPaymentMethodToDomain); - const updateCustomerSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'updateCustomer') - .mockResolvedValue({} as any); + const updateCustomerSpy = jest.spyOn(StripeAdapter.prototype, 'updateCustomer').mockResolvedValue({} as any); const header = Stripe.webhooks.generateTestHeaderString({ payload: payloadToString, @@ -160,7 +158,7 @@ describe('Webhook events', () => { secret, }); const getCustomerSpy = jest - .spyOn(StripePaymentsAdapter.prototype, 'getCustomer') + .spyOn(StripeAdapter.prototype, 'getCustomer') .mockResolvedValueOnce(Customer.toDomain(mockedCustomer)); const invoiceCompletedHandlerRunSpy = jest .spyOn(InvoiceCompletedHandler.prototype, 'run')