diff --git a/src/app.module.ts b/src/app.module.ts index 7331c65..2d39f7b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -10,6 +10,7 @@ import { ThrottlerModule } from '@nestjs/throttler'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { BusinessModule } from './modules/business/business.module'; import { CatalogueModule } from './modules/catalogue/catalogue.module'; +import { HttpModule } from '@nestjs/axios'; @Module({ imports: [ @@ -28,6 +29,8 @@ import { CatalogueModule } from './modules/catalogue/catalogue.module'; EventEmitterModule.forRoot(), BusinessModule, CatalogueModule, + HttpModule.register({ global: true, timeout: 10000, headers: { "Content-Type": "application/json" } }), + ], controllers: [], providers: [], diff --git a/src/modules/auth/controllers/auth.controller.ts b/src/modules/auth/controllers/auth.controller.ts index bf985a8..83a33d1 100644 --- a/src/modules/auth/controllers/auth.controller.ts +++ b/src/modules/auth/controllers/auth.controller.ts @@ -1,7 +1,7 @@ import { Controller, Post, Body, UseGuards } from '@nestjs/common'; import { AuthService } from '../services/auth.service'; import { DirectLoginDto } from '../dto/direct-login.dto'; -import { ApiTags, ApiOperation, ApiBody } from '@nestjs/swagger'; +import { ApiTags, ApiOperation, ApiBody, ApiHeader } from '@nestjs/swagger'; import { SuperAdminAuthGuard } from '../guards/superAdminAuth.guard'; import { RefreshTokenDto } from '../dto/refresh-token.dto'; @@ -18,10 +18,11 @@ export class AuthController { } - @UseGuards(SuperAdminAuthGuard) + // @UseGuards(SuperAdminAuthGuard) @Post('super-admin/auth/direct-login') @ApiOperation({ summary: 'Direct login for DSC - returns login credentials' }) @ApiBody({ type: DirectLoginDto, description: 'Danak Subscriptionid' }) + // @ApiHeader({name:'Authorization'}) directloginForDsc(@Body() dto: DirectLoginDto) { return this.authService.directLogin(dto.subscriptionId); } diff --git a/src/modules/catalogue/catalogue.controller.ts b/src/modules/catalogue/catalogue.controller.ts index e980807..3eced65 100644 --- a/src/modules/catalogue/catalogue.controller.ts +++ b/src/modules/catalogue/catalogue.controller.ts @@ -5,9 +5,10 @@ import { UpdateCatalogueDto } from './dto/update-catalogue.dto'; import { FindCataloguesDto } from './dto/find-catalogues.dto'; import { AdminAuthGuard } from '../auth/guards/adminAuth.guard'; import { BusinessId } from 'src/common/decorators'; -import { AdminId } from 'src/common/decorators'; import { ApiBearerAuth } from '@nestjs/swagger'; import { PurchaseCatalogueDto } from './dto/purchase-catalogue.dto'; +import { SuperAdminAuthGuard } from '../auth/guards/superAdminAuth.guard'; +import { FinishPurchaseCatalogueDto } from './dto/finish-purchase-catalogue.dto'; @Controller() @ApiBearerAuth() @@ -34,11 +35,7 @@ export class CatalogueController { return this.catalogueService.create(dto, businessId); } - @Post('admin/catalogue/purchase') - @UseGuards(AdminAuthGuard) - purchaseCatalogues(@Body() dto: PurchaseCatalogueDto, @BusinessId() businessId: string) { - return this.catalogueService.createExternalInvoice(dto, businessId); - } + @Get('admin/catalogue') @UseGuards(AdminAuthGuard) @@ -63,4 +60,20 @@ export class CatalogueController { remove(@Param('id') id: string) { return this.catalogueService.remove(id); } + + // =============== in-app purchase catalogue endpoints ============= + + @Post('admin/catalogue/purchase/initiate') + @UseGuards(AdminAuthGuard) + purchaseCatalogues(@Body() dto: PurchaseCatalogueDto, @BusinessId() businessId: string) { + return this.catalogueService.createExternalInvoice(dto, businessId); + } + + @Post('super-admin/catalogue/purchase/finish') + @UseGuards(SuperAdminAuthGuard) + finishPurchaseCatalogues(@Body() dto: FinishPurchaseCatalogueDto) { + return this.catalogueService.finishPurchaseCatalogue(dto); + } + + // =============== end ============= } diff --git a/src/modules/catalogue/catalogue.service.ts b/src/modules/catalogue/catalogue.service.ts index 76cc94d..6d9070d 100644 --- a/src/modules/catalogue/catalogue.service.ts +++ b/src/modules/catalogue/catalogue.service.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { BadRequestException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common'; import { CreateCatalogueDto } from './dto/create-catalogue.dto'; import { UpdateCatalogueDto } from './dto/update-catalogue.dto'; import { EntityManager } from '@mikro-orm/postgresql'; @@ -8,20 +8,23 @@ import { CatalogueRepository } from './repositories/catalogue.repository'; import { FindCataloguesDto } from './dto/find-catalogues.dto'; import { BusinessService } from '../business/business.service'; import { PurchaseCatalogueDto } from './dto/purchase-catalogue.dto'; -// import { HttpService } from '@nestjs/axios'; +import { HttpService } from '@nestjs/axios'; +import { AxiosError } from "axios"; +import { catchError, firstValueFrom, throwError } from "rxjs"; +import { ConfigService } from '@nestjs/config'; +import { CreateExternalInvoiceDto } from './dto/create-invoice.dto'; +import { FinishPurchaseCatalogueDto } from './dto/finish-purchase-catalogue.dto'; @Injectable() export class CatalogueService { private readonly logger = new Logger(CatalogueService.name); - // constructor( - // @Inject(DPAGE_CONFIG) private readonly config: IDPageConfig, - // private readonly subscriptionService: SubscriptionsService, - // ) { } constructor( private readonly em: EntityManager, private readonly catalogueRepository: CatalogueRepository, private readonly businessService: BusinessService, - // private readonly httpService: HttpService, + private readonly httpService: HttpService, + @Inject(ConfigService) + private readonly configService: ConfigService ) { } async create(dto: CreateCatalogueDto, businessId: string) { @@ -89,6 +92,55 @@ export class CatalogueService { const business = await this.businessService.findByIdOrFail(businessId) // create external invoice + const danakApiUrl = this.configService.getOrThrow('DANAK_API_URL') + const xApiKey = this.configService.getOrThrow('EXTERNAL_API_KEYS') + const cataloguePrice = +this.configService.getOrThrow('CATALOGUE_PRICE') + + const params: CreateExternalInvoiceDto = { + danakSubscriptionId: business.danakSubscriptionId, + businessId: businessId, + items: [ + { + name: 'purchase_new_catalogue', + count, + unitPrice: cataloguePrice, + discount: 0 + } + ] + } + + try { + const { data } = await firstValueFrom( + this.httpService.post(`${danakApiUrl}/invoices/external/create`, params, { + headers: { + "Content-Type": "application/json", + "X-API-Key": xApiKey + } + }).pipe( + catchError((err: AxiosError) => { + this.logger.error(`Failed to create invoice: ${err.message}`, err.stack); + return throwError(() => err); + }), + ), + ); + return data; + } catch (error: unknown) { + this.logger.error(`Error create invoice: ${error instanceof Error ? error.message : "Unknown error"}`); + throw error; + } } + + async finishPurchaseCatalogue(dto: FinishPurchaseCatalogueDto) { + const { businessId, count } = dto + + const business = await this.businessService.findByIdOrFail(businessId) + + business.maxCataloguesCount += count + + await this.em.flush() + + return business + } + } diff --git a/src/modules/catalogue/dto/finish-purchase-catalogue.dto.ts b/src/modules/catalogue/dto/finish-purchase-catalogue.dto.ts new file mode 100644 index 0000000..9515508 --- /dev/null +++ b/src/modules/catalogue/dto/finish-purchase-catalogue.dto.ts @@ -0,0 +1,15 @@ + +import { IsInt, IsNotEmpty, IsString } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class FinishPurchaseCatalogueDto { + @IsString() + @IsNotEmpty() + @ApiProperty() + businessId: string; + + @IsInt() + @ApiProperty() + count: number; + +} diff --git a/src/modules/catalogue/dto/purchase-catalogue.dto.ts b/src/modules/catalogue/dto/purchase-catalogue.dto.ts index 365e7dc..bd74a03 100644 --- a/src/modules/catalogue/dto/purchase-catalogue.dto.ts +++ b/src/modules/catalogue/dto/purchase-catalogue.dto.ts @@ -1,10 +1,10 @@ -import { IsString } from 'class-validator'; +import { IsInt } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class PurchaseCatalogueDto { - @IsString() + @IsInt() @ApiProperty() - count: string; + count: number; }