diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 90a364248..959e8c633 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -49,6 +49,7 @@ import { PaypalModule } from '../paypal/paypal.module' import { ExportModule } from '../export/export.module' import { JwtModule } from '@nestjs/jwt' import { NotificationModule } from '../sockets/notifications/notification.module' +import { StripeModule } from '../stripe/stripe.module' @Module({ imports: [ @@ -96,6 +97,7 @@ import { NotificationModule } from '../sockets/notifications/notification.module ExportModule, JwtModule, NotificationModule, + StripeModule, ], controllers: [AppController], providers: [ diff --git a/apps/api/src/campaign/campaign.service.ts b/apps/api/src/campaign/campaign.service.ts index 71c5445b2..3f28e929c 100644 --- a/apps/api/src/campaign/campaign.service.ts +++ b/apps/api/src/campaign/campaign.service.ts @@ -1,18 +1,8 @@ -import { - Prisma, - Campaign, - CampaignState, - CampaignType, - Donation, - DonationStatus, - DonationType, - Vault, -} from '@prisma/client' +import { Prisma, Campaign, CampaignState, CampaignType, Donation, Vault } from '@prisma/client' import { forwardRef, Inject, Injectable, - InternalServerErrorException, Logger, NotAcceptableException, NotFoundException, @@ -21,7 +11,6 @@ import { import { PersonService } from '../person/person.service' import { PrismaService } from '../prisma/prisma.service' import { VaultService } from '../vault/vault.service' -import { shouldAllowStatusChange } from '../donations/helpers/donation-status-updates' import { PaymentData } from '../donations/helpers/payment-intent-helpers' import { CreateCampaignDto } from './dto/create-campaign.dto' import { UpdateCampaignDto } from './dto/update-campaign.dto' @@ -32,17 +21,11 @@ import { CampaignListItem, CampaignListItemSelect, } from './dto/list-campaigns.dto' -import { - NotificationService, - donationNotificationSelect, -} from '../sockets/notifications/notification.service' -import { DonationMetadata } from '../donations/dontation-metadata.interface' @Injectable() export class CampaignService { constructor( private prisma: PrismaService, - private notificationService: NotificationService, @Inject(forwardRef(() => VaultService)) private vaultService: VaultService, @Inject(forwardRef(() => PersonService)) private personService: PersonService, ) {} @@ -409,174 +392,6 @@ export class CampaignService { return this.prisma.donation.findFirst({ where: { extPaymentIntentId: paymentIntentId } }) } - /** - * Creates or Updates an incoming donation depending on the newDonationStatus attribute - * @param campaign - * @param paymentData - * @param newDonationStatus - * @param metadata - * @returns donation.id of the created/updated donation - */ - async updateDonationPayment( - campaign: Campaign, - paymentData: PaymentData, - newDonationStatus: DonationStatus, - metadata?: DonationMetadata, - ): Promise { - const campaignId = campaign.id - Logger.debug('Update donation to status: ' + newDonationStatus, { - campaignId, - paymentIntentId: paymentData.paymentIntentId, - }) - - /** - * Create or connect campaign vault - */ - const vault = await this.prisma.vault.findFirst({ where: { campaignId } }) - const targetVaultData = vault - ? // Connect the existing vault to this donation - { connect: { id: vault.id } } - : // Create new vault for the campaign - { create: { campaignId, currency: campaign.currency, name: campaign.title } } - - // Find donation by extPaymentIntentId and update if status allows - - let donation = await this.prisma.donation.findUnique({ - where: { extPaymentIntentId: paymentData.paymentIntentId }, - select: donationNotificationSelect, - }) - - // check for UUID length of personId - // subscriptions always have a personId - if (!donation && paymentData.personId && paymentData.personId.length === 36) { - // search for a subscription donation - // for subscriptions, we don't have a paymentIntentId - donation = await this.prisma.donation.findFirst({ - where: { - status: DonationStatus.initial, - personId: paymentData.personId, - chargedAmount: paymentData.chargedAmount, - extPaymentMethodId: 'subscription', - }, - select: donationNotificationSelect, - }) - - if (donation) { - donation.status = newDonationStatus - this.notificationService.sendNotification('successfulDonation', donation) - } - - Logger.debug('Donation found by subscription: ', donation) - } - - //if missing create the donation with the incoming status - if (!donation) { - Logger.debug( - 'No donation exists with extPaymentIntentId: ' + - paymentData.paymentIntentId + - ' Creating new donation with status: ' + - newDonationStatus, - ) - - try { - donation = await this.prisma.donation.create({ - data: { - amount: paymentData.netAmount, - chargedAmount: paymentData.chargedAmount, - currency: campaign.currency, - targetVault: targetVaultData, - provider: paymentData.paymentProvider, - type: DonationType.donation, - status: newDonationStatus, - extCustomerId: paymentData.stripeCustomerId ?? '', - extPaymentIntentId: paymentData.paymentIntentId, - extPaymentMethodId: paymentData.paymentMethodId ?? '', - billingName: paymentData.billingName, - billingEmail: paymentData.billingEmail, - person: paymentData.personId ? { connect: { id: paymentData.personId } } : {}, - }, - select: donationNotificationSelect, - }) - - this.notificationService.sendNotification('successfulDonation', donation) - } catch (error) { - Logger.error( - `Error while creating donation with paymentIntentId: ${paymentData.paymentIntentId} and status: ${newDonationStatus} . Error is: ${error}`, - ) - throw new InternalServerErrorException(error) - } - } - //donation exists, so check if it is safe to update it - else if (shouldAllowStatusChange(donation.status, newDonationStatus)) { - try { - const updatedDonation = await this.prisma.donation.update({ - where: { - id: donation.id, - }, - data: { - status: newDonationStatus, - amount: paymentData.netAmount, - extCustomerId: paymentData.stripeCustomerId, - extPaymentMethodId: paymentData.paymentMethodId, - extPaymentIntentId: paymentData.paymentIntentId, - billingName: paymentData.billingName, - billingEmail: paymentData.billingEmail, - }, - select: donationNotificationSelect, - }) - - this.notificationService.sendNotification('successfulDonation', { - ...updatedDonation, - person: donation.person, - }) - } catch (error) { - Logger.error( - `Error wile updating donation with paymentIntentId: ${paymentData.paymentIntentId} in database. Error is: ${error}`, - ) - throw new InternalServerErrorException(error) - } - } - //donation exists but we need to skip because previous status is from later event than the incoming - else { - Logger.warn( - `Skipping update of donation with paymentIntentId: ${paymentData.paymentIntentId} - and status: ${newDonationStatus} because the event comes after existing donation with status: ${donation.status}`, - ) - } - - //For successful donations we will also need to link them to user and add donation wish: - if (newDonationStatus === DonationStatus.succeeded) { - Logger.debug('metadata?.isAnonymous = ' + metadata?.isAnonymous) - - if (metadata?.isAnonymous != 'true') { - await this.prisma.donation.update({ - where: { id: donation.id }, - data: { - person: { - connect: { - email: paymentData.billingEmail, - }, - }, - }, - }) - } - } - - return donation.id - } - - async createDonationWish(wish: string, donationId: string, campaignId: string) { - const person = await this.prisma.donation.findUnique({ where: { id: donationId } }).person() - await this.prisma.donationWish.create({ - data: { - message: wish, - donationId, - campaignId, - personId: person?.id, - }, - }) - } - async donateToCampaign(campaign: Campaign, paymentData: PaymentData) { Logger.debug('Update amounts with successful donation', { campaignId: campaign.id, @@ -588,8 +403,6 @@ export class CampaignService { const vault = await this.getCampaignVault(campaign.id) if (vault) { await this.vaultService.incrementVaultAmount(vault.id, paymentData.netAmount) - } else { - //vault is already checked and created if not existing in updateDonationPayment() above } } diff --git a/apps/api/src/config/validation.config.ts b/apps/api/src/config/validation.config.ts index 7928c67cc..29100204e 100644 --- a/apps/api/src/config/validation.config.ts +++ b/apps/api/src/config/validation.config.ts @@ -11,7 +11,7 @@ const globalValidationPipe = new ValidationPipe({ excludeExtraneousValues: true, }, stopAtFirstError: false, - forbidUnknownValues: true, + forbidUnknownValues: false, disableErrorMessages: false, exceptionFactory: (errors) => new BadRequestException(errors), validationError: { target: false, value: false }, diff --git a/apps/api/src/domain/generated/donation/dto/create-donation.dto.ts b/apps/api/src/domain/generated/donation/dto/create-donation.dto.ts index e447208dc..c7354dfa7 100644 --- a/apps/api/src/domain/generated/donation/dto/create-donation.dto.ts +++ b/apps/api/src/domain/generated/donation/dto/create-donation.dto.ts @@ -4,7 +4,7 @@ import { ApiProperty } from '@nestjs/swagger' export class CreateDonationDto { @ApiProperty({ enum: DonationType }) type: DonationType - extCustomerId: string + extCustomerId?: string extPaymentIntentId: string extPaymentMethodId: string billingEmail?: string diff --git a/apps/api/src/domain/generated/donation/entities/donation.entity.ts b/apps/api/src/domain/generated/donation/entities/donation.entity.ts index c7db03740..1bf37d706 100644 --- a/apps/api/src/domain/generated/donation/entities/donation.entity.ts +++ b/apps/api/src/domain/generated/donation/entities/donation.entity.ts @@ -8,7 +8,7 @@ export class Donation { status: DonationStatus provider: PaymentProvider targetVaultId: string - extCustomerId: string + extCustomerId: string | null extPaymentIntentId: string extPaymentMethodId: string createdAt: Date diff --git a/apps/api/src/domain/generated/recurringDonation/dto/create-recurringDonation.dto.ts b/apps/api/src/domain/generated/recurringDonation/dto/create-recurringDonation.dto.ts index 38f5fb77e..56eec27e3 100644 --- a/apps/api/src/domain/generated/recurringDonation/dto/create-recurringDonation.dto.ts +++ b/apps/api/src/domain/generated/recurringDonation/dto/create-recurringDonation.dto.ts @@ -5,5 +5,5 @@ export class CreateRecurringDonationDto { @ApiProperty({ enum: RecurringDonationStatus }) status: RecurringDonationStatus extSubscriptionId: string - extCustomerId?: string + extCustomerId: string } diff --git a/apps/api/src/domain/generated/recurringDonation/entities/recurringDonation.entity.ts b/apps/api/src/domain/generated/recurringDonation/entities/recurringDonation.entity.ts index efe07a4cf..b72d3cac8 100644 --- a/apps/api/src/domain/generated/recurringDonation/entities/recurringDonation.entity.ts +++ b/apps/api/src/domain/generated/recurringDonation/entities/recurringDonation.entity.ts @@ -8,7 +8,7 @@ export class RecurringDonation { vaultId: string personId: string extSubscriptionId: string - extCustomerId: string | null + extCustomerId: string createdAt: Date updatedAt: Date | null amount: number diff --git a/apps/api/src/donations/donations.controller.spec.ts b/apps/api/src/donations/donations.controller.spec.ts index 586ac38de..e8fffceef 100644 --- a/apps/api/src/donations/donations.controller.spec.ts +++ b/apps/api/src/donations/donations.controller.spec.ts @@ -1,16 +1,7 @@ import { STRIPE_CLIENT_TOKEN } from '@golevelup/nestjs-stripe' -import { NotAcceptableException } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' -import { - Campaign, - CampaignState, - Currency, - DonationStatus, - DonationType, - PaymentProvider, - Prisma, -} from '@prisma/client' +import { Currency, DonationStatus, DonationType, PaymentProvider } from '@prisma/client' import { CampaignService } from '../campaign/campaign.service' import { ExportService } from '../export/export.service' import { PersonService } from '../person/person.service' @@ -19,7 +10,6 @@ import { NotificationModule } from '../sockets/notifications/notification.module import { VaultService } from '../vault/vault.service' import { DonationsController } from './donations.controller' import { DonationsService } from './donations.service' -import { CreateSessionDto } from './dto/create-session.dto' describe('DonationsController', () => { let controller: DonationsController @@ -27,15 +17,6 @@ describe('DonationsController', () => { checkout: { sessions: { create: jest.fn() } }, } stripeMock.checkout.sessions.create.mockResolvedValue({ payment_intent: 'unique-intent' }) - - const mockSession = { - mode: 'payment', - amount: 100, - campaignId: 'testCampaignId', - successUrl: 'http://test.com', - cancelUrl: 'http://test.com', - isAnonymous: true, - } as CreateSessionDto const vaultMock = { incrementVaultAmount: jest.fn(), } @@ -101,71 +82,6 @@ describe('DonationsController', () => { expect(controller).toBeDefined() }) - it('createCheckoutSession should create stripe session for active campaign', async () => { - prismaMock.campaign.findFirst.mockResolvedValue({ - allowDonationOnComplete: false, - state: CampaignState.active, - } as Campaign) - - await expect(controller.createCheckoutSession(mockSession)).resolves.toBeObject() - expect(prismaMock.campaign.findFirst).toHaveBeenCalled() - expect(stripeMock.checkout.sessions.create).toHaveBeenCalledWith({ - mode: mockSession.mode, - line_items: [ - { - price_data: { - currency: undefined, - product_data: { - name: undefined, - }, - unit_amount: 100, - }, - quantity: 1, - }, - ], - payment_method_types: ['card'], - payment_intent_data: { - metadata: { - campaignId: mockSession.campaignId, - isAnonymous: 'true', - personId: undefined, - wish: null, - }, - }, - subscription_data: undefined, - success_url: mockSession.successUrl, - cancel_url: mockSession.cancelUrl, - customer_email: undefined, - tax_id_collection: { - enabled: true, - }, - }) - }) - - it('createCheckoutSession should not create stripe session for completed campaign', async () => { - prismaMock.campaign.findFirst.mockResolvedValue({ - allowDonationOnComplete: false, - state: CampaignState.complete, - } as Campaign) - - await expect(controller.createCheckoutSession(mockSession)).rejects.toThrow( - new NotAcceptableException('Campaign cannot accept donations in state: complete'), - ) - expect(prismaMock.campaign.findFirst).toHaveBeenCalled() - expect(stripeMock.checkout.sessions.create).not.toHaveBeenCalled() - }) - - it('createCheckoutSession should create stripe session for completed campaign if allowed', async () => { - prismaMock.campaign.findFirst.mockResolvedValue({ - allowDonationOnComplete: true, - state: CampaignState.complete, - } as Campaign) - - await expect(controller.createCheckoutSession(mockSession)).resolves.toBeObject() - expect(prismaMock.campaign.findFirst).toHaveBeenCalled() - expect(stripeMock.checkout.sessions.create).toHaveBeenCalled() - }) - it('should update a donations donor, when it is changed', async () => { const updatePaymentDto = { type: DonationType.donation, diff --git a/apps/api/src/donations/donations.controller.ts b/apps/api/src/donations/donations.controller.ts index b11b935b9..4d6221122 100644 --- a/apps/api/src/donations/donations.controller.ts +++ b/apps/api/src/donations/donations.controller.ts @@ -8,7 +8,6 @@ import { Post, UnauthorizedException, Query, - Logger, Res, } from '@nestjs/common' import { ApiQuery, ApiTags } from '@nestjs/swagger' @@ -18,15 +17,10 @@ import { RealmViewSupporters, ViewSupporters } from '@podkrepi-bg/podkrepi-types import { isAdmin, KeycloakTokenParsed } from '../auth/keycloak' import { DonationsService } from './donations.service' -import { CreateSessionDto } from './dto/create-session.dto' import { CreatePaymentDto } from './dto/create-payment.dto' import { UpdatePaymentDto } from './dto/update-payment.dto' import { CreateBankPaymentDto } from './dto/create-bank-payment.dto' -import { UpdatePaymentIntentDto } from './dto/update-payment-intent.dto' -import { CreateStripePaymentDto } from './dto/create-stripe-payment.dto' -import { CreatePaymentIntentDto } from './dto/create-payment-intent.dto' import { DonationQueryDto } from '../common/dto/donation-query-dto' -import { CancelPaymentIntentDto } from './dto/cancel-payment-intent.dto' @ApiTags('donation') @Controller('donation') @@ -44,51 +38,6 @@ export class DonationsController { } } - @Post('create-checkout-session') - @Public() - async createCheckoutSession(@Body() sessionDto: CreateSessionDto) { - if ( - sessionDto.mode === 'subscription' && - (sessionDto.personId === null || sessionDto.personId.length === 0) - ) { - // in case of a intermediate (step 2) login, we might end up with no personId - // not able to fetch the current logged user here (due to @Public()) - sessionDto.personId = await this.donationsService.getUserId(sessionDto.personEmail) - } - - if ( - sessionDto.mode == 'subscription' && - (sessionDto.personId == null || sessionDto.personId.length == 0) - ) { - Logger.error( - `No personId found for email ${sessionDto.personEmail}. Unable to create a checkout session for a recurring donation`, - ) - throw new UnauthorizedException('You must be logged in to create a recurring donation') - } - - Logger.debug(`Creating checkout session with data ${JSON.stringify(sessionDto)}`) - - return this.donationsService.createCheckoutSession(sessionDto) - } - - @Get('prices') - @Public() - findPrices() { - return this.donationsService.listPrices() - } - - @Get('prices/single') - @Public() - findSinglePrices() { - return this.donationsService.listPrices('one_time') - } - - @Get('prices/recurring') - @Public() - findRecurringPrices() { - return this.donationsService.listPrices('recurring') - } - @Get('user-donations') async userDonations(@AuthenticatedUser() user: KeycloakTokenParsed) { return await this.donationsService.getDonationsByUser(user.sub) @@ -168,44 +117,6 @@ export class DonationsController { return this.donationsService.create(createPaymentDto, user) } - @Post('payment-intent') - @Public() - createPaymentIntent( - @Body() - createPaymentIntentDto: CreatePaymentIntentDto, - ) { - return this.donationsService.createPaymentIntent(createPaymentIntentDto) - } - - @Post('payment-intent/:id') - @Public() - updatePaymentIntent( - @Param('id') id: string, - @Body() - updatePaymentIntentDto: UpdatePaymentIntentDto, - ) { - return this.donationsService.updatePaymentIntent(id, updatePaymentIntentDto) - } - - @Post('payment-intent/:id/cancel') - @Public() - cancelPaymentIntent( - @Param('id') id: string, - @Body() - cancelPaymentIntentDto: CancelPaymentIntentDto, - ) { - return this.donationsService.cancelPaymentIntent(id, cancelPaymentIntentDto) - } - - @Post('create-stripe-payment') - @Public() - createStripePayment( - @Body() - stripePaymentDto: CreateStripePaymentDto, - ) { - return this.donationsService.createStripePayment(stripePaymentDto) - } - @Post('create-bank-payment') @Roles({ roles: [RealmViewSupporters.role, ViewSupporters.role], diff --git a/apps/api/src/donations/donations.module.ts b/apps/api/src/donations/donations.module.ts index 537c2bfca..2173211dc 100644 --- a/apps/api/src/donations/donations.module.ts +++ b/apps/api/src/donations/donations.module.ts @@ -42,5 +42,6 @@ import { NotificationModule } from '../sockets/notifications/notification.module PersonService, ExportService, ], + exports: [DonationsService], }) export class DonationsModule {} diff --git a/apps/api/src/donations/donations.service.ts b/apps/api/src/donations/donations.service.ts index 121864482..a8bf946f4 100644 --- a/apps/api/src/donations/donations.service.ts +++ b/apps/api/src/donations/donations.service.ts @@ -1,274 +1,172 @@ import Stripe from 'stripe' -import { ConfigService } from '@nestjs/config' import { InjectStripeClient } from '@golevelup/nestjs-stripe' -import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common' import { - Campaign, - Donation, - DonationStatus, - DonationType, - PaymentProvider, - Prisma, -} from '@prisma/client' + BadRequestException, + Injectable, + InternalServerErrorException, + Logger, + NotFoundException, +} from '@nestjs/common' +import { Campaign, Donation, DonationStatus, DonationType, Prisma } from '@prisma/client' import { Response } from 'express' import { getTemplateByTable } from '../export/helpers/exportableData' import { KeycloakTokenParsed } from '../auth/keycloak' -import { CampaignService } from '../campaign/campaign.service' import { PrismaService } from '../prisma/prisma.service' import { VaultService } from '../vault/vault.service' import { ExportService } from '../export/export.service' import { DonationMetadata } from './dontation-metadata.interface' import { CreateBankPaymentDto } from './dto/create-bank-payment.dto' import { CreatePaymentDto } from './dto/create-payment.dto' -import { CreateSessionDto } from './dto/create-session.dto' import { UpdatePaymentDto } from './dto/update-payment.dto' import { Person } from '../person/entities/person.entity' import { DonationBaseDto, ListDonationsDto } from './dto/list-donations.dto' import { donationWithPerson, DonationWithPerson } from './validators/donation.validator' -import { CreateStripePaymentDto } from './dto/create-stripe-payment.dto' import { ImportStatus } from '../bank-transactions-file/dto/bank-transactions-import-status.dto' +import { CreateSubscriptionPaymentDto } from './dto/create-subscription-payment.dto' +import { PaymentData } from './helpers/payment-intent-helpers' +import { + donationNotificationSelect, + NotificationService, +} from '../sockets/notifications/notification.service' @Injectable() export class DonationsService { constructor( @InjectStripeClient() private stripeClient: Stripe, - private config: ConfigService, - private campaignService: CampaignService, private prisma: PrismaService, private vaultService: VaultService, private exportService: ExportService, + private notificationService: NotificationService, ) {} - async listPrices(type?: Stripe.PriceListParams.Type, active?: boolean): Promise { - const listResponse = await this.stripeClient.prices.list({ active, type, limit: 100 }).then( - function (list) { - Logger.debug('[Stripe] Prices received: ' + list.data.length) - return { list } - }, - function (error) { - if (error instanceof Stripe.errors.StripeError) - Logger.error( - '[Stripe] Error while getting price list. Error type: ' + - error.type + - ' message: ' + - error.message + - ' full error: ' + - JSON.stringify(error), - ) - }, - ) - - if (listResponse) { - return listResponse.list.data.filter((price) => price.active) - } else return new Array() - } - /** - * Create initial donation object for tracking purposes + * Creates or Updates an incoming donation depending on the newDonationStatus attribute + * @param campaign + * @param paymentData + * @param metadata + * @returns donation.id of the created donation */ - async createInitialDonationFromSession( + async createDonation( campaign: Campaign, - sessionDto: CreateSessionDto, - paymentIntentId: string, - ) { - Logger.log('[ CreateInitialDonation ]', { - campaignId: campaign.id, - amount: sessionDto.amount, - paymentIntentId, + paymentData: PaymentData, + status: DonationStatus, + metadata?: DonationMetadata, + ): Promise { + const campaignId = campaign.id + Logger.debug(`Create donation with status ${status}`, { + campaignId, + paymentIntentId: paymentData.paymentIntentId, }) /** * Create or connect campaign vault */ - const vault = await this.prisma.vault.findFirst({ where: { campaignId: campaign.id } }) + const vault = await this.prisma.vault.findFirst({ where: { campaignId } }) const targetVaultData = vault ? // Connect the existing vault to this donation { connect: { id: vault.id } } : // Create new vault for the campaign - { create: { campaignId: campaign.id, currency: campaign.currency, name: campaign.title } } - - /** - * Here we cannot create initial donation anymore because stripe is not returning paymentIntendId in the CreateSessionDto - * It will be created in the paymentIntent.created webhook - */ - } + { create: { campaignId, currency: campaign.currency, name: campaign.title } } - /** - * Create initial donation object for tracking purposes - * This is used when the payment is created from the payment intent - */ - async createInitialDonationFromIntent( - campaign: Campaign, - stripePaymentDto: CreateStripePaymentDto, - paymentIntent: Stripe.PaymentIntent, - ): Promise { - Logger.debug('[ CreateInitialDonationFromIntent]', { - campaignId: campaign.id, - amount: paymentIntent.amount, - paymentIntentId: paymentIntent.id, - }) - - /** - * Create or connect campaign vault - */ - const vault = await this.prisma.vault.findFirst({ where: { campaignId: campaign.id } }) - const targetVaultData = vault - ? // Connect the existing vault to this donation - { connect: { id: vault.id } } - : // Create new vault for the campaign - { create: { campaignId: campaign.id, currency: campaign.currency, name: campaign.title } } - - /** - * Create or update initial donation object - */ - const donation = await this.prisma.donation.upsert({ - where: { extPaymentIntentId: paymentIntent.id }, - create: { - amount: 0, - chargedAmount: paymentIntent.amount, - currency: campaign.currency, - provider: PaymentProvider.stripe, - type: DonationType.donation, - status: DonationStatus.initial, - extCustomerId: stripePaymentDto.personEmail, - extPaymentIntentId: paymentIntent.id, - extPaymentMethodId: 'card', - billingEmail: stripePaymentDto.personEmail, - targetVault: targetVaultData, - }, - update: { - amount: 0, //this will be updated on successful payment event - chargedAmount: paymentIntent.amount, - currency: campaign.currency, - provider: PaymentProvider.stripe, - type: DonationType.donation, - status: DonationStatus.waiting, - extCustomerId: stripePaymentDto.personEmail, - extPaymentMethodId: 'card', - billingEmail: stripePaymentDto.personEmail, - targetVault: targetVaultData, - }, - }) - - if (!stripePaymentDto.isAnonymous) { - await this.prisma.donation.update({ - where: { id: donation.id }, + try { + const donation = await this.prisma.donation.create({ data: { - person: { - connectOrCreate: { - where: { - email: stripePaymentDto.personEmail, - }, - create: { - firstName: stripePaymentDto.firstName ?? '', - lastName: stripePaymentDto.lastName ?? '', - email: stripePaymentDto.personEmail, - phone: stripePaymentDto.phone, + amount: paymentData.netAmount, + chargedAmount: paymentData.chargedAmount, + currency: campaign.currency, + targetVault: targetVaultData, + provider: paymentData.paymentProvider, + type: DonationType.donation, + status: status, + extCustomerId: paymentData.stripeCustomerId ?? '', + extPaymentIntentId: paymentData.paymentIntentId, + extPaymentMethodId: paymentData.paymentMethodId ?? '', + billingName: paymentData.billingName, + billingEmail: paymentData.billingEmail, + }, + select: donationNotificationSelect, + }) + if (metadata?.isAnonymous !== 'true') { + await this.prisma.donation.update({ + where: { id: donation.id }, + data: { + person: { + connect: { + email: paymentData.billingEmail, }, }, }, - }, - }) - } - - return donation - } - - async createCheckoutSession( - sessionDto: CreateSessionDto, - ): Promise { - const campaign = await this.campaignService.validateCampaignId(sessionDto.campaignId) - const { mode } = sessionDto - const appUrl = this.config.get('APP_URL') - const metadata: DonationMetadata = { - campaignId: sessionDto.campaignId, - personId: sessionDto.personId, - isAnonymous: sessionDto.isAnonymous ? 'true' : 'false', - wish: sessionDto.message ?? null, - } - - const items = await this.prepareSessionItems(sessionDto, campaign) - const createSessionRequest: Stripe.Checkout.SessionCreateParams = { - mode, - customer_email: sessionDto.personEmail, - line_items: items, - payment_method_types: ['card'], - payment_intent_data: mode == 'payment' ? { metadata } : undefined, - subscription_data: mode == 'subscription' ? { metadata } : undefined, - success_url: sessionDto.successUrl ?? `${appUrl}/success`, - cancel_url: sessionDto.cancelUrl ?? `${appUrl}/canceled`, - tax_id_collection: { enabled: true }, - } - - Logger.debug('[ CreateCheckoutSession ]', createSessionRequest) - - const sessionResponse = await this.stripeClient.checkout.sessions - .create(createSessionRequest) - .then( - function (session) { - Logger.debug('[Stripe] Checkout session created.') - return { session } - }, - function (error) { - if (error instanceof Stripe.errors.StripeError) - Logger.error( - '[Stripe] Error while creating checkout session. Error type: ' + - error.type + - ' message: ' + - error.message + - ' full error: ' + - JSON.stringify(error), - ) - }, - ) - - if (sessionResponse) { - this.createInitialDonationFromSession( - campaign, - sessionDto, - (sessionResponse.session.payment_intent as string) ?? sessionResponse.session.id, + }) + } + this.notificationService.sendNotification('successfulDonation', donation) + return donation.id + } catch (error) { + Logger.error( + `Error while creating donation with paymentIntentId: ${paymentData.paymentIntentId} Error is: ${error}.`, ) + throw new InternalServerErrorException(error) } + } - return sessionResponse + async createDonationWish(wish: string, donationId: string, campaignId: string) { + const person = await this.prisma.donation.findUnique({ where: { id: donationId } }).person() + await this.prisma.donationWish.create({ + data: { + message: wish, + donationId, + campaignId, + personId: person?.id, + }, + }) } - private async prepareSessionItems( - sessionDto: CreateSessionDto, - campaign: Campaign, - ): Promise { - if (sessionDto.mode == 'subscription') { - //use an inline price for subscriptions - const stripeItem = { - price_data: { - currency: campaign.currency, - unit_amount: sessionDto.amount, - recurring: { - interval: 'month' as Stripe.Price.Recurring.Interval, - interval_count: 1, - }, - product_data: { - name: campaign.title, - }, - }, - quantity: 1, - } - return [stripeItem] + async createSubscriptionDonation( + user: KeycloakTokenParsed, + subscriptionPaymentDto: CreateSubscriptionPaymentDto, + ): Promise { + const customer = await this.stripeClient.customers.create({ + email: subscriptionPaymentDto.email, + metadata: { + keycloakId: user.sub, + }, + }) + const person = await this.prisma.person.findFirst({ + where: { keycloakId: user.sub }, + }) + if (!person) { + throw new NotFoundException('Person not found') } - // Create donation with custom amount - return [ - { - price_data: { - currency: campaign.currency, - unit_amount: sessionDto.amount, - product_data: { - name: campaign.title, + const product = await this.stripeClient.products.create({ + name: `Donation of ${subscriptionPaymentDto.amount}`, + description: `Donation of ${subscriptionPaymentDto.amount} to campaign ${subscriptionPaymentDto.campaignId} by person ${person.email}`, + }) + const subscription = await this.stripeClient.subscriptions.create({ + customer: customer.id, + items: [ + { + price_data: { + unit_amount: subscriptionPaymentDto.amount, + currency: subscriptionPaymentDto.currency, + product: product.id, + recurring: { interval: 'month' }, }, }, - quantity: 1, + ], + payment_behavior: 'default_incomplete', + payment_settings: { save_default_payment_method: 'on_subscription' }, + metadata: { + campaignId: subscriptionPaymentDto.campaignId, + personId: person.id, }, - ] + }) + const invoice = await this.stripeClient.invoices.retrieve( + subscription.latest_invoice as string, + { + expand: ['payment_intent'], + }, + ) + return invoice.payment_intent as Stripe.PaymentIntent } /** @@ -480,59 +378,6 @@ export class DonationsService { return donation } - /** - * Create a payment intent for a donation - * @param inputDto Payment intent create params - * @returns {Promise>} - */ - async createPaymentIntent( - inputDto: Stripe.PaymentIntentCreateParams, - ): Promise> { - return await this.stripeClient.paymentIntents.create(inputDto) - } - - /** - * Create a payment intent for a donation - * https://stripe.com/docs/api/payment_intents/create - * @param inputDto Payment intent create params - * @returns {Promise>} - */ - async createStripePayment(inputDto: CreateStripePaymentDto): Promise { - const intent = await this.stripeClient.paymentIntents.retrieve(inputDto.paymentIntentId) - if (!intent.metadata.campaignId) { - throw new BadRequestException('Campaign id is missing from payment intent metadata') - } - const campaignId = intent.metadata.camapaignId - const campaign = await this.campaignService.validateCampaignId(campaignId) - return this.createInitialDonationFromIntent(campaign, inputDto, intent) - } - - /** - * Update a payment intent for a donation - * https://stripe.com/docs/api/payment_intents/update - * @param inputDto Payment intent create params - * @returns {Promise>} - */ - async updatePaymentIntent( - id: string, - inputDto: Stripe.PaymentIntentUpdateParams, - ): Promise> { - return this.stripeClient.paymentIntents.update(id, inputDto) - } - - /** - * Cancel a payment intent for a donation - * https://stripe.com/docs/api/payment_intents/cancel - * @param inputDto Payment intent create params - * @returns {Promise>} - */ - async cancelPaymentIntent( - id: string, - inputDto: Stripe.PaymentIntentCancelParams, - ): Promise> { - return this.stripeClient.paymentIntents.cancel(id, inputDto) - } - async createUpdateBankPayment(donationDto: CreateBankPaymentDto): Promise { return await this.prisma.$transaction(async (tx) => { //to avoid incrementing vault amount twice we first check if there is such donation diff --git a/apps/api/src/donations/dto/create-stripe-payment.dto.ts b/apps/api/src/donations/dto/create-stripe-payment.dto.ts deleted file mode 100644 index 567111cf1..000000000 --- a/apps/api/src/donations/dto/create-stripe-payment.dto.ts +++ /dev/null @@ -1,41 +0,0 @@ -import Stripe from 'stripe' -import { Expose } from 'class-transformer' -import { ApiProperty } from '@nestjs/swagger' -import { IsBoolean, IsEmail, IsOptional, IsString } from 'class-validator' - -export class CreateStripePaymentDto { - @Expose() - @ApiProperty() - @IsString() - paymentIntentId: Stripe.PaymentIntent['id'] - - @Expose() - @ApiProperty() - @IsString() - @IsOptional() - firstName: string | null - - @Expose() - @ApiProperty() - @IsString() - @IsOptional() - lastName: string | null - - @Expose() - @ApiProperty() - @IsString() - @IsOptional() - phone: string | null - - @Expose() - @ApiProperty() - @IsString() - @IsOptional() - @IsEmail() - personEmail: string - - @Expose() - @ApiProperty() - @IsBoolean() - isAnonymous: boolean -} diff --git a/apps/api/src/donations/dto/create-subscription-payment.dto.ts b/apps/api/src/donations/dto/create-subscription-payment.dto.ts new file mode 100644 index 000000000..8997fcb49 --- /dev/null +++ b/apps/api/src/donations/dto/create-subscription-payment.dto.ts @@ -0,0 +1,28 @@ +import { Expose } from 'class-transformer' +import { ApiProperty } from '@nestjs/swagger' +import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator' +import { Currency } from '@prisma/client' + +export class CreateSubscriptionPaymentDto { + @Expose() + @ApiProperty() + @IsString() + @IsOptional() + campaignId: string + + @Expose() + @ApiProperty() + @IsNumber() + amount: number + + @ApiProperty() + @Expose() + @IsEnum(Currency) + currency: Currency + + @Expose() + @ApiProperty() + @IsString() + @IsOptional() + email: string +} diff --git a/apps/api/src/donations/events/stripe-payment.service.spec.ts b/apps/api/src/donations/events/stripe-payment.service.spec.ts index 75e219dc1..11d99c23f 100644 --- a/apps/api/src/donations/events/stripe-payment.service.spec.ts +++ b/apps/api/src/donations/events/stripe-payment.service.spec.ts @@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing' import { ConfigService } from '@nestjs/config' import { CampaignService } from '../../campaign/campaign.service' import { StripePaymentService } from './stripe-payment.service' -import { getPaymentData, getPaymentDataFromCharge } from '../helpers/payment-intent-helpers' +import { getPaymentDataFromCharge } from '../helpers/payment-intent-helpers' import Stripe from 'stripe' import { VaultService } from '../../vault/vault.service' import { PersonService } from '../../person/person.service' @@ -16,10 +16,6 @@ import { DonationType, RecurringDonationStatus } from '@prisma/client' import { campaignId, mockedCampaign, - mockPaymentEventCancelled, - mockPaymentEventCreated, - mockPaymentIntentCreated, - mockPaymentIntentBGIncluded, mockPaymentIntentBGIncludedNot, mockPaymentIntentUSIncluded, mockPaymentIntentUKIncluded, @@ -29,12 +25,15 @@ import { mockedCampaignCompeleted, mockedVault, mockChargeEventSucceeded, + mockPaymentIntentBGIncluded, } from './stripe-payment.testdata' import { DonationStatus } from '@prisma/client' import { RecurringDonationService } from '../../recurring-donation/recurring-donation.service' import { HttpService } from '@nestjs/axios' import { mockDeep } from 'jest-mock-extended' import { NotificationModule } from '../../sockets/notifications/notification.module' +import { DonationsService } from '../donations.service' +import { ExportModule } from '../../export/export.module' const defaultStripeWebhookEndpoint = '/stripe/webhook' const stripeSecret = 'wh_123' @@ -42,7 +41,6 @@ const stripeSecret = 'wh_123' describe('StripePaymentService', () => { let stripePaymentService: StripePaymentService let app: INestApplication - let hydratePayloadFn: jest.SpyInstance const stripe = new Stripe(stripeSecret, { apiVersion: '2022-11-15' }) const moduleConfig: StripeModuleConfig = { @@ -64,11 +62,13 @@ describe('StripePaymentService', () => { useFactory: () => moduleConfig, }), NotificationModule, + ExportModule, ], providers: [ ConfigService, StripePaymentService, CampaignService, + DonationsService, MockPrismaService, VaultService, PersonService, @@ -99,85 +99,6 @@ describe('StripePaymentService', () => { expect(stripePaymentService).toBeDefined() }) - it('should handle payment_intent.created', () => { - const payloadString = JSON.stringify(mockPaymentEventCreated, null, 2) - - const header = stripe.webhooks.generateTestHeaderString({ - payload: payloadString, - secret: stripeSecret, - }) - - const campaignService = app.get(CampaignService) - const mockedCampaignById = jest - .spyOn(campaignService, 'getCampaignById') - .mockImplementation(() => Promise.resolve(mockedCampaign)) - - const paymentData = getPaymentData(mockPaymentEventCreated.data.object as Stripe.PaymentIntent) - - const mockedupdateDonationPayment = jest - .spyOn(campaignService, 'updateDonationPayment') - .mockImplementation(() => Promise.resolve('')) - .mockName('updateDonationPayment') - - const mockedcreateDonationWish = jest - .spyOn(campaignService, 'createDonationWish') - .mockName('createDonationWish') - - return request(app.getHttpServer()) - .post(defaultStripeWebhookEndpoint) - .set('stripe-signature', header) - .type('json') - .send(payloadString) - .expect(201) - .then(() => { - expect(mockedCampaignById).toHaveBeenCalledWith(campaignId) //campaignId from the Stripe Event - expect(mockedcreateDonationWish).not.toHaveBeenCalled() - expect(mockedupdateDonationPayment).toHaveBeenCalledWith( - mockedCampaign, - paymentData, - DonationStatus.waiting, - ) - }) - }) - - it('should handle payment_intent.canceled', () => { - const payloadString = JSON.stringify(mockPaymentEventCancelled, null, 2) - - const header = stripe.webhooks.generateTestHeaderString({ - payload: payloadString, - secret: stripeSecret, - }) - - const campaignService = app.get(CampaignService) - const mockedCampaignById = jest - .spyOn(campaignService, 'getCampaignById') - .mockImplementation(() => Promise.resolve(mockedCampaign)) - - const paymentData = getPaymentData( - mockPaymentEventCancelled.data.object as Stripe.PaymentIntent, - ) - - const mockedupdateDonationPayment = jest - .spyOn(campaignService, 'updateDonationPayment') - .mockImplementation(() => Promise.resolve('')) - .mockName('updateDonationPayment') - - return request(app.getHttpServer()) - .post(defaultStripeWebhookEndpoint) - .set('stripe-signature', header) - .type('json') - .send(payloadString) - .expect(201) - .then(() => { - expect(mockedCampaignById).toHaveBeenCalledWith(campaignId) //campaignId from the Stripe Event - expect(mockedupdateDonationPayment).toHaveBeenCalledWith( - mockedCampaign, - paymentData, - DonationStatus.cancelled, - ) - }) - }) - it('should handle charge.succeeded for not anonymous user', () => { //Set not anonymous explicitly in the test data ;(mockChargeEventSucceeded.data.object as Stripe.Charge).metadata.isAnonymous = 'false' @@ -198,11 +119,6 @@ describe('StripePaymentService', () => { mockChargeEventSucceeded.data.object as Stripe.Charge, ) - const mockedcreateDonationWish = jest - .spyOn(campaignService, 'createDonationWish') - .mockName('createDonationWish') - .mockImplementation(() => Promise.resolve()) - prismaMock.donation.create.mockResolvedValue({ id: 'test-donation-id', type: DonationType.donation, @@ -247,7 +163,6 @@ describe('StripePaymentService', () => { }, }, }) - expect(mockedcreateDonationWish).toHaveBeenCalled() }) }) @@ -271,11 +186,6 @@ describe('StripePaymentService', () => { mockChargeEventSucceeded.data.object as Stripe.Charge, ) - const mockedcreateDonationWish = jest - .spyOn(campaignService, 'createDonationWish') - .mockName('createDonationWish') - .mockImplementation(() => Promise.resolve()) - prismaMock.donation.create.mockResolvedValue({ id: 'test-donation-id', type: DonationType.donation, @@ -311,25 +221,9 @@ describe('StripePaymentService', () => { // expect(mockedupdateDonationPayment).toHaveBeenCalled() expect(prismaMock.donation.create).toHaveBeenCalled() expect(prismaMock.donation.update).not.toHaveBeenCalled() - expect(mockedcreateDonationWish).toHaveBeenCalled() }) }) - it('calculate payment-intent.created', async () => { - const billingDetails = getPaymentData(mockPaymentIntentCreated) - expect(billingDetails.netAmount).toEqual(0) - expect(billingDetails.chargedAmount).toEqual(1065) - }) - - it('calculate payment-intent.succeeded with BG tax included in charge', async () => { - const billingDetails = getPaymentData( - mockPaymentIntentBGIncluded, - mockPaymentIntentBGIncluded.latest_charge as Stripe.Charge, - ) - expect(billingDetails.netAmount).toEqual(1000) - expect(billingDetails.chargedAmount).toEqual(1063) - }) - it('should handle customer.subscription.created', () => { const payloadString = JSON.stringify(mockCustomerSubscriptionCreated, null, 2) @@ -398,6 +292,7 @@ describe('StripePaymentService', () => { }) const campaignService = app.get(CampaignService) + const donationService = app.get(DonationsService) const mockedCampaignById = jest .spyOn(campaignService, 'getCampaignById') .mockImplementation(() => Promise.resolve(mockedCampaign)) @@ -407,9 +302,9 @@ describe('StripePaymentService', () => { .mockImplementation(() => Promise.resolve()) const mockedupdateDonationPayment = jest - .spyOn(campaignService, 'updateDonationPayment') + .spyOn(donationService, 'createDonation') .mockImplementation(() => Promise.resolve('')) - .mockName('updateDonationPayment') + .mockName('createDonation') return request(app.getHttpServer()) .post(defaultStripeWebhookEndpoint) @@ -436,6 +331,7 @@ describe('StripePaymentService', () => { }) const campaignService = app.get(CampaignService) + const donationService = app.get(DonationsService) const recurring = app.get(RecurringDonationService) const mockCancelSubscription = jest @@ -451,9 +347,9 @@ describe('StripePaymentService', () => { .mockImplementation(() => Promise.resolve()) const mockedupdateDonationPayment = jest - .spyOn(campaignService, 'updateDonationPayment') + .spyOn(donationService, 'createDonation') .mockImplementation(() => Promise.resolve('')) - .mockName('updateDonationPayment') + .mockName('createDonation') const mockFindAllRecurringDonations = jest .spyOn(recurring, 'findAllActiveRecurringDonationsByCampaignId') @@ -483,27 +379,32 @@ describe('StripePaymentService', () => { }) }) -it('calculate payment-intent.succeeded with BG tax not included in charge', async () => { - const billingDetails = getPaymentData( - mockPaymentIntentBGIncludedNot, +it('calculate payment-intent.succeeded with BG tax included in charge', async () => { + const billingDetails = getPaymentDataFromCharge( + mockPaymentIntentBGIncluded.latest_charge as Stripe.Charge, + ) + expect(billingDetails.netAmount).toEqual(1000) + expect(billingDetails.chargedAmount).toEqual(1063) +}) + +it('calculate charge.succeeded with BG tax not included in charge', async () => { + const billingDetails = getPaymentDataFromCharge( mockPaymentIntentBGIncludedNot.latest_charge as Stripe.Charge, ) expect(billingDetails.netAmount).toEqual(938) expect(billingDetails.chargedAmount).toEqual(1000) }) -it('calculate payment-intent.succeeded with US tax included in charge', async () => { - const billingDetails = getPaymentData( - mockPaymentIntentUSIncluded, +it('calculate charge.succeeded with US tax included in charge', async () => { + const billingDetails = getPaymentDataFromCharge( mockPaymentIntentUSIncluded.latest_charge as Stripe.Charge, ) expect(billingDetails.netAmount).toEqual(10000) expect(billingDetails.chargedAmount).toEqual(10350) }) -it('calculate payment-intent.succeeded with GB tax included in charge', async () => { - const billingDetails = getPaymentData( - mockPaymentIntentUKIncluded, +it('calculate charge.succeeded with GB tax included in charge', async () => { + const billingDetails = getPaymentDataFromCharge( mockPaymentIntentUKIncluded.latest_charge as Stripe.Charge, ) expect(billingDetails.netAmount).toEqual(50000) diff --git a/apps/api/src/donations/events/stripe-payment.service.ts b/apps/api/src/donations/events/stripe-payment.service.ts index 2cc50e6b2..df5ebe27c 100644 --- a/apps/api/src/donations/events/stripe-payment.service.ts +++ b/apps/api/src/donations/events/stripe-payment.service.ts @@ -8,13 +8,13 @@ import { RecurringDonationService } from '../../recurring-donation/recurring-don import { CreateRecurringDonationDto } from '../../recurring-donation/dto/create-recurring-donation.dto' import { - getPaymentData, string2Currency, string2RecurringDonationStatus, getInvoiceData, getPaymentDataFromCharge, } from '../helpers/payment-intent-helpers' -import { DonationStatus, CampaignState, Campaign } from '@prisma/client' +import { CampaignState, DonationStatus } from '@prisma/client' +import { DonationsService } from '../donations.service' /** Testing Stripe on localhost is described here: * https://github.com/podkrepi-bg/api/blob/master/TESTING.md#testing-stripe @@ -23,70 +23,10 @@ import { DonationStatus, CampaignState, Campaign } from '@prisma/client' export class StripePaymentService { constructor( private campaignService: CampaignService, + private donationService: DonationsService, private recurringDonationService: RecurringDonationService, ) {} - @StripeWebhookHandler('payment_intent.created') - async handlePaymentIntentCreated(event: Stripe.Event) { - const paymentIntent: Stripe.PaymentIntent = event.data.object as Stripe.PaymentIntent - - Logger.debug( - '[ handlePaymentIntentCreated ]', - paymentIntent, - paymentIntent.metadata as DonationMetadata, - ) - - const metadata: DonationMetadata = paymentIntent.metadata as DonationMetadata - - if (!metadata.campaignId) { - Logger.debug('[ handlePaymentIntentCreated ] No campaignId in metadata ' + paymentIntent.id) - return - } - - const campaign = await this.campaignService.getCampaignById(metadata.campaignId) - - if (campaign.currency !== paymentIntent.currency.toUpperCase()) { - throw new BadRequestException( - `Donation in different currency is not allowed. Campaign currency ${ - campaign.currency - } <> donation currency ${paymentIntent.currency.toUpperCase()}`, - ) - } - - const paymentData = getPaymentData(paymentIntent) - /* - * Handle the create event - */ - await this.campaignService.updateDonationPayment(campaign, paymentData, DonationStatus.waiting) - } - - @StripeWebhookHandler('payment_intent.canceled') - async handlePaymentIntentCancelled(event: Stripe.Event) { - const paymentIntent: Stripe.PaymentIntent = event.data.object as Stripe.PaymentIntent - Logger.log( - '[ handlePaymentIntentCancelled ]', - paymentIntent, - paymentIntent.metadata as DonationMetadata, - ) - - const metadata: DonationMetadata = paymentIntent.metadata as DonationMetadata - if (!metadata.campaignId) { - throw new BadRequestException( - 'Payment intent metadata does not contain target campaignId. Probably wrong session initiation. Payment intent is: ' + - paymentIntent.id, - ) - } - - const campaign = await this.campaignService.getCampaignById(metadata.campaignId) - - const billingData = getPaymentData(paymentIntent) - await this.campaignService.updateDonationPayment( - campaign, - billingData, - DonationStatus.cancelled, - ) - } - @StripeWebhookHandler('charge.succeeded') async handleChargeSucceeded(event: Stripe.Event) { const charge: Stripe.Charge = event.data.object as Stripe.Charge @@ -111,7 +51,7 @@ export class StripePaymentService { const billingData = getPaymentDataFromCharge(charge) - const donationId = await this.campaignService.updateDonationPayment( + await this.donationService.createDonation( campaign, billingData, DonationStatus.succeeded, @@ -119,9 +59,11 @@ export class StripePaymentService { ) await this.campaignService.donateToCampaign(campaign, billingData) await this.checkForCompletedCampaign(metadata.campaignId) - if (metadata?.wish) { - await this.campaignService.createDonationWish(metadata.wish, donationId, campaign.id) - } + } + + @StripeWebhookHandler('setup_intent.canceled') + async handlePaymentIntentCancelled() { + //TODO: handle cancelling of setup intent } @StripeWebhookHandler('customer.subscription.created') @@ -297,11 +239,7 @@ export class StripePaymentService { const paymentData = getInvoiceData(invoice) - await this.campaignService.updateDonationPayment( - campaign, - paymentData, - DonationStatus.succeeded, - ) + await this.donationService.createDonation(campaign, paymentData, DonationStatus.succeeded) await this.campaignService.donateToCampaign(campaign, paymentData) await this.checkForCompletedCampaign(metadata.campaignId) } diff --git a/apps/api/src/donations/helpers/payment-intent-helpers.ts b/apps/api/src/donations/helpers/payment-intent-helpers.ts index ae4518d0a..d5cb97875 100644 --- a/apps/api/src/donations/helpers/payment-intent-helpers.ts +++ b/apps/api/src/donations/helpers/payment-intent-helpers.ts @@ -3,20 +3,6 @@ import Stripe from 'stripe' import { getCountryRegion, stripeFeeCalculator } from './stripe-fee-calculator' import { RecurringDonationStatus, Currency } from '@prisma/client' -function getPaymentMethodId(paymentIntent: Stripe.PaymentIntent): string | undefined { - if (typeof paymentIntent.payment_method === 'string') { - return paymentIntent.payment_method - } - return paymentIntent.payment_method?.id ?? undefined -} - -function getPaymentCustomerId(paymentIntent: Stripe.PaymentIntent): string | undefined { - if (typeof paymentIntent.customer === 'string') { - return paymentIntent.customer - } - return paymentIntent.customer?.id ?? undefined -} - export type PaymentData = { paymentIntentId: string netAmount: number @@ -30,32 +16,6 @@ export type PaymentData = { personId?: string } -export function getPaymentData( - paymentIntent: Stripe.PaymentIntent, - charge?: Stripe.Charge, -): PaymentData { - return { - paymentProvider: PaymentProvider.stripe, - paymentIntentId: paymentIntent.id, - //netAmount is 0 until we receive a payment_intent.successful event where charges array contains the card country - netAmount: !paymentIntent.latest_charge - ? 0 - : Math.round( - paymentIntent.amount - - stripeFeeCalculator( - paymentIntent.amount, - getCountryRegion(charge?.payment_method_details?.card?.country as string), - ), - ), - chargedAmount: paymentIntent.amount, - currency: paymentIntent.currency, - billingName: charge?.billing_details?.name ?? undefined, - billingEmail: charge?.billing_details?.email ?? paymentIntent.receipt_email ?? undefined, - paymentMethodId: getPaymentMethodId(paymentIntent), - stripeCustomerId: getPaymentCustomerId(paymentIntent), - } -} - export function getPaymentDataFromCharge(charge: Stripe.Charge): PaymentData { return { paymentProvider: PaymentProvider.stripe, @@ -72,8 +32,8 @@ export function getPaymentDataFromCharge(charge: Stripe.Charge): PaymentData { currency: charge.currency, billingName: charge?.billing_details?.name ?? undefined, billingEmail: charge?.billing_details?.email ?? charge.receipt_email ?? undefined, - paymentMethodId: 'card', - stripeCustomerId: charge.billing_details?.email ?? undefined, + paymentMethodId: charge.payment_method ?? undefined, + stripeCustomerId: (charge.customer as string) ?? undefined, } } diff --git a/apps/api/src/paypal/paypal.controller.spec.ts b/apps/api/src/paypal/paypal.controller.spec.ts index 3574e2c91..434377657 100644 --- a/apps/api/src/paypal/paypal.controller.spec.ts +++ b/apps/api/src/paypal/paypal.controller.spec.ts @@ -1,20 +1,29 @@ import { Test, TestingModule } from '@nestjs/testing' import { PaypalController } from './paypal.controller' import { PaypalService } from './paypal.service' -import { PaypalModule } from './paypal.module' -import { CampaignModule } from '../campaign/campaign.module' import { ConfigModule } from '@nestjs/config' import { HttpModule } from '@nestjs/axios' -import { NotificationModule } from '../sockets/notifications/notification.module' +import { CampaignService } from '../campaign/campaign.service' +import { DonationsService } from '../donations/donations.service' describe('PaypalController', () => { let controller: PaypalController beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [PaypalModule, CampaignModule, ConfigModule, HttpModule, NotificationModule], + imports: [ConfigModule, HttpModule], controllers: [PaypalController], - providers: [PaypalService], + providers: [ + PaypalService, + { + provide: CampaignService, + useValue: {}, + }, + { + provide: DonationsService, + useValue: {}, + }, + ], }).compile() controller = module.get(PaypalController) diff --git a/apps/api/src/paypal/paypal.module.ts b/apps/api/src/paypal/paypal.module.ts index a48c04ad2..59e07e5bb 100644 --- a/apps/api/src/paypal/paypal.module.ts +++ b/apps/api/src/paypal/paypal.module.ts @@ -4,9 +4,10 @@ import { PaypalService } from './paypal.service' import { HttpModule } from '@nestjs/axios' import { CampaignModule } from '../campaign/campaign.module' import { ConfigService } from '@nestjs/config' +import { DonationsModule } from '../donations/donations.module' @Module({ - imports: [HttpModule, CampaignModule], + imports: [HttpModule, CampaignModule, DonationsModule], controllers: [PaypalController], providers: [PaypalService, ConfigService], exports: [PaypalService], diff --git a/apps/api/src/paypal/paypal.service.spec.ts b/apps/api/src/paypal/paypal.service.spec.ts index e2f3a2ff9..410821158 100644 --- a/apps/api/src/paypal/paypal.service.spec.ts +++ b/apps/api/src/paypal/paypal.service.spec.ts @@ -1,9 +1,8 @@ import { HttpModule } from '@nestjs/axios' import { ConfigModule } from '@nestjs/config' import { Test, TestingModule } from '@nestjs/testing' -import { CampaignModule } from '../campaign/campaign.module' -import { NotificationModule } from '../sockets/notifications/notification.module' -import { PaypalModule } from './paypal.module' +import { CampaignService } from '../campaign/campaign.service' +import { DonationsService } from '../donations/donations.service' import { PaypalService } from './paypal.service' describe('PaypalService', () => { @@ -11,8 +10,18 @@ describe('PaypalService', () => { beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - imports: [PaypalModule, CampaignModule, ConfigModule, HttpModule, NotificationModule], - providers: [PaypalService], + imports: [ConfigModule, HttpModule], + providers: [ + PaypalService, + { + provide: CampaignService, + useValue: {}, + }, + { + provide: DonationsService, + useValue: {}, + }, + ], }).compile() service = module.get(PaypalService) diff --git a/apps/api/src/paypal/paypal.service.ts b/apps/api/src/paypal/paypal.service.ts index 86bd28cfe..237686514 100644 --- a/apps/api/src/paypal/paypal.service.ts +++ b/apps/api/src/paypal/paypal.service.ts @@ -2,13 +2,14 @@ import { Injectable, Logger } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { CampaignService } from '../campaign/campaign.service' import { HttpService } from '@nestjs/axios' -import { AxiosResponse } from '@nestjs/terminus/dist/health-indicator/http/axios.interfaces' import { DonationStatus, PaymentProvider } from '@prisma/client' +import { DonationsService } from '../donations/donations.service' @Injectable() export class PaypalService { constructor( private campaignService: CampaignService, + private donationService: DonationsService, private config: ConfigService, private httpService: HttpService, ) {} @@ -27,11 +28,7 @@ export class PaypalService { // get campaign by id const campaign = await this.campaignService.getCampaignById(billingDetails.campaignId) - await this.campaignService.updateDonationPayment( - campaign, - billingDetails, - DonationStatus.waiting, - ) + await this.donationService.createDonation(campaign, billingDetails, DonationStatus.waiting) Logger.debug('Donation created!') } @@ -50,11 +47,7 @@ export class PaypalService { // get campaign by id const campaign = await this.campaignService.getCampaignById(billingDetails.campaignId) - await this.campaignService.updateDonationPayment( - campaign, - billingDetails, - DonationStatus.succeeded, - ) + await this.donationService.createDonation(campaign, billingDetails, DonationStatus.succeeded) Logger.debug('Donation completed!') @@ -72,12 +65,14 @@ export class PaypalService { headers: Record, rawPaypalBody: string, ): Promise { - const paypalVerificationUrl = new URL('/v1/notifications/verify-webhook-signature', - this.config.get('paypal.apiUrl')).toString() + const paypalVerificationUrl = new URL( + '/v1/notifications/verify-webhook-signature', + this.config.get('paypal.apiUrl'), + ).toString() const token = await this.generateAccessToken() - Logger.log("Token to use: " + token) + Logger.log('Token to use: ' + token) if (!token) return false @@ -99,25 +94,24 @@ export class PaypalService { Logger.log('Verification request will be: ' + verifyRequest) - const verificationResponse = - await this.httpService.axiosRef({ - url: paypalVerificationUrl, - method: 'post', - data: verifyRequest, - headers: { - Authorization: `Bearer ${token}`, - 'Content-Type': 'application/json', - }, - }) - .then(response => response.data) - .catch((e) => { - Logger.error(`Error while verifying webhook event. Error is: ${e.message}`, 'PaypalWebhook') - return null - }) - - if(!verificationResponse) - return false - + const verificationResponse = await this.httpService + .axiosRef({ + url: paypalVerificationUrl, + method: 'post', + data: verifyRequest, + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + }) + .then((response) => response.data) + .catch((e) => { + Logger.error(`Error while verifying webhook event. Error is: ${e.message}`, 'PaypalWebhook') + return null + }) + + if (!verificationResponse) return false + Logger.log('verification response: ', verificationResponse) Logger.log(`verification result: ${verificationResponse.verification_status}`, 'PaypalWebhook') @@ -130,8 +124,10 @@ export class PaypalService { 'PaypalWebhook', ) - const paypalTokenUrl = new URL('/v1/oauth2/token', - this.config.get('paypal.apiUrl')).toString() + const paypalTokenUrl = new URL( + '/v1/oauth2/token', + this.config.get('paypal.apiUrl'), + ).toString() Logger.log(`Generating token with apiUrl ${paypalTokenUrl}`, 'PaypalWebhook') return this.httpService diff --git a/apps/api/src/prisma/prisma-client.mock.ts b/apps/api/src/prisma/prisma-client.mock.ts index 0d867df72..2840d220e 100644 --- a/apps/api/src/prisma/prisma-client.mock.ts +++ b/apps/api/src/prisma/prisma-client.mock.ts @@ -1,6 +1,5 @@ import 'jest-extended' -import { mockDeep, mockReset } from 'jest-mock-extended' -import { DeepMockProxy } from 'jest-mock-extended/lib/mjs/Mock' +import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended' import prisma from './prisma-client' import { PrismaService } from './prisma.service' diff --git a/apps/api/src/donations/dto/cancel-payment-intent.dto.ts b/apps/api/src/stripe/dto/cancel-payment-intent.dto.ts similarity index 100% rename from apps/api/src/donations/dto/cancel-payment-intent.dto.ts rename to apps/api/src/stripe/dto/cancel-payment-intent.dto.ts diff --git a/apps/api/src/donations/dto/create-payment-intent.dto.ts b/apps/api/src/stripe/dto/create-payment-intent.dto.ts similarity index 85% rename from apps/api/src/donations/dto/create-payment-intent.dto.ts rename to apps/api/src/stripe/dto/create-payment-intent.dto.ts index 4dfe0fa0a..88beb8863 100644 --- a/apps/api/src/donations/dto/create-payment-intent.dto.ts +++ b/apps/api/src/stripe/dto/create-payment-intent.dto.ts @@ -2,7 +2,7 @@ import Stripe from 'stripe' import { ApiProperty } from '@nestjs/swagger' import { Currency } from '@prisma/client' import { Expose } from 'class-transformer' -import { IsNumber } from 'class-validator' +import { IsEnum, IsNumber } from 'class-validator' export class CreatePaymentIntentDto implements Stripe.PaymentIntentCreateParams { @ApiProperty() @@ -12,6 +12,7 @@ export class CreatePaymentIntentDto implements Stripe.PaymentIntentCreateParams @ApiProperty() @Expose() + @IsEnum(Currency) currency: Currency @ApiProperty() diff --git a/apps/api/src/stripe/dto/create-setup-intent.dto.ts b/apps/api/src/stripe/dto/create-setup-intent.dto.ts new file mode 100644 index 000000000..b9fcd8696 --- /dev/null +++ b/apps/api/src/stripe/dto/create-setup-intent.dto.ts @@ -0,0 +1,9 @@ +import Stripe from 'stripe' +import { ApiProperty } from '@nestjs/swagger' +import { Expose } from 'class-transformer' + +export class CreateSetupIntentDto implements Stripe.SetupIntentCreateParams { + @ApiProperty() + @Expose() + metadata: Stripe.MetadataParam +} diff --git a/apps/api/src/donations/dto/update-payment-intent.dto.ts b/apps/api/src/stripe/dto/update-payment-intent.dto.ts similarity index 100% rename from apps/api/src/donations/dto/update-payment-intent.dto.ts rename to apps/api/src/stripe/dto/update-payment-intent.dto.ts diff --git a/apps/api/src/stripe/dto/update-setup-intent.dto.ts b/apps/api/src/stripe/dto/update-setup-intent.dto.ts new file mode 100644 index 000000000..6b7a5545a --- /dev/null +++ b/apps/api/src/stripe/dto/update-setup-intent.dto.ts @@ -0,0 +1,9 @@ +import Stripe from 'stripe' +import { ApiProperty } from '@nestjs/swagger' +import { Expose } from 'class-transformer' + +export class UpdateSetupIntentDto implements Stripe.SetupIntentUpdateParams { + @ApiProperty() + @Expose() + metadata: Stripe.MetadataParam +} diff --git a/apps/api/src/stripe/stripe.controller.spec.ts b/apps/api/src/stripe/stripe.controller.spec.ts new file mode 100644 index 000000000..468da6714 --- /dev/null +++ b/apps/api/src/stripe/stripe.controller.spec.ts @@ -0,0 +1,38 @@ +import { StripeModule as ExtStripeModule, StripeModuleConfig } from '@golevelup/nestjs-stripe' +import { Test, TestingModule } from '@nestjs/testing' +import { StripeController } from './stripe.controller' +import { StripeService } from './stripe.service' + +describe('StripeController', () => { + let controller: StripeController + + beforeEach(async () => { + const stripeSecret = 'wh_123' + const moduleConfig: StripeModuleConfig = { + apiKey: stripeSecret, + webhookConfig: { + stripeSecrets: { + account: stripeSecret, + }, + loggingConfiguration: { + logMatchingEventHandlers: true, + }, + }, + } + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ExtStripeModule.forRootAsync(ExtStripeModule, { + useFactory: () => moduleConfig, + }), + ], + controllers: [StripeController], + providers: [StripeService], + }).compile() + + controller = module.get(StripeController) + }) + + it('should be defined', () => { + expect(controller).toBeDefined() + }) +}) diff --git a/apps/api/src/stripe/stripe.controller.ts b/apps/api/src/stripe/stripe.controller.ts new file mode 100644 index 000000000..04334d545 --- /dev/null +++ b/apps/api/src/stripe/stripe.controller.ts @@ -0,0 +1,83 @@ +import { Body, Controller, Get, Param, Post } from '@nestjs/common' +import { ApiTags } from '@nestjs/swagger' +import { Public } from 'nest-keycloak-connect' +import { CancelPaymentIntentDto } from './dto/cancel-payment-intent.dto' +import { CreatePaymentIntentDto } from './dto/create-payment-intent.dto' +import { UpdatePaymentIntentDto } from './dto/update-payment-intent.dto' +import { UpdateSetupIntentDto } from './dto/update-setup-intent.dto' +import { StripeService } from './stripe.service' + +@Controller('stripe') +@ApiTags('stripe') +export class StripeController { + constructor(private readonly stripeService: StripeService) {} + + @Post('setup-intent') + @Public() + createSetupIntent() { + return this.stripeService.createSetupIntent() + } + + @Post('setup-intent/:id') + @Public() + updateSetupIntent( + @Param('id') id: string, + @Body() + updateSetupIntentDto: UpdateSetupIntentDto, + ) { + return this.stripeService.updateSetupIntent(id, updateSetupIntentDto) + } + + @Post('setup-intent/:id/finalize') + @Public() + finalizeSetupIntent(@Param('id') id: string) { + return this.stripeService.finalizeSetupIntent(id) + } + + @Post('payment-intent') + @Public() + createPaymentIntent( + @Body() + createPaymentIntentDto: CreatePaymentIntentDto, + ) { + return this.stripeService.createPaymentIntent(createPaymentIntentDto) + } + + @Post('payment-intent/:id') + @Public() + updatePaymentIntent( + @Param('id') id: string, + @Body() + updatePaymentIntentDto: UpdatePaymentIntentDto, + ) { + return this.stripeService.updatePaymentIntent(id, updatePaymentIntentDto) + } + + @Post('payment-intent/:id/cancel') + @Public() + cancelPaymentIntent( + @Param('id') id: string, + @Body() + cancelPaymentIntentDto: CancelPaymentIntentDto, + ) { + return this.stripeService.cancelPaymentIntent(id, cancelPaymentIntentDto) + } + + @Get('prices') + @Public() + findPrices() { + return this.stripeService.listPrices() + } + + @Get('prices/single') + @Public() + findSinglePrices() { + return this.stripeService.listPrices('one_time') + } + + @Get('prices/recurring') + @Public() + findRecurringPrices() { + return this.stripeService.listPrices('recurring') + } +} diff --git a/apps/api/src/stripe/stripe.module.ts b/apps/api/src/stripe/stripe.module.ts new file mode 100644 index 000000000..c2b65165d --- /dev/null +++ b/apps/api/src/stripe/stripe.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common' +import { StripeModule as StripeClientModule } from '@golevelup/nestjs-stripe' +import { StripeService } from './stripe.service' +import { StripeController } from './stripe.controller' +import { ConfigService } from '@nestjs/config' +import { StripeConfigFactory } from '../donations/helpers/stripe-config-factory' + +@Module({ + imports: [ + StripeClientModule.forRootAsync(StripeClientModule, { + inject: [ConfigService], + useFactory: StripeConfigFactory.useFactory, + }), + ], + providers: [StripeService], + controllers: [StripeController], +}) +export class StripeModule {} diff --git a/apps/api/src/stripe/stripe.service.spec.ts b/apps/api/src/stripe/stripe.service.spec.ts new file mode 100644 index 000000000..2300661e1 --- /dev/null +++ b/apps/api/src/stripe/stripe.service.spec.ts @@ -0,0 +1,36 @@ +import { StripeModule as ExtStripeModule, StripeModuleConfig } from '@golevelup/nestjs-stripe' +import { Test, TestingModule } from '@nestjs/testing' +import { StripeService } from './stripe.service' + +describe('StripeService', () => { + let service: StripeService + + beforeEach(async () => { + const stripeSecret = 'wh_123' + const moduleConfig: StripeModuleConfig = { + apiKey: stripeSecret, + webhookConfig: { + stripeSecrets: { + account: stripeSecret, + }, + loggingConfiguration: { + logMatchingEventHandlers: true, + }, + }, + } + const module: TestingModule = await Test.createTestingModule({ + imports: [ + ExtStripeModule.forRootAsync(ExtStripeModule, { + useFactory: () => moduleConfig, + }), + ], + providers: [StripeService], + }).compile() + + service = module.get(StripeService) + }) + + it('should be defined', () => { + expect(service).toBeDefined() + }) +}) diff --git a/apps/api/src/stripe/stripe.service.ts b/apps/api/src/stripe/stripe.service.ts new file mode 100644 index 000000000..8fd9711b8 --- /dev/null +++ b/apps/api/src/stripe/stripe.service.ts @@ -0,0 +1,138 @@ +import { InjectStripeClient } from '@golevelup/nestjs-stripe' +import { BadRequestException, Injectable, Logger } from '@nestjs/common' +import Stripe from 'stripe' +import { UpdateSetupIntentDto } from './dto/update-setup-intent.dto' + +@Injectable() +export class StripeService { + constructor(@InjectStripeClient() private stripeClient: Stripe) {} + + /** + * Update a setup intent for a donation + * @param inputDto Payment intent update params + * @returns {Promise>} + */ + async updateSetupIntent( + id: string, + inputDto: UpdateSetupIntentDto, + ): Promise> { + return await this.stripeClient.setupIntents.update(id, inputDto) + } + /** + * Create a payment intent for a donation + * https://stripe.com/docs/api/payment_intents/create + * @param inputDto Payment intent create params + * @returns {Promise>} + */ + async finalizeSetupIntent(setupIntentId: string): Promise { + const setupIntent = await this.stripeClient.setupIntents.retrieve(setupIntentId, { + expand: ['payment_method'], + }) + if (!setupIntent.payment_method || typeof setupIntent.payment_method === 'string') { + throw new BadRequestException('Payment method is missing from setup intent') + } + const paymentMethod = setupIntent.payment_method + if (!paymentMethod?.billing_details?.email) { + throw new BadRequestException('Email is required from the payment method') + } + if (!setupIntent.metadata || !setupIntent.metadata.amount || !setupIntent.metadata.currency) { + throw new BadRequestException('Amount and currency are required from the setup intent') + } + const email = paymentMethod.billing_details.email + + let customer = await this.stripeClient.customers + .list({ + email, + }) + .then((res) => res.data.at(0)) + if (!customer) { + customer = await this.stripeClient.customers.create({ + email, + payment_method: paymentMethod.id, + }) + } + this.stripeClient.paymentMethods.attach(paymentMethod.id, { + customer: customer.id, + }) + const paymentIntent = await this.stripeClient.paymentIntents.create({ + amount: Math.round(Number(setupIntent.metadata.amount)), + currency: setupIntent.metadata.currency, + customer: customer.id, + payment_method: setupIntent.payment_method.id, + confirm: true, + metadata: { + ...setupIntent.metadata, + }, + }) + return paymentIntent + } + /** + * Create a setup intent for a donation + * @param inputDto Payment intent create params + * @returns {Promise>} + */ + async createSetupIntent(): Promise> { + return await this.stripeClient.setupIntents.create() + } + + /** + * Create a payment intent for a donation + * @param inputDto Payment intent create params + * @returns {Promise>} + */ + async createPaymentIntent( + inputDto: Stripe.PaymentIntentCreateParams, + ): Promise> { + return await this.stripeClient.paymentIntents.create(inputDto) + } + + /** + * Update a payment intent for a donation + * https://stripe.com/docs/api/payment_intents/update + * @param inputDto Payment intent create params + * @returns {Promise>} + */ + async updatePaymentIntent( + id: string, + inputDto: Stripe.PaymentIntentUpdateParams, + ): Promise> { + return this.stripeClient.paymentIntents.update(id, inputDto) + } + + /** + * Cancel a payment intent for a donation + * https://stripe.com/docs/api/payment_intents/cancel + * @param inputDto Payment intent create params + * @returns {Promise>} + */ + async cancelPaymentIntent( + id: string, + inputDto: Stripe.PaymentIntentCancelParams, + ): Promise> { + return this.stripeClient.paymentIntents.cancel(id, inputDto) + } + + async listPrices(type?: Stripe.PriceListParams.Type, active?: boolean): Promise { + const listResponse = await this.stripeClient.prices.list({ active, type, limit: 100 }).then( + function (list) { + Logger.debug('[Stripe] Prices received: ' + list.data.length) + return { list } + }, + function (error) { + if (error instanceof Stripe.errors.StripeError) + Logger.error( + '[Stripe] Error while getting price list. Error type: ' + + error.type + + ' message: ' + + error.message + + ' full error: ' + + JSON.stringify(error), + ) + }, + ) + + if (listResponse) { + return listResponse.list.data.filter((price) => price.active) + } else return new Array() + } +} diff --git a/migrations/20230316135108_optional_customer_id/migration.sql b/migrations/20230316135108_optional_customer_id/migration.sql new file mode 100644 index 000000000..24dcee0e6 --- /dev/null +++ b/migrations/20230316135108_optional_customer_id/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "donations" ALTER COLUMN "ext_customer_id" DROP NOT NULL; diff --git a/migrations/20230321100658_customer_id_requirement/migration.sql b/migrations/20230321100658_customer_id_requirement/migration.sql new file mode 100644 index 000000000..aeb603403 --- /dev/null +++ b/migrations/20230321100658_customer_id_requirement/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Made the column `ext_customer_id` on table `recurring_donations` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "recurring_donations" ALTER COLUMN "ext_customer_id" SET NOT NULL; diff --git a/podkrepi.dbml b/podkrepi.dbml index 4f8ce70fa..7231c0c11 100644 --- a/podkrepi.dbml +++ b/podkrepi.dbml @@ -299,7 +299,7 @@ Table donations { status DonationStatus [not null, default: 'initial'] provider PaymentProvider [not null, default: 'none'] targetVaultId String [not null, note: 'Vault where the funds are going'] - extCustomerId String [not null, note: 'Payment provider attributes'] + extCustomerId String [note: 'Payment provider attributes'] extPaymentIntentId String [unique, not null] extPaymentMethodId String [not null] createdAt DateTime [default: `now()`, not null] @@ -332,7 +332,7 @@ Table recurring_donations { vaultId String [not null] personId String [not null] extSubscriptionId String [not null, note: 'Payment provider Subscription id'] - extCustomerId String + extCustomerId String [not null] createdAt DateTime [default: `now()`, not null] updatedAt DateTime amount Int [not null, default: 0] diff --git a/schema.prisma b/schema.prisma index 9f772b994..179dd810a 100644 --- a/schema.prisma +++ b/schema.prisma @@ -215,12 +215,12 @@ model Campaign { /// Keeps track of previous slugs that are not used currently in any active campaign model SlugArchive { - slug String @id @unique @db.VarChar(250) - /// Stores the id of the last campaign that has used it - campaignId String @map("campaign_id") @db.Uuid - campaign Campaign @relation(fields: [campaignId], references: [id]) + slug String @id @unique @db.VarChar(250) + /// Stores the id of the last campaign that has used it + campaignId String @map("campaign_id") @db.Uuid + campaign Campaign @relation(fields: [campaignId], references: [id]) -@@map("slug_archive") + @@map("slug_archive") } model Irregularity { @@ -361,7 +361,7 @@ model Donation { /// Vault where the funds are going targetVaultId String @map("target_vault_id") @db.Uuid /// Payment provider attributes - extCustomerId String @map("ext_customer_id") @db.VarChar(50) + extCustomerId String? @map("ext_customer_id") @db.VarChar(50) extPaymentIntentId String @unique @map("ext_payment_intent_id") extPaymentMethodId String @map("ext_payment_method_id") /// @@ -401,7 +401,7 @@ model RecurringDonation { personId String @map("person_id") @db.Uuid /// Payment provider Subscription id extSubscriptionId String @map("ext_subscription_id") @db.VarChar(50) - extCustomerId String? @map("ext_customer_id") @db.VarChar(50) + extCustomerId String @map("ext_customer_id") @db.VarChar(50) /// createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) updatedAt DateTime? @updatedAt @map("updated_at") @db.Timestamptz(6) diff --git a/yarn.lock b/yarn.lock index c9ca8f373..89aec3243 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,9 +33,9 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/core@npm:15.1.4": - version: 15.1.4 - resolution: "@angular-devkit/core@npm:15.1.4" +"@angular-devkit/core@npm:15.2.4": + version: 15.2.4 + resolution: "@angular-devkit/core@npm:15.2.4" dependencies: ajv: 8.12.0 ajv-formats: 2.1.1 @@ -47,23 +47,23 @@ __metadata: peerDependenciesMeta: chokidar: optional: true - checksum: df5ee37e998b302b0a65be1363ff5eb2510e2582223325e42098b77d6ff76c67408c822147c2b5cfc946f15cd6a50486b8fbb3797294f3c5577deef397854914 + checksum: f0ce18f6f916ef49f57eafa8e5884e683dda4478487329fc2d8bdff7ad0d705e842de2cdcf45048d049b4792155bfec8e180081e75d9f04519b0a10dc7752f3a languageName: node linkType: hard -"@angular-devkit/schematics-cli@npm:15.1.4": - version: 15.1.4 - resolution: "@angular-devkit/schematics-cli@npm:15.1.4" +"@angular-devkit/schematics-cli@npm:15.2.4": + version: 15.2.4 + resolution: "@angular-devkit/schematics-cli@npm:15.2.4" dependencies: - "@angular-devkit/core": 15.1.4 - "@angular-devkit/schematics": 15.1.4 + "@angular-devkit/core": 15.2.4 + "@angular-devkit/schematics": 15.2.4 ansi-colors: 4.1.3 inquirer: 8.2.4 symbol-observable: 4.0.0 yargs-parser: 21.1.1 bin: schematics: bin/schematics.js - checksum: d6805ebdd36c274882b12f7ee90eab1db2e90f211865f2d745779b8e5c94bad2b2dad57ccd019507b048348070f7d8779913ee7405c2a9b39cd17617329f7268 + checksum: c30e6557f04dddd998b6cfd95ee462c0bba369e6eda36ea22e8f919bd265d38fed575590802becb3e58f4b629f5d7b1aaaefa7b59cdecc2aa248bed3a2a1e607 languageName: node linkType: hard @@ -80,16 +80,16 @@ __metadata: languageName: node linkType: hard -"@angular-devkit/schematics@npm:15.1.4": - version: 15.1.4 - resolution: "@angular-devkit/schematics@npm:15.1.4" +"@angular-devkit/schematics@npm:15.2.4": + version: 15.2.4 + resolution: "@angular-devkit/schematics@npm:15.2.4" dependencies: - "@angular-devkit/core": 15.1.4 + "@angular-devkit/core": 15.2.4 jsonc-parser: 3.2.0 - magic-string: 0.27.0 + magic-string: 0.29.0 ora: 5.4.1 rxjs: 6.6.7 - checksum: a38065afa60cd6e259799ede5df518fac330aa082a7b1fff563bfc19ccb707d33c5f58c6d8c7f537048740949932769c992458564a19405acedb669e8eed823b + checksum: fabbaefffd1640b051872b22c928c6feef0a36e1e5fdefcb26640cf74bac27abba57c7090e9906d4ff9b81b748688aad87581a38138556bc6b207f3dc0b3409d languageName: node linkType: hard @@ -110,37 +110,37 @@ __metadata: linkType: hard "@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.15.0": - version: 7.21.0 - resolution: "@babel/core@npm:7.21.0" + version: 7.21.3 + resolution: "@babel/core@npm:7.21.3" dependencies: "@ampproject/remapping": ^2.2.0 "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.21.0 + "@babel/generator": ^7.21.3 "@babel/helper-compilation-targets": ^7.20.7 - "@babel/helper-module-transforms": ^7.21.0 + "@babel/helper-module-transforms": ^7.21.2 "@babel/helpers": ^7.21.0 - "@babel/parser": ^7.21.0 + "@babel/parser": ^7.21.3 "@babel/template": ^7.20.7 - "@babel/traverse": ^7.21.0 - "@babel/types": ^7.21.0 + "@babel/traverse": ^7.21.3 + "@babel/types": ^7.21.3 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.2 semver: ^6.3.0 - checksum: 357f4dd3638861ceebf6d95ff49ad8b902065ee8b7b352621deed5666c2a6d702a48ca7254dba23ecae2a0afb67d20f90db7dd645c3b75e35e72ad9776c671aa + checksum: bef25fbea96f461bf79bd1d0e4f0cdce679fd5ada464a89c1141ddba59ae1adfdbb23e04440c266ed525712d33d5ffd818cd8b0c25b1dee0e648d5559516153a languageName: node linkType: hard -"@babel/generator@npm:^7.21.0, @babel/generator@npm:^7.21.1, @babel/generator@npm:^7.7.2": - version: 7.21.1 - resolution: "@babel/generator@npm:7.21.1" +"@babel/generator@npm:^7.21.3, @babel/generator@npm:^7.7.2": + version: 7.21.3 + resolution: "@babel/generator@npm:7.21.3" dependencies: - "@babel/types": ^7.21.0 + "@babel/types": ^7.21.3 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 69085a211ff91a7a608ee3f86e6fcb9cf5e724b756d792a713b0c328a671cd3e423e1ef1b12533f366baba0616caffe0a7ba9d328727eab484de5961badbef00 + checksum: be6bb5a32a0273260b91210d4137b7b5da148a2db8dd324654275cb0af865ae59de5e1536e93ac83423b2586415059e1c24cf94293026755cf995757238da749 languageName: node linkType: hard @@ -277,7 +277,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.0, @babel/helper-module-transforms@npm:^7.21.2": +"@babel/helper-module-transforms@npm:^7.18.6, @babel/helper-module-transforms@npm:^7.20.11, @babel/helper-module-transforms@npm:^7.21.2": version: 7.21.2 resolution: "@babel/helper-module-transforms@npm:7.21.2" dependencies: @@ -419,12 +419,12 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.0, @babel/parser@npm:^7.21.2": - version: 7.21.2 - resolution: "@babel/parser@npm:7.21.2" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.21.3": + version: 7.21.3 + resolution: "@babel/parser@npm:7.21.3" bin: parser: ./bin/babel-parser.js - checksum: e2b89de2c63d4cdd2cafeaea34f389bba729727eec7a8728f736bc472a59396059e3e9fe322c9bed8fd126d201fb609712949dc8783f4cae4806acd9a73da6ff + checksum: a71e6456a1260c2a943736b56cc0acdf5f2a53c6c79e545f56618967e51f9b710d1d3359264e7c979313a7153741b1d95ad8860834cc2ab4ce4f428b13cc07be languageName: node linkType: hard @@ -954,13 +954,13 @@ __metadata: linkType: hard "@babel/plugin-transform-destructuring@npm:^7.20.2": - version: 7.20.7 - resolution: "@babel/plugin-transform-destructuring@npm:7.20.7" + version: 7.21.3 + resolution: "@babel/plugin-transform-destructuring@npm:7.21.3" dependencies: "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: bd8affdb142c77662037215e37128b2110a786c92a67e1f00b38223c438c1610bd84cbc0386e9cd3479245ea811c5ca6c9838f49be4729b592159a30ce79add2 + checksum: 43ebbe0bfa20287e34427be7c2200ce096c20913775ea75268fb47fe0e55f9510800587e6052c42fe6dffa0daaad95dd465c3e312fd1ef9785648384c45417ac languageName: node linkType: hard @@ -1132,13 +1132,13 @@ __metadata: linkType: hard "@babel/plugin-transform-parameters@npm:^7.20.1, @babel/plugin-transform-parameters@npm:^7.20.7": - version: 7.20.7 - resolution: "@babel/plugin-transform-parameters@npm:7.20.7" + version: 7.21.3 + resolution: "@babel/plugin-transform-parameters@npm:7.21.3" dependencies: "@babel/helper-plugin-utils": ^7.20.2 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 6ffe0dd9afb2d2b9bc247381aa2e95dd9997ff5568a0a11900528919a4e073ac68f46409431455badb8809644d47cff180045bc2b9700e3f36e3b23554978947 + checksum: c92128d7b1fcf54e2cab186c196bbbf55a9a6de11a83328dc2602649c9dc6d16ef73712beecd776cd49bfdc624b5f56740f4a53568d3deb9505ec666bc869da3 languageName: node linkType: hard @@ -1249,15 +1249,16 @@ __metadata: linkType: hard "@babel/plugin-transform-typescript@npm:^7.21.0": - version: 7.21.0 - resolution: "@babel/plugin-transform-typescript@npm:7.21.0" + version: 7.21.3 + resolution: "@babel/plugin-transform-typescript@npm:7.21.3" dependencies: + "@babel/helper-annotate-as-pure": ^7.18.6 "@babel/helper-create-class-features-plugin": ^7.21.0 "@babel/helper-plugin-utils": ^7.20.2 "@babel/plugin-syntax-typescript": ^7.20.0 peerDependencies: "@babel/core": ^7.0.0-0 - checksum: 091931118eb515738a4bc8245875f985fc9759d3f85cdf08ee641779b41520241b369404e2bb86fc81907ad827678fdb704e8e5a995352def5dd3051ea2cd870 + checksum: c16fd577bf43f633deb76fca2a8527d8ae25968c8efdf327c1955472c3e0257e62992473d1ad7f9ee95379ce2404699af405ea03346055adadd3478ad0ecd117 languageName: node linkType: hard @@ -1424,32 +1425,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.7.2": - version: 7.21.2 - resolution: "@babel/traverse@npm:7.21.2" +"@babel/traverse@npm:^7.16.0, @babel/traverse@npm:^7.20.5, @babel/traverse@npm:^7.20.7, @babel/traverse@npm:^7.21.0, @babel/traverse@npm:^7.21.2, @babel/traverse@npm:^7.21.3, @babel/traverse@npm:^7.7.2": + version: 7.21.3 + resolution: "@babel/traverse@npm:7.21.3" dependencies: "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.21.1 + "@babel/generator": ^7.21.3 "@babel/helper-environment-visitor": ^7.18.9 "@babel/helper-function-name": ^7.21.0 "@babel/helper-hoist-variables": ^7.18.6 "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.21.2 - "@babel/types": ^7.21.2 + "@babel/parser": ^7.21.3 + "@babel/types": ^7.21.3 debug: ^4.1.0 globals: ^11.1.0 - checksum: d851e3f5cfbdc2fac037a014eae7b0707709de50f7d2fbb82ffbf932d3eeba90a77431529371d6e544f8faaf8c6540eeb18fdd8d1c6fa2b61acea0fb47e18d4b + checksum: 0af5bcd47a2fc501592b90ac1feae9d449afb9ab0772a4f6e68230f4cd3a475795d538c1de3f880fe3414b6c2820bac84d02c6549eea796f39d74a603717447b languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.21.2 - resolution: "@babel/types@npm:7.21.2" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.6, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.2, @babel/types@npm:^7.20.5, @babel/types@npm:^7.20.7, @babel/types@npm:^7.21.0, @babel/types@npm:^7.21.2, @babel/types@npm:^7.21.3, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.21.3 + resolution: "@babel/types@npm:7.21.3" dependencies: "@babel/helper-string-parser": ^7.19.4 "@babel/helper-validator-identifier": ^7.19.1 to-fast-properties: ^2.0.0 - checksum: a45a52acde139e575502c6de42c994bdbe262bafcb92ae9381fb54cdf1a3672149086843fda655c7683ce9806e998fd002bbe878fa44984498d0fdc7935ce7ff + checksum: b750274718ba9cefd0b81836c464009bb6ba339fccce51b9baff497a0a2d96c044c61dc90cf203cec0adc770454b53a9681c3f7716883c802b85ab84c365ba35 languageName: node linkType: hard @@ -1476,6 +1477,24 @@ __metadata: languageName: node linkType: hard +"@eslint-community/eslint-utils@npm:^4.2.0": + version: 4.3.0 + resolution: "@eslint-community/eslint-utils@npm:4.3.0" + dependencies: + eslint-visitor-keys: ^3.3.0 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: f487760a692f0f1fef76e248ad72976919576ba57edc2b1b1dc1d182553bae6b5bf7b078e654da85d04f0af8a485d20bd26280002768f4fbcd2e330078340cb0 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.4.0": + version: 4.4.1 + resolution: "@eslint-community/regexpp@npm:4.4.1" + checksum: db97d8d08e784147b55ab0dda5892503c1a0eebad94d1c4646d89a94f02ca70b25f05d8e021fc05a075e7eb312e03e21f63d84f0b327719f8cf3bb64e66917cb + languageName: node + linkType: hard + "@eslint/eslintrc@npm:^1.0.5": version: 1.4.1 resolution: "@eslint/eslintrc@npm:1.4.1" @@ -1630,11 +1649,11 @@ __metadata: linkType: hard "@graphql-typed-document-node/core@npm:^3.1.1": - version: 3.1.2 - resolution: "@graphql-typed-document-node/core@npm:3.1.2" + version: 3.2.0 + resolution: "@graphql-typed-document-node/core@npm:3.2.0" peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - checksum: a61afa025acdabd7833e4f654a5802fc1a526171f81e0c435c8e651050a5a0682499a2c7a51304ceb61fde36cd69fc7975ce5e1b16b9ba7ea474c649f33eea8b + checksum: fa44443accd28c8cf4cb96aaaf39d144a22e8b091b13366843f4e97d19c7bfeaf609ce3c7603a4aeffe385081eaf8ea245d078633a7324c11c5ec4b2011bb76d languageName: node linkType: hard @@ -2238,40 +2257,40 @@ __metadata: linkType: hard "@nestjs/cli@npm:^9.1.8": - version: 9.2.0 - resolution: "@nestjs/cli@npm:9.2.0" + version: 9.3.0 + resolution: "@nestjs/cli@npm:9.3.0" dependencies: - "@angular-devkit/core": 15.1.4 - "@angular-devkit/schematics": 15.1.4 - "@angular-devkit/schematics-cli": 15.1.4 - "@nestjs/schematics": ^9.0.0 - chalk: 3.0.0 + "@angular-devkit/core": 15.2.4 + "@angular-devkit/schematics": 15.2.4 + "@angular-devkit/schematics-cli": 15.2.4 + "@nestjs/schematics": ^9.0.4 + chalk: 4.1.2 chokidar: 3.5.3 cli-table3: 0.6.3 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 7.3.0 - inquirer: 7.3.3 + fork-ts-checker-webpack-plugin: 8.0.0 + inquirer: 8.2.5 node-emoji: 1.11.0 ora: 5.4.1 os-name: 4.0.1 - rimraf: 4.1.2 + rimraf: 4.4.0 shelljs: 0.8.5 source-map-support: 0.5.21 tree-kill: 1.2.2 tsconfig-paths: 4.1.2 - tsconfig-paths-webpack-plugin: 4.0.0 + tsconfig-paths-webpack-plugin: 4.0.1 typescript: 4.9.5 - webpack: 5.75.0 + webpack: 5.76.2 webpack-node-externals: 3.0.0 bin: nest: bin/nest.js - checksum: f752fc172a29bfa1b361bd0e7659e64ed3147316b0578a73383646e7cb52df720a17869dc6e673e88f09adbce7a388436f827e78eaa7e3cf4e64bfb584c4724a + checksum: b741ee52a78ce9ef91a836ad53ac239d5857a0db1c274a241905916df3e5b57f9092937ebfe92645b1de7514399011a70903ed7755585ae9c097d644d1a688d1 languageName: node linkType: hard "@nestjs/common@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/common@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/common@npm:9.3.12" dependencies: iterare: 1.2.1 tslib: 2.5.0 @@ -2289,7 +2308,7 @@ __metadata: optional: true class-validator: optional: true - checksum: 48fde36befe8f8d4527ec4e9b3ab5a0929c6fb5f5e47fa3312dc2789d523503bf20a92eceb2ad88e506614a2574af60b5c4ea6211956264ac16bc0eb737de5e5 + checksum: c31a3bc16362cc537865c5285161b92113118f0e952d95390525b19f3c999a76ad952ead9db969735afd22ca2cc085b40e1a258fde3351e2b5028d9fdbbf9967 languageName: node linkType: hard @@ -2310,8 +2329,8 @@ __metadata: linkType: hard "@nestjs/core@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/core@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/core@npm:9.3.12" dependencies: "@nuxtjs/opencollective": 0.3.2 fast-safe-stringify: 2.1.1 @@ -2333,7 +2352,7 @@ __metadata: optional: true "@nestjs/websockets": optional: true - checksum: b2a29c8b61b8b7bbc8d883d4ff9aed80504e28c11bd627fccbeb11a7920559b8d885efecee4799c3bd9c2cd0224dd519c7b2dcdfa9f523855529143596e1837a + checksum: 9a2ba9c304aae9655a8656e7eb2b9644340bc5498216377818c62d58930c1326dd313f3f20497e2499ed4c6215b5b77a2d18f874a463c14094b216ae743f14e1 languageName: node linkType: hard @@ -2407,10 +2426,10 @@ __metadata: linkType: hard "@nestjs/platform-express@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/platform-express@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/platform-express@npm:9.3.12" dependencies: - body-parser: 1.20.1 + body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 multer: 1.4.4-lts.1 @@ -2418,21 +2437,21 @@ __metadata: peerDependencies: "@nestjs/common": ^9.0.0 "@nestjs/core": ^9.0.0 - checksum: 26b41de9ded9134e259a4203ca2305f5436d64710b1880844214bbdbaffb112487b6d0d08d41a880051c3f6607807d10c9dcba715ed0d7b551231349f549472a + checksum: 6da92591e8c16288e210e577a4a31c433cdcaedfed3ef072be9149e175c1351c29cfeaf4b0adc99a2eb0e3da76e2a9287c7d8a6a8c1c5b22e730f86773b54145 languageName: node linkType: hard "@nestjs/platform-socket.io@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/platform-socket.io@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/platform-socket.io@npm:9.3.12" dependencies: - socket.io: 4.6.0 + socket.io: 4.6.1 tslib: 2.5.0 peerDependencies: "@nestjs/common": ^9.0.0 "@nestjs/websockets": ^9.0.0 rxjs: ^7.1.0 - checksum: 3463e1810e40bb024a65b64464a2755727a39af4d8b97f2385003d43d15c552113f2666a462860bf1a8e5d2cf34a7da31f11f347c1f2f18461a36407c1e5db08 + checksum: 45a20a7dcf825cf156870ba245fbfdce0fb564dc35bd1ee1d464337e5f4ed35ccc8e17fc08ce24a9ee10941dfea37760588226eb7943e8f4ca825668cd93de74 languageName: node linkType: hard @@ -2531,8 +2550,8 @@ __metadata: linkType: hard "@nestjs/testing@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/testing@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/testing@npm:9.3.12" dependencies: tslib: 2.5.0 peerDependencies: @@ -2545,13 +2564,13 @@ __metadata: optional: true "@nestjs/platform-express": optional: true - checksum: cd9003323e59440445df8f1d47a1c268aae871e3df6fb6fbcc7ab40afcd7c99286f968e9305403bc636e119a9ed1e70139e2486d1423ca456c7175abeab1b56a + checksum: 82549ca8f08d77ea862329e7aca3b8c865431a3809136f1e8f37e6f5d8b7e4af787f833bf8010c031b3ab512c10b6359092a98a87ed5fea98b72f6d04e3968ec languageName: node linkType: hard "@nestjs/websockets@npm:^9.2.1": - version: 9.3.9 - resolution: "@nestjs/websockets@npm:9.3.9" + version: 9.3.12 + resolution: "@nestjs/websockets@npm:9.3.12" dependencies: iterare: 1.2.1 object-hash: 3.0.0 @@ -2565,7 +2584,7 @@ __metadata: peerDependenciesMeta: "@nestjs/platform-socket.io": optional: true - checksum: e5960350ea544d42be6f2ad833ddbb718dc6f72254e0ddfe16069de9d58926ad8b7f02c8b3cc3d1aa19aea6dd3e968a4d13b115486d5ea286c3507688f924c34 + checksum: 734c5ef2d2fa75c9d951188809f987a1129c344e844135818f2d0ba05fe7b8eba661d152eef2e5846ef8b5f79f36d69b395a5d08d220218e8a62ffffe2885af2 languageName: node linkType: hard @@ -2616,18 +2635,18 @@ __metadata: languageName: node linkType: hard -"@nrwl/cli@npm:15.8.6, @nrwl/cli@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/cli@npm:15.8.6" +"@nrwl/cli@npm:15.8.7, @nrwl/cli@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/cli@npm:15.8.7" dependencies: - nx: 15.8.6 - checksum: d576972d1556d8eaf784199d48c942f0630e6177408ff80ceae467cc7ff3a3beb2ac6774a98f15aa9b7009f72c48347b8fa4187f911959ab389e656b601a17be + nx: 15.8.7 + checksum: 2c5b091cf0e55d181b0c6e6fc010d94672114a133ef57aab09ab4620f4c1d0599694304db5d89863c6b5ae5004b90c8d8bc4cb61a84f66449f3c420bb445baf3 languageName: node linkType: hard -"@nrwl/devkit@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/devkit@npm:15.8.6" +"@nrwl/devkit@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/devkit@npm:15.8.7" dependencies: "@phenomnomnominal/tsquery": 4.1.1 ejs: ^3.1.7 @@ -2637,15 +2656,15 @@ __metadata: tslib: ^2.3.0 peerDependencies: nx: ">= 14.1 <= 16" - checksum: b14592dec3792058bad6845853f14c50ab78e88ca25ead394f9eb89351fe561ae4589c57e8b6bbc2e529ed044852a9bcf3b5a36089a05735731d6d928d5af0d9 + checksum: 3a8a5830be6a1ad83df6277482a49120d7318922187e527a901098e1ca75d0be90eb831d2241921f2a65a1945a196b87f37982a635ae5de7feac00ad64c4884f languageName: node linkType: hard "@nrwl/eslint-plugin-nx@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/eslint-plugin-nx@npm:15.8.6" + version: 15.8.7 + resolution: "@nrwl/eslint-plugin-nx@npm:15.8.7" dependencies: - "@nrwl/devkit": 15.8.6 + "@nrwl/devkit": 15.8.7 "@typescript-eslint/utils": ^5.36.1 chalk: ^4.1.0 confusing-browser-globals: ^1.0.9 @@ -2656,18 +2675,18 @@ __metadata: peerDependenciesMeta: eslint-config-prettier: optional: true - checksum: a50ad710f6e622b18a28151ac65f77a308eb8c93977de2e5d06171d579ff8adb0d51ac00c4e9997e479bdec1487510c49881fd378b7da6454bcf91b609e4053f + checksum: 67e5f6bed855bc697515114d258277e21eae1e79d4a05e8c00920d6a1064bcb7a1e7b0260a13439512ccbe49835abf5ad5de52a7e6bd5f788f0c05e2f1b62916 languageName: node linkType: hard -"@nrwl/jest@npm:15.8.6, @nrwl/jest@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/jest@npm:15.8.6" +"@nrwl/jest@npm:15.8.7, @nrwl/jest@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/jest@npm:15.8.7" dependencies: "@jest/reporters": 28.1.1 "@jest/test-result": 28.1.1 - "@nrwl/devkit": 15.8.6 - "@nrwl/js": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/js": 15.8.7 "@phenomnomnominal/tsquery": 4.1.1 chalk: ^4.1.0 dotenv: ~10.0.0 @@ -2677,13 +2696,13 @@ __metadata: jest-util: 28.1.1 resolve.exports: 1.1.0 tslib: ^2.3.0 - checksum: 1ff2915b14e7f9ababa2654e435b9b38bafa81e83cac8c34ddaabb0e89debdeea67d5adee3066ff49cd458c4f299004c9a2db88e279715db15843cd583ba11f9 + checksum: 6899d019188f3e857be2c7158788b55c2a6aa85d87b3750dc530213b4c06dd3292ceebd2b73d01580af0f48bc712fd9bb34e230a809469830d713ce32318aff8 languageName: node linkType: hard -"@nrwl/js@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/js@npm:15.8.6" +"@nrwl/js@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/js@npm:15.8.7" dependencies: "@babel/core": ^7.15.0 "@babel/plugin-proposal-class-properties": ^7.14.5 @@ -2692,8 +2711,8 @@ __metadata: "@babel/preset-env": ^7.15.0 "@babel/preset-typescript": ^7.15.0 "@babel/runtime": ^7.14.8 - "@nrwl/devkit": 15.8.6 - "@nrwl/workspace": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/workspace": 15.8.7 "@phenomnomnominal/tsquery": 4.1.1 babel-plugin-const-enum: ^1.0.1 babel-plugin-macros: ^2.8.0 @@ -2707,16 +2726,16 @@ __metadata: source-map-support: 0.5.19 tree-kill: 1.2.2 tslib: ^2.3.0 - checksum: 2df820c85c786c01d5565348d54cfd919e44a17883c32f94e078f87f79110f376c678feb3534dd01fd0dc2c326f76fd788bf97bf2fc2ed62cce5cccf0efd8fc6 + checksum: 0fbe65f0567e8af6700a619396d2eb3004a3a6919279c06c6981e31c3116a6607a5a7a562b0e144090fb358331fc40237b7bbb2ba488a000d6a8ce45ab042a9b languageName: node linkType: hard -"@nrwl/linter@npm:15.8.6, @nrwl/linter@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/linter@npm:15.8.6" +"@nrwl/linter@npm:15.8.7, @nrwl/linter@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/linter@npm:15.8.7" dependencies: - "@nrwl/devkit": 15.8.6 - "@nrwl/js": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/js": 15.8.7 "@phenomnomnominal/tsquery": 4.1.1 tmp: ~0.2.1 tslib: ^2.3.0 @@ -2725,120 +2744,120 @@ __metadata: peerDependenciesMeta: eslint: optional: true - checksum: 888ad4bfba2d6dbfc69f446e769973ee427a56169d763bf00a5186a270ad2d452186f8061e09984c8cf0f64765789e50bf37b2b640af6907698519a456ae2632 + checksum: 0faf36df95fdc2b6afd7d896298430da3866a023b4f9a6a5a4e15b49fec841b7b6dfa70723972405e7ed6e0a03b9de5e388b66b2b9e9174e3a552404a1b8b4a9 languageName: node linkType: hard "@nrwl/nest@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/nest@npm:15.8.6" + version: 15.8.7 + resolution: "@nrwl/nest@npm:15.8.7" dependencies: "@nestjs/schematics": ^9.0.0 - "@nrwl/devkit": 15.8.6 - "@nrwl/js": 15.8.6 - "@nrwl/linter": 15.8.6 - "@nrwl/node": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/js": 15.8.7 + "@nrwl/linter": 15.8.7 + "@nrwl/node": 15.8.7 enquirer: ~2.3.6 - checksum: e5faab08faff7424d59b5fb9e4f0ffcd17c3d706e7a62e9ff8be548e5f0b1ba74a4713fa2306f9945d41c1b1c3237f3b880863d6bdb689feab611768f4848aba + checksum: bc430589a3782a5d62b44960061e4a63a187a30a0a8927a73e8620759da33c09ac7818ebd197c9c762a2a36fdc74198b0a842135f2b37ebd93111e0d466c3ee9 languageName: node linkType: hard -"@nrwl/node@npm:15.8.6, @nrwl/node@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/node@npm:15.8.6" +"@nrwl/node@npm:15.8.7, @nrwl/node@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/node@npm:15.8.7" dependencies: - "@nrwl/devkit": 15.8.6 - "@nrwl/jest": 15.8.6 - "@nrwl/js": 15.8.6 - "@nrwl/linter": 15.8.6 - "@nrwl/webpack": 15.8.6 - "@nrwl/workspace": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/jest": 15.8.7 + "@nrwl/js": 15.8.7 + "@nrwl/linter": 15.8.7 + "@nrwl/webpack": 15.8.7 + "@nrwl/workspace": 15.8.7 tslib: ^2.3.0 - checksum: 660885193a20fdffe9a9799dcb03ebd575f52e92ad845ae83e48110577736d95f77cad409a2975fc095e770d98721c676f04f9d2d13419b586c598f2ebfde26b + checksum: d41312e7278d21c7af53191a63958c66cfdfc58fd47784a47bc01d311d0b68fad2ac10f0ed6a7e3bbeec019eb643abd48d3dbb5d5235a748f65eaaf67bf4ae84 languageName: node linkType: hard -"@nrwl/nx-darwin-arm64@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-darwin-arm64@npm:15.8.6" +"@nrwl/nx-darwin-arm64@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-darwin-arm64@npm:15.8.7" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@nrwl/nx-darwin-x64@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-darwin-x64@npm:15.8.6" +"@nrwl/nx-darwin-x64@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-darwin-x64@npm:15.8.7" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@nrwl/nx-linux-arm-gnueabihf@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-linux-arm-gnueabihf@npm:15.8.6" +"@nrwl/nx-linux-arm-gnueabihf@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-linux-arm-gnueabihf@npm:15.8.7" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@nrwl/nx-linux-arm64-gnu@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-linux-arm64-gnu@npm:15.8.6" +"@nrwl/nx-linux-arm64-gnu@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-linux-arm64-gnu@npm:15.8.7" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@nrwl/nx-linux-arm64-musl@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-linux-arm64-musl@npm:15.8.6" +"@nrwl/nx-linux-arm64-musl@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-linux-arm64-musl@npm:15.8.7" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@nrwl/nx-linux-x64-gnu@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-linux-x64-gnu@npm:15.8.6" +"@nrwl/nx-linux-x64-gnu@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-linux-x64-gnu@npm:15.8.7" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@nrwl/nx-linux-x64-musl@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-linux-x64-musl@npm:15.8.6" +"@nrwl/nx-linux-x64-musl@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-linux-x64-musl@npm:15.8.7" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@nrwl/nx-win32-arm64-msvc@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-win32-arm64-msvc@npm:15.8.6" +"@nrwl/nx-win32-arm64-msvc@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-win32-arm64-msvc@npm:15.8.7" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@nrwl/nx-win32-x64-msvc@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/nx-win32-x64-msvc@npm:15.8.6" +"@nrwl/nx-win32-x64-msvc@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/nx-win32-x64-msvc@npm:15.8.7" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@nrwl/tao@npm:15.8.6, @nrwl/tao@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/tao@npm:15.8.6" +"@nrwl/tao@npm:15.8.7, @nrwl/tao@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/tao@npm:15.8.7" dependencies: - nx: 15.8.6 + nx: 15.8.7 bin: tao: index.js - checksum: 9391a0ffbbefd85b063f489f9c0a6652bab0c4cd9b04cd1d49260dfbffb7bc13e203cd3547eee4c70e7f8baa1fba55fa8d2bc646d25da9fe2f828a4439cff7a0 + checksum: 0045c017713226e4dc8d9eb36f0cd0882a534317519404993acc6d7d6fe2e1bcd4edf6b711b50ce311712b882968d386bc28d846cf61996b60389442d9c32f21 languageName: node linkType: hard -"@nrwl/webpack@npm:15.8.6": - version: 15.8.6 - resolution: "@nrwl/webpack@npm:15.8.6" +"@nrwl/webpack@npm:15.8.7": + version: 15.8.7 + resolution: "@nrwl/webpack@npm:15.8.7" dependencies: - "@nrwl/devkit": 15.8.6 - "@nrwl/js": 15.8.6 - "@nrwl/workspace": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/js": 15.8.7 + "@nrwl/workspace": 15.8.7 autoprefixer: ^10.4.9 babel-loader: ^9.1.2 chalk: ^4.1.0 @@ -2876,16 +2895,16 @@ __metadata: webpack-dev-server: ^4.9.3 webpack-node-externals: ^3.0.0 webpack-subresource-integrity: ^5.1.0 - checksum: 70740154dda2a357bfdae984f3a02b5e4b25ed8f0d3b80d55ff85ed945987d51459a04cc10ae7997a24b1fd7185488dd7b72878a6be1fdf691d5ec531de26414 + checksum: a84e2b16d3b2a00aacd952308802cb573d5a6668fa146c6a82d8d369dc542fc2e51c2f5613cf492b8e01b465343587583d26dcc3b76a8a5729fc5f982699c6fc languageName: node linkType: hard -"@nrwl/workspace@npm:15.8.6, @nrwl/workspace@npm:^15.5.1": - version: 15.8.6 - resolution: "@nrwl/workspace@npm:15.8.6" +"@nrwl/workspace@npm:15.8.7, @nrwl/workspace@npm:^15.5.1": + version: 15.8.7 + resolution: "@nrwl/workspace@npm:15.8.7" dependencies: - "@nrwl/devkit": 15.8.6 - "@nrwl/linter": 15.8.6 + "@nrwl/devkit": 15.8.7 + "@nrwl/linter": 15.8.7 "@parcel/watcher": 2.0.4 chalk: ^4.1.0 chokidar: ^3.5.1 @@ -2898,7 +2917,7 @@ __metadata: ignore: ^5.0.4 minimatch: 3.0.5 npm-run-path: ^4.0.1 - nx: 15.8.6 + nx: 15.8.7 open: ^8.4.0 rxjs: ^6.5.4 semver: 7.3.4 @@ -2911,7 +2930,7 @@ __metadata: peerDependenciesMeta: prettier: optional: true - checksum: 2598e8472acec9f94c293682992c0cd1ad5d033c19d7105fbe0ab2c353c03a48ea53685b9f659a40ce46ad07d8e703f17bbc4b7285530f8b887eac6b26ce0c2b + checksum: ef4c90682763d908eb6ec466de3c72cc09e368aa6e7fcd0b58b412f182e72e20fc32040619b07acca331a7cf0861aa7a3ac12f1004c1b3a584df5ef1d224794e languageName: node linkType: hard @@ -3723,12 +3742,12 @@ __metadata: linkType: hard "@types/eslint@npm:*": - version: 8.21.1 - resolution: "@types/eslint@npm:8.21.1" + version: 8.21.3 + resolution: "@types/eslint@npm:8.21.3" dependencies: "@types/estree": "*" "@types/json-schema": "*" - checksum: 584068441e4000c7b41c8928274fdcc737bc62f564928c30eb64ec41bbdbac31612f9fedaf490bceab31ec8305e99615166428188ea345d58878394683086fae + checksum: 80e0b5ca9ffb77bff19d01df08b93a99a6b44cad4c40e6450733ead6f1bc44b5514e7d28c3b0ad6304aeb01d690ee7ca89250729a9373551b7e587c6dbb41d7f languageName: node linkType: hard @@ -3904,9 +3923,9 @@ __metadata: linkType: hard "@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=8.1.0": - version: 18.15.1 - resolution: "@types/node@npm:18.15.1" - checksum: c63a40786919ef77c1dd26f60ffb1ed76c8b608fb156942e4d600d4536e06c19d09463a66cd76e6415f90cd281d7a1a4683e7cfd9a6f0927491853c56ffa17bf + version: 18.15.5 + resolution: "@types/node@npm:18.15.5" + checksum: 5fbf3453bd5ce1402bb2964e55d928fc8a8a7de5451b1b0fe66587fecb8a3eb86854ca9cefa5076a5971e2cff00e1773ceeb5d872a54f6c6ddfbbc1064b4e91a languageName: node linkType: hard @@ -3918,9 +3937,9 @@ __metadata: linkType: hard "@types/node@npm:^14.0.1": - version: 14.18.37 - resolution: "@types/node@npm:14.18.37" - checksum: d21e8c58ddd01ae069b196c2a4eaf9c9749e6666565349667334c60cfc119c1fa280234a8001157dd7ffe73501ce4f4940ca05f9d5c402c5abe78c8dca8376a6 + version: 14.18.40 + resolution: "@types/node@npm:14.18.40" + checksum: 99029290936a6be679dfe5f82687f2f164aedd35f436837577d12cb183a6c4021bd9f9a3093bef07a69b35af3de01b28a7625793f60677511418ace544c4deda languageName: node linkType: hard @@ -4009,9 +4028,9 @@ __metadata: linkType: hard "@types/validator@npm:^13.7.10": - version: 13.7.13 - resolution: "@types/validator@npm:13.7.13" - checksum: 7a80d3da763f3d00342a0b14c09ca3f891c9b503ce1d4e39cde4c2ccba00b9592cde33950844fd557a2f666cfe0aff905b5ca9789b61d178729b7df783034df5 + version: 13.7.14 + resolution: "@types/validator@npm:13.7.14" + checksum: 51bd82cd08aa7d8006f97357b5768a77bfca30e4823b5962e63bbf6446f46b5afe236bec1089148a15fd04cc0a748a10e2dd1a559f07163ec5e4e9fb5581896e languageName: node linkType: hard @@ -4032,11 +4051,11 @@ __metadata: linkType: hard "@types/yargs@npm:^17.0.8": - version: 17.0.22 - resolution: "@types/yargs@npm:17.0.22" + version: 17.0.23 + resolution: "@types/yargs@npm:17.0.23" dependencies: "@types/yargs-parser": "*" - checksum: 0773523fda71bafdc52f13f5970039e535a353665a60ba9261149a5c9c2b908242e6e77fbb7a8c06931ec78ce889d64d09673c68ba23eb5f5742d5385d0d1982 + checksum: c5f787d7a9a36ea94ba5d3f340fc5d93d2860eff8fa9731cd614ed23212e4fca75637e2386e37e376a720e4bf088ceed6f39050f1c3638fc1b75bce5c70b1ad4 languageName: node linkType: hard @@ -4050,17 +4069,17 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.48.1": - version: 5.54.1 - resolution: "@typescript-eslint/eslint-plugin@npm:5.54.1" + version: 5.56.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.56.0" dependencies: - "@typescript-eslint/scope-manager": 5.54.1 - "@typescript-eslint/type-utils": 5.54.1 - "@typescript-eslint/utils": 5.54.1 + "@eslint-community/regexpp": ^4.4.0 + "@typescript-eslint/scope-manager": 5.56.0 + "@typescript-eslint/type-utils": 5.56.0 + "@typescript-eslint/utils": 5.56.0 debug: ^4.3.4 grapheme-splitter: ^1.0.4 ignore: ^5.2.0 natural-compare-lite: ^1.4.0 - regexpp: ^3.2.0 semver: ^7.3.7 tsutils: ^3.21.0 peerDependencies: @@ -4069,43 +4088,43 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 76476c08ca0142a9bf6e2381f5cd1c037d86fbafa9c0dded4a97bd3b23b5962dd2c3943bade11b21d674195674f0e36dbf80faa15a1906f5a2ca1f699baf1dd5 + checksum: 2eed4a4ed8279950ad553252e8623e947ffdee39b0d677a13f6e4e2d863ea1cbc5d683ff189e55d0de6fd5a25afd72d3c3a9ab7ae417d5405a21ead907e1b154 languageName: node linkType: hard "@typescript-eslint/parser@npm:^5.48.1": - version: 5.54.1 - resolution: "@typescript-eslint/parser@npm:5.54.1" + version: 5.56.0 + resolution: "@typescript-eslint/parser@npm:5.56.0" dependencies: - "@typescript-eslint/scope-manager": 5.54.1 - "@typescript-eslint/types": 5.54.1 - "@typescript-eslint/typescript-estree": 5.54.1 + "@typescript-eslint/scope-manager": 5.56.0 + "@typescript-eslint/types": 5.56.0 + "@typescript-eslint/typescript-estree": 5.56.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: f466513d306ca926b97c2cec1eebaf2cd15d45bd5633a4358f23ba9a4de1b0ec4630b1c20abc395943934ed1d2ef65f545fd6737c317a7abe579612101e8a83f + checksum: eb25490290bd5e22f9c42603dedc0d2d8ee845553e3cf48ea377bd5dc22440d3463f8b84be637b6a2b37cd9ea56b21e4e43007a0a69998948d9c8965c03fe1aa languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.54.1": - version: 5.54.1 - resolution: "@typescript-eslint/scope-manager@npm:5.54.1" +"@typescript-eslint/scope-manager@npm:5.56.0": + version: 5.56.0 + resolution: "@typescript-eslint/scope-manager@npm:5.56.0" dependencies: - "@typescript-eslint/types": 5.54.1 - "@typescript-eslint/visitor-keys": 5.54.1 - checksum: 9add24cf3a7852634ad0680a827646860ac4698a6ac8aae31e8b781e29f59e84b51f0cdaacffd0747811012647f01b51969d988da9b302ead374ceebffbe204b + "@typescript-eslint/types": 5.56.0 + "@typescript-eslint/visitor-keys": 5.56.0 + checksum: bacac255ee52148cee6622be2811c0d7e25419058b89f1a11f4c1303faef4535a0a1237549f9556ec1d7a297c640ce4357183a1a8465d72e1393b7d8fb43874b languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.54.1": - version: 5.54.1 - resolution: "@typescript-eslint/type-utils@npm:5.54.1" +"@typescript-eslint/type-utils@npm:5.56.0": + version: 5.56.0 + resolution: "@typescript-eslint/type-utils@npm:5.56.0" dependencies: - "@typescript-eslint/typescript-estree": 5.54.1 - "@typescript-eslint/utils": 5.54.1 + "@typescript-eslint/typescript-estree": 5.56.0 + "@typescript-eslint/utils": 5.56.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -4113,23 +4132,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 0073838b782b7f4619775be124ca6643fec43a2d56043eaf3ceb100960a5193f14ac747b28ce17a5c9ac643fdee8abda82a7d905c81521358de7b27a2dcbc9af + checksum: 3dd1fcfadad18790b900a3d90f6617904adb6b0e2bd1e1edb6ebf239e1399865ca9098647405385feb4252d8b2b4577883e6fd3ef8d00bdd521d6070972d486b languageName: node linkType: hard -"@typescript-eslint/types@npm:5.54.1": - version: 5.54.1 - resolution: "@typescript-eslint/types@npm:5.54.1" - checksum: 84a8f725cfa10646af389659e09c510c38d82c65960c7b613f844a264acc0e197471cba03f3e8f4b6411bc35dca28922c8352a7bd44621411c73fd6dd4096da2 +"@typescript-eslint/types@npm:5.56.0": + version: 5.56.0 + resolution: "@typescript-eslint/types@npm:5.56.0" + checksum: 82ca11553bbb1bbfcaf7e7760b03c0d898940238dc002552c21af3e58f7d482c64c3c6cf0666521aff2a1e7b4b58bb6e4d9a00b1e4998a16b5039f5d288d003a languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.54.1": - version: 5.54.1 - resolution: "@typescript-eslint/typescript-estree@npm:5.54.1" +"@typescript-eslint/typescript-estree@npm:5.56.0": + version: 5.56.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.56.0" dependencies: - "@typescript-eslint/types": 5.54.1 - "@typescript-eslint/visitor-keys": 5.54.1 + "@typescript-eslint/types": 5.56.0 + "@typescript-eslint/visitor-keys": 5.56.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -4138,35 +4157,35 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: ea42bdb4832fa96fa1121237c9b664ac4506e2836646651e08a8542c8601d78af6c288779707f893ca4c884221829bb7d7b4b43c4a9c3ed959519266d03a139b + checksum: ec3e85201786aa9adddba7cb834a9f330a7f55c729ee9ccf847dbdc2f7437b760f3774152ccad6d0aa48d13fd78df766c880e3a7ca42e01a20aba0e1a1ed61c5 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.54.1, @typescript-eslint/utils@npm:^5.36.1": - version: 5.54.1 - resolution: "@typescript-eslint/utils@npm:5.54.1" +"@typescript-eslint/utils@npm:5.56.0, @typescript-eslint/utils@npm:^5.36.1": + version: 5.56.0 + resolution: "@typescript-eslint/utils@npm:5.56.0" dependencies: + "@eslint-community/eslint-utils": ^4.2.0 "@types/json-schema": ^7.0.9 "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.54.1 - "@typescript-eslint/types": 5.54.1 - "@typescript-eslint/typescript-estree": 5.54.1 + "@typescript-eslint/scope-manager": 5.56.0 + "@typescript-eslint/types": 5.56.0 + "@typescript-eslint/typescript-estree": 5.56.0 eslint-scope: ^5.1.1 - eslint-utils: ^3.0.0 semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 8f428ea4d338ce85d55fd0c9ae2b217b323f29f51b7c9f8077fef7001ca21d28b032c5e5165b67ae6057aef69edb0e7a164c3c483703be6f3e4e574248bbc399 + checksum: 413e8d4bf7023ee5ba4f695b62e796a1f94930bb92fe5aa0cee58f63b9837116c23f618825a9c671f610e50f5630188b6059b4ed6b05a2a3336f01d8e977becb languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.54.1": - version: 5.54.1 - resolution: "@typescript-eslint/visitor-keys@npm:5.54.1" +"@typescript-eslint/visitor-keys@npm:5.56.0": + version: 5.56.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.56.0" dependencies: - "@typescript-eslint/types": 5.54.1 + "@typescript-eslint/types": 5.56.0 eslint-visitor-keys: ^3.3.0 - checksum: 3a691abd2a43b86a0c41526d14a2afcc93a2e0512b5f8b9ec43f6029c493870808036eae5ee4fc655d26e1999017c4a4dffb241f47c36c2a1238ec9fbd08719c + checksum: 568fda40134e153d7befb59b55698f7919ba780d2d3431d8745feabf2e0fbb8aa7a02173b3c467dd20a0f6594e5248a1f82bb25d6c37827716d77452e86cad29 languageName: node linkType: hard @@ -5167,15 +5186,35 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.20.2": + version: 1.20.2 + resolution: "body-parser@npm:1.20.2" + dependencies: + bytes: 3.1.2 + content-type: ~1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: ~1.6.18 + unpipe: 1.0.0 + checksum: 14d37ec638ab5c93f6099ecaed7f28f890d222c650c69306872e00b9efa081ff6c596cd9afb9930656aae4d6c4e1c17537bea12bb73c87a217cb3cfea8896737 + languageName: node + linkType: hard + "bonjour-service@npm:^1.0.11": - version: 1.1.0 - resolution: "bonjour-service@npm:1.1.0" + version: 1.1.1 + resolution: "bonjour-service@npm:1.1.1" dependencies: array-flatten: ^2.1.2 dns-equal: ^1.0.0 fast-deep-equal: ^3.1.3 multicast-dns: ^7.2.5 - checksum: c0cdf6f6438ef4873ffd17768a9e62300ca30ac2bc3437bcfb6c75a3efd70ad80418c38ec19af2f5fe3a9f1dee725b83ff8e0c4a473b1b9f1718a39033b34cbf + checksum: 832d0cf78b91368fac8bb11fd7a714e46f4c4fb1bb14d7283bce614a6fb3aae2f3fe209aba5b4fa051811c1cab6921d073a83db8432fb23292f27dd4161fb0f1 languageName: node linkType: hard @@ -5435,9 +5474,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001449, caniuse-lite@npm:^1.0.30001464": - version: 1.0.30001465 - resolution: "caniuse-lite@npm:1.0.30001465" - checksum: c991ecdfff378a22b268f9b1eb732d003c8ad89db3241a4cdec3b3ec3354aa966a44171cb806c90abe2e3f0573d67dc29a7dce2478b1f070b23747c392244c5d + version: 1.0.30001469 + resolution: "caniuse-lite@npm:1.0.30001469" + checksum: 8e496509d7e9ff189c72205675b5db0c5f1b6a09917027441e835efae0848a468a8c4e7d2b409ffc202438fcd23ae53e017f976a03c22c04d12d3c0e1e33e5de languageName: node linkType: hard @@ -5457,16 +5496,6 @@ __metadata: languageName: node linkType: hard -"chalk@npm:3.0.0": - version: 3.0.0 - resolution: "chalk@npm:3.0.0" - dependencies: - ansi-styles: ^4.1.0 - supports-color: ^7.1.0 - checksum: 8e3ddf3981c4da405ddbd7d9c8d91944ddf6e33d6837756979f7840a29272a69a5189ecae0ff84006750d6d1e92368d413335eab4db5476db6e6703a1d1e0505 - languageName: node - linkType: hard - "chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" @@ -6004,7 +6033,7 @@ __metadata: languageName: node linkType: hard -"content-type@npm:~1.0.4": +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 @@ -6175,11 +6204,11 @@ __metadata: linkType: hard "css-declaration-sorter@npm:^6.3.1": - version: 6.3.1 - resolution: "css-declaration-sorter@npm:6.3.1" + version: 6.4.0 + resolution: "css-declaration-sorter@npm:6.4.0" peerDependencies: postcss: ^8.0.9 - checksum: ff0d9989ee21ec4c42430b9bb86c43f973ed5024d68f30edc1e3fb07a22828ce3c3e5b922019f2ccbff606722e43c407c5c76e3cddac523ac4afcb31e4b2601c + checksum: b716bc3d79154d3d618a90bd192533adf6604307c176e25e715a3b7cde587ef16971769fbf496118a376794280edf97016653477936c38c5a74cc852d6e38873 languageName: node linkType: hard @@ -6439,9 +6468,9 @@ __metadata: linkType: hard "deepmerge@npm:^4.2.2": - version: 4.3.0 - resolution: "deepmerge@npm:4.3.0" - checksum: c7980eb5c5be040b371f1df0d566473875cfabed9f672ccc177b81ba8eee5686ce2478de2f1d0076391621cbe729e5eacda397179a59ef0f68901849647db126 + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 2024c6a980a1b7128084170c4cf56b0fd58a63f2da1660dcfe977415f27b17dbe5888668b59d0b063753f3220719d5e400b7f113609489c90160bb9a5518d052 languageName: node linkType: hard @@ -6771,9 +6800,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.4.284": - version: 1.4.328 - resolution: "electron-to-chromium@npm:1.4.328" - checksum: 82c1617a77e40ac4ca5011749318a2fee8f8c75f8b517fcff7602219c85fd97a9fab2d5a1353ea10fb7f9c7d18acb90c9ed58c2292256f81e2ffa42ee66c4b0b + version: 1.4.337 + resolution: "electron-to-chromium@npm:1.4.337" + checksum: b7da59afa5e3239f222c3e146d27757cef10bdcb4180f3f77d7a7d339d1ee7c99d0e281807fe47a865426995a3d1c06809743ca71dddc0aaf65e749e66a8be40 languageName: node linkType: hard @@ -6852,7 +6881,7 @@ __metadata: languageName: node linkType: hard -"engine.io@npm:~6.4.0": +"engine.io@npm:~6.4.1": version: 6.4.1 resolution: "engine.io@npm:6.4.1" dependencies: @@ -6896,7 +6925,7 @@ __metadata: languageName: node linkType: hard -"entities@npm:^4.2.0, entities@npm:^4.3.0, entities@npm:^4.4.0": +"entities@npm:^4.2.0, entities@npm:^4.4.0": version: 4.4.0 resolution: "entities@npm:4.4.0" checksum: 84d250329f4b56b40fa93ed067b194db21e8815e4eb9b59f43a086f0ecd342814f6bc483de8a77da5d64e0f626033192b1b4f1792232a7ea6b970ebe0f3187c2 @@ -7622,9 +7651,9 @@ __metadata: languageName: node linkType: hard -"fork-ts-checker-webpack-plugin@npm:7.3.0": - version: 7.3.0 - resolution: "fork-ts-checker-webpack-plugin@npm:7.3.0" +"fork-ts-checker-webpack-plugin@npm:8.0.0": + version: 8.0.0 + resolution: "fork-ts-checker-webpack-plugin@npm:8.0.0" dependencies: "@babel/code-frame": ^7.16.7 chalk: ^4.1.2 @@ -7640,12 +7669,8 @@ __metadata: tapable: ^2.2.1 peerDependencies: typescript: ">3.6.0" - vue-template-compiler: "*" webpack: ^5.11.0 - peerDependenciesMeta: - vue-template-compiler: - optional: true - checksum: 49c2af801e264349a3fdf0afe4ad33065960c43bd7e56c8351a5e0d32c8c54146cc89d6a0b70b1e0f810de96787bd0c7fd275cc8727a9aea1a077c53de99659a + checksum: aad4cbc5b802e6281a2700a379837697c93ad95288468f9595219d91d9c26674736d37852bb4c4341e9122f26181e9e05fc1a362e8d029fdd88e99de7816037b languageName: node linkType: hard @@ -7725,7 +7750,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:11.1.0, fs-extra@npm:^11.1.0": +"fs-extra@npm:11.1.0": version: 11.1.0 resolution: "fs-extra@npm:11.1.0" dependencies: @@ -7736,6 +7761,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^11.1.0": + version: 11.1.1 + resolution: "fs-extra@npm:11.1.1" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + languageName: node + linkType: hard + "fs-jetpack@npm:4.3.1": version: 4.3.1 resolution: "fs-jetpack@npm:4.3.1" @@ -7951,6 +7987,18 @@ __metadata: languageName: node linkType: hard +"glob@npm:^9.2.0": + version: 9.3.2 + resolution: "glob@npm:9.3.2" + dependencies: + fs.realpath: ^1.0.0 + minimatch: ^7.4.1 + minipass: ^4.2.4 + path-scurry: ^1.6.1 + checksum: f3d188e9f70e24fa729a63ca197bcdb36d838677abec1fb9bbfe4b7620063bf90dc0f8d195203d632abfdfa049fad0edf22f93c60076de67cef20c23bcbfaee8 + languageName: node + linkType: hard + "global-dirs@npm:3.0.0": version: 3.0.0 resolution: "global-dirs@npm:3.0.0" @@ -8014,9 +8062,9 @@ __metadata: linkType: hard "graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.2, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 languageName: node linkType: hard @@ -8261,14 +8309,14 @@ __metadata: linkType: hard "htmlparser2@npm:^8.0.1": - version: 8.0.1 - resolution: "htmlparser2@npm:8.0.1" + version: 8.0.2 + resolution: "htmlparser2@npm:8.0.2" dependencies: domelementtype: ^2.3.0 - domhandler: ^5.0.2 + domhandler: ^5.0.3 domutils: ^3.0.1 - entities: ^4.3.0 - checksum: 06d5c71e8313597722bc429ae2a7a8333d77bd3ab07ccb916628384b37332027b047f8619448d8f4a3312b6609c6ea3302a4e77435d859e9e686999e6699ca39 + entities: ^4.4.0 + checksum: 29167a0f9282f181da8a6d0311b76820c8a59bc9e3c87009e21968264c2987d2723d6fde5a964d4b7b6cba663fca96ffb373c06d8223a85f52a6089ced942700 languageName: node linkType: hard @@ -8561,30 +8609,32 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:7.3.3": - version: 7.3.3 - resolution: "inquirer@npm:7.3.3" +"inquirer@npm:8.2.4": + version: 8.2.4 + resolution: "inquirer@npm:8.2.4" dependencies: ansi-escapes: ^4.2.1 - chalk: ^4.1.0 + chalk: ^4.1.1 cli-cursor: ^3.1.0 cli-width: ^3.0.0 external-editor: ^3.0.3 figures: ^3.0.0 - lodash: ^4.17.19 + lodash: ^4.17.21 mute-stream: 0.0.8 + ora: ^5.4.1 run-async: ^2.4.0 - rxjs: ^6.6.0 + rxjs: ^7.5.5 string-width: ^4.1.0 strip-ansi: ^6.0.0 through: ^2.3.6 - checksum: 4d387fc1eb6126acbd58cbdb9ad99d2887d181df86ab0c2b9abdf734e751093e2d5882c2b6dc7144d9ab16b7ab30a78a1d7f01fb6a2850a44aeb175d1e3f8778 + wrap-ansi: ^7.0.0 + checksum: dfcb6529d3af443dfea2241cb471508091b51f5121a088fdb8728b23ec9b349ef0a5e13a0ef2c8e19457b0bed22f7cbbcd561f7a4529d084c562a58c605e2655 languageName: node linkType: hard -"inquirer@npm:8.2.4": - version: 8.2.4 - resolution: "inquirer@npm:8.2.4" +"inquirer@npm:8.2.5": + version: 8.2.5 + resolution: "inquirer@npm:8.2.5" dependencies: ansi-escapes: ^4.2.1 chalk: ^4.1.1 @@ -8601,7 +8651,7 @@ __metadata: strip-ansi: ^6.0.0 through: ^2.3.6 wrap-ansi: ^7.0.0 - checksum: dfcb6529d3af443dfea2241cb471508091b51f5121a088fdb8728b23ec9b349ef0a5e13a0ef2c8e19457b0bed22f7cbbcd561f7a4529d084c562a58c605e2655 + checksum: f13ee4c444187786fb393609dedf6b30870115a57b603f2e6424f29a99abc13446fd45ee22461c33c9c40a92a60a8df62d0d6b25d74fc6676fa4cb211de55b55 languageName: node linkType: hard @@ -9389,14 +9439,14 @@ __metadata: linkType: hard "jest-mock-extended@npm:^3.0.1": - version: 3.0.3 - resolution: "jest-mock-extended@npm:3.0.3" + version: 3.0.4 + resolution: "jest-mock-extended@npm:3.0.4" dependencies: ts-essentials: ^7.0.3 peerDependencies: jest: ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0 || ^28.0.0 || ^29.0.0 - typescript: ^3.0.0 || ^4.0.0 - checksum: 826d619b0e9e5f4a7691259a5f8919fd40d6468d13d9c0d8c0724d8ebd106404e7416079692f4dce5b4d42e3579b5948ea789b951ab6858ab76c192113e1d759 + typescript: ^3.0.0 || ^4.0.0 || ^5.0.0 + checksum: f861253c63508b30d971fbbbc1bf2911ff4406cd260d0e23483a1d4514898b18ba5efbd43fbdf6d94996dc09b20eb1aad1b46aeaea9c99244ba12dc99814fd3f languageName: node linkType: hard @@ -10099,6 +10149,16 @@ __metadata: languageName: node linkType: hard +"launch-editor@npm:^2.6.0": + version: 2.6.0 + resolution: "launch-editor@npm:2.6.0" + dependencies: + picocolors: ^1.0.0 + shell-quote: ^1.7.3 + checksum: 48e4230643e8fdb5c14c11314706d58d9f3fbafe2606be3d6e37da1918ad8bfe39dd87875c726a1b59b9f4da99d87ec3e36d4c528464f0b820f9e91e5cb1c02d + languageName: node + linkType: hard + "lazystream@npm:^1.0.0": version: 1.0.1 resolution: "lazystream@npm:1.0.1" @@ -10173,9 +10233,9 @@ __metadata: linkType: hard "libphonenumber-js@npm:^1.10.14": - version: 1.10.21 - resolution: "libphonenumber-js@npm:1.10.21" - checksum: 0ffac0db4552eb1d39f8da8ee57cba20b1ad76a268f0e633c2dfcfe7db97d498cc6631c1ec4c1fc608944b866509d5b8e5039af878debec00b3a526946edebac + version: 1.10.24 + resolution: "libphonenumber-js@npm:1.10.24" + checksum: a79d98d398d6647649c46077d4159354b25d63c9f040c2cbf7b6f5b2e61df6224be75b0f2ce654d50fd6c78a3eab4eaf9f33f9ea09fcce1d8d4740ceef0b00ca languageName: node linkType: hard @@ -10430,7 +10490,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.21": +"lodash@npm:4.17.21, lodash@npm:^4.17.15, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -10494,7 +10554,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 @@ -10531,12 +10591,12 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:0.27.0": - version: 0.27.0 - resolution: "magic-string@npm:0.27.0" +"magic-string@npm:0.29.0": + version: 0.29.0 + resolution: "magic-string@npm:0.29.0" dependencies: "@jridgewell/sourcemap-codec": ^1.4.13 - checksum: 273faaa50baadb7a2df6e442eac34ad611304fc08fe16e24fe2e472fd944bfcb73ffb50d2dc972dc04e92784222002af46868cb9698b1be181c81830fd95a13e + checksum: 19e5398fcfc44804917127c72ad622c68a19a0a10cbdb8d4f9f9417584a087fe9e117140bfb2463d86743cf1ed9cf4182ae0b0ad1a7536f7fdda257ee4449ffb languageName: node linkType: hard @@ -10767,6 +10827,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^7.4.1": + version: 7.4.3 + resolution: "minimatch@npm:7.4.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: daa954231b6859e3ba0e5fbd2486986d3cae283bb69acb7ed3833c84a293f8d7edb8514360ea62c01426ba791446b2a1e1cc0d718bed15c0212cef35c59a6b95 + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -10834,7 +10903,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^4.0.0": +"minipass@npm:^4.0.0, minipass@npm:^4.0.2, minipass@npm:^4.2.4": version: 4.2.5 resolution: "minipass@npm:4.2.5" checksum: 4f9c19af23a5d4a9e7156feefc9110634b178a8cff8f8271af16ec5ebf7e221725a97429952c856f5b17b30c2065ebd24c81722d90c93d2122611d75b952b48f @@ -11588,21 +11657,21 @@ __metadata: languageName: node linkType: hard -"nx@npm:15.8.6, nx@npm:^15.5.1": - version: 15.8.6 - resolution: "nx@npm:15.8.6" +"nx@npm:15.8.7, nx@npm:^15.5.1": + version: 15.8.7 + resolution: "nx@npm:15.8.7" dependencies: - "@nrwl/cli": 15.8.6 - "@nrwl/nx-darwin-arm64": 15.8.6 - "@nrwl/nx-darwin-x64": 15.8.6 - "@nrwl/nx-linux-arm-gnueabihf": 15.8.6 - "@nrwl/nx-linux-arm64-gnu": 15.8.6 - "@nrwl/nx-linux-arm64-musl": 15.8.6 - "@nrwl/nx-linux-x64-gnu": 15.8.6 - "@nrwl/nx-linux-x64-musl": 15.8.6 - "@nrwl/nx-win32-arm64-msvc": 15.8.6 - "@nrwl/nx-win32-x64-msvc": 15.8.6 - "@nrwl/tao": 15.8.6 + "@nrwl/cli": 15.8.7 + "@nrwl/nx-darwin-arm64": 15.8.7 + "@nrwl/nx-darwin-x64": 15.8.7 + "@nrwl/nx-linux-arm-gnueabihf": 15.8.7 + "@nrwl/nx-linux-arm64-gnu": 15.8.7 + "@nrwl/nx-linux-arm64-musl": 15.8.7 + "@nrwl/nx-linux-x64-gnu": 15.8.7 + "@nrwl/nx-linux-x64-musl": 15.8.7 + "@nrwl/nx-win32-arm64-msvc": 15.8.7 + "@nrwl/nx-win32-x64-msvc": 15.8.7 + "@nrwl/tao": 15.8.7 "@parcel/watcher": 2.0.4 "@yarnpkg/lockfile": ^1.1.0 "@yarnpkg/parsers": ^3.0.0-rc.18 @@ -11665,7 +11734,7 @@ __metadata: optional: true bin: nx: bin/nx.js - checksum: 4bc1f39703286eacdd32b31f6a2312b38cdb29020075552630efe4c7f2214616ac06e4c43816bc94a68b12430291aaf66dae7ff2e3b4eab3b9372d442c88c91e + checksum: b85e1df9cf028dc97d91fdb17897edc890ca0910268c98079e180b07d4da33c083d85dade5bede00ff7c1e3778fca86b08339d19f176b870026c836ea360a457 languageName: node linkType: hard @@ -11999,6 +12068,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.6.1": + version: 1.6.3 + resolution: "path-scurry@npm:1.6.3" + dependencies: + lru-cache: ^7.14.1 + minipass: ^4.0.2 + checksum: 814ebd7f8df717e2381dc707ba3a3ddf84d0a4f9d653036c7554cb1fea632d4d78eb17dd5f4c85111b78ba8b8c0a5b59c756645c9d343bdacacda4ba8d1626c2 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -12824,6 +12903,18 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:2.5.2": + version: 2.5.2 + resolution: "raw-body@npm:2.5.2" + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: ba1583c8d8a48e8fbb7a873fdbb2df66ea4ff83775421bfe21ee120140949ab048200668c47d9ae3880012f6e217052690628cf679ddfbd82c9fc9358d574676 + languageName: node + linkType: hard + "react-is@npm:^18.0.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" @@ -13178,12 +13269,14 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:4.1.2": - version: 4.1.2 - resolution: "rimraf@npm:4.1.2" +"rimraf@npm:4.4.0": + version: 4.4.0 + resolution: "rimraf@npm:4.4.0" + dependencies: + glob: ^9.2.0 bin: rimraf: dist/cjs/src/bin.js - checksum: 480b8147fd9bcbef3ac118f88a7b1169c3872977a3411a0c84df838bfc30e175a394c0db6f9619fc8b8a886a18c6d779d5e74f380a0075ecc710afaf81b3f50c + checksum: 0cedaf9d138589d1bb0ab851f05804c6d30827aa66563472b04ab76245f83537e23e7b94f1f79ea6c368c0d84a18fcde6a756fca3a44c967e08792671b3a0a6e languageName: node linkType: hard @@ -13203,7 +13296,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:6.6.7, rxjs@npm:^6.5.4, rxjs@npm:^6.6.0": +"rxjs@npm:6.6.7, rxjs@npm:^6.5.4": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -13277,15 +13370,15 @@ __metadata: linkType: hard "sass@npm:^1.42.1": - version: 1.59.2 - resolution: "sass@npm:1.59.2" + version: 1.59.3 + resolution: "sass@npm:1.59.3" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: ab015ac49beb1252373023cc79b687aabd7850a7f450250b2fbe4eb3f64b0aef6759f8c7b33234221788a0e42cdd3999edfb5995218e474123b99cb126773e30 + checksum: 839b5282cdf7d0ba3fdbfb605277dd584a8c40fa3e3e58ad905d64cd812acfb82ff0a4072d4981673db884ee61505472ff07c5c5a8a497f16ba013b183ba6473 languageName: node linkType: hard @@ -13506,6 +13599,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.7.3": + version: 1.8.0 + resolution: "shell-quote@npm:1.8.0" + checksum: 6ef7c5e308b9c77eedded882653a132214fa98b4a1512bb507588cf6cd2fc78bfee73e945d0c3211af028a1eabe09c6a19b96edd8977dc149810797e93809749 + languageName: node + linkType: hard + "shelljs@npm:0.8.5": version: 0.8.5 resolution: "shelljs@npm:0.8.5" @@ -13620,17 +13720,17 @@ __metadata: languageName: node linkType: hard -"socket.io@npm:4.6.0": - version: 4.6.0 - resolution: "socket.io@npm:4.6.0" +"socket.io@npm:4.6.1": + version: 4.6.1 + resolution: "socket.io@npm:4.6.1" dependencies: accepts: ~1.3.4 base64id: ~2.0.0 debug: ~4.3.2 - engine.io: ~6.4.0 + engine.io: ~6.4.1 socket.io-adapter: ~2.5.2 socket.io-parser: ~4.2.1 - checksum: 4fbd850537f52da301111d809e631afec0352b8001a1121851441c3baca3a5351b3aabdd9538da8b9cf491fb35b1137197dda57a58fea61e8046aa80008616d0 + checksum: 447941727142669b3709c3ae59ed790a2c3ad312d935400e2e25fdf59a95cdc92ebcf6b000ab2042a2a77ae51bb87598b40845a8d3b1f6ea6a0dd1df9c8f8459 languageName: node linkType: hard @@ -14002,11 +14102,11 @@ __metadata: linkType: hard "style-loader@npm:^3.3.0": - version: 3.3.1 - resolution: "style-loader@npm:3.3.1" + version: 3.3.2 + resolution: "style-loader@npm:3.3.2" peerDependencies: webpack: ^5.0.0 - checksum: 470feef680f59e2fce4d6601b5c55b88c01ad8d1dd693c528ffd591ff5fd7c01a4eff3bdbe62f26f847d6bd2430c9ab594be23307cfe7a3446ab236683f0d066 + checksum: 5ee5ce2dc885369eccb55d429376e83d02570d473ac5edeb69fd65ee894847f1e51429cf078351f617bd04516ece8a1dd967f9f40464bd8fa76d903c6b2a6f08 languageName: node linkType: hard @@ -14557,6 +14657,17 @@ __metadata: languageName: node linkType: hard +"tsconfig-paths-webpack-plugin@npm:4.0.1": + version: 4.0.1 + resolution: "tsconfig-paths-webpack-plugin@npm:4.0.1" + dependencies: + chalk: ^4.1.0 + enhanced-resolve: ^5.7.0 + tsconfig-paths: ^4.1.2 + checksum: a09e765c4939379fa060f78bbc906d0a7be541b9b49d3ad4744e99a2e87f28aa30d549a549196159bc5a50f420fdb1391b8eed360f47ee8ce40e15dcb800b5aa + languageName: node + linkType: hard + "tsconfig-paths@npm:4.1.2, tsconfig-paths@npm:^4.0.0, tsconfig-paths@npm:^4.1.2": version: 4.1.2 resolution: "tsconfig-paths@npm:4.1.2" @@ -14714,11 +14825,11 @@ __metadata: linkType: hard "undici@npm:^5.8.2": - version: 5.20.0 - resolution: "undici@npm:5.20.0" + version: 5.21.0 + resolution: "undici@npm:5.21.0" dependencies: busboy: ^1.6.0 - checksum: 25412a785b2bd0b12f0bb0ec47ef00aa7a611ca0e570cb7af97cffe6a42e0d78e4b15190363a43771e9002defc3c6647c1b2d52201b3f64e2196819db4d150d3 + checksum: 013d5fd503b631d607942c511c2ab3f3fa78ebcab302acab998b43176b4815503ec15ed9752c5a47918b3bff8a0137768001d3eb57625b2bb6f6d30d8a794d6c languageName: node linkType: hard @@ -15066,8 +15177,8 @@ __metadata: linkType: hard "webpack-dev-server@npm:^4.9.3": - version: 4.11.1 - resolution: "webpack-dev-server@npm:4.11.1" + version: 4.13.1 + resolution: "webpack-dev-server@npm:4.13.1" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 @@ -15088,6 +15199,7 @@ __metadata: html-entities: ^2.3.2 http-proxy-middleware: ^2.0.3 ipaddr.js: ^2.0.1 + launch-editor: ^2.6.0 open: ^8.0.9 p-retry: ^4.5.0 rimraf: ^3.0.2 @@ -15097,15 +15209,17 @@ __metadata: sockjs: ^0.3.24 spdy: ^4.0.2 webpack-dev-middleware: ^5.3.1 - ws: ^8.4.2 + ws: ^8.13.0 peerDependencies: webpack: ^4.37.0 || ^5.0.0 peerDependenciesMeta: + webpack: + optional: true webpack-cli: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: b7601a39ee0f413988259e29a36835b0a68522cfaa161de5b7ec99b3399acdd99d44189add4aaf4a5191258bb130f9cf3e68919324a1955c7557f5fe6ab0d96c + checksum: f70611544b7d964a31eb3d934d7c2b376b97e6927a89e03b2e21cfa5812bb639625cd18fd350de1604ba6c455b324135523a894032f28c69d90d90682e4f3b7d languageName: node linkType: hard @@ -15138,9 +15252,9 @@ __metadata: languageName: node linkType: hard -"webpack@npm:5.75.0": - version: 5.75.0 - resolution: "webpack@npm:5.75.0" +"webpack@npm:5.76.2": + version: 5.76.2 + resolution: "webpack@npm:5.76.2" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^0.0.51 @@ -15171,13 +15285,13 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: 2bcc5f3c195f375944e8af2f00bf2feea39cb9fda5f763b0d1b00077f1c51783db25c94d3fae96a07dead9fa085e6ae7474417e5ab31719c9776ea5969ceb83a + checksum: 86db98299a175c371031449c26077e87b33acd8f45de7f7945ed4b9b37c8ca11bc5169af9c44743efccd4d55e08042a3aa3a3bc42aff831309a0821ffbcd395e languageName: node linkType: hard "webpack@npm:^5.75.0": - version: 5.76.1 - resolution: "webpack@npm:5.76.1" + version: 5.76.3 + resolution: "webpack@npm:5.76.3" dependencies: "@types/eslint-scope": ^3.7.3 "@types/estree": ^0.0.51 @@ -15208,7 +15322,7 @@ __metadata: optional: true bin: webpack: bin/webpack.js - checksum: b01fe0bc2dbca0e10d290ddb0bf81e807a031de48028176e2b21afd696b4d3f25ab9accdad888ef4a1f7c7f4d41f13d5bf2395b7653fdf3e5e3dafa54e56dab2 + checksum: 363f536b56971d056e34ab4cffa4cbc630b220e51be1a8c3adea87d9f0b51c49cfc7c3720d6614a1fd2c8c63f1ab3100db916fe8367c8bb9299327ff8c3f856d languageName: node linkType: hard @@ -15375,7 +15489,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.4.2": +"ws@npm:^8.13.0": version: 8.13.0 resolution: "ws@npm:8.13.0" peerDependencies: