From 926426369a71b66e1c50d4e0ff2c531bbd27b30e Mon Sep 17 00:00:00 2001 From: ummarig Date: Mon, 27 Jul 2026 06:40:48 +0100 Subject: [PATCH 1/5] implemented the errors --- src/common/filters/http-exception.filter.ts | 45 +++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/common/filters/http-exception.filter.ts diff --git a/src/common/filters/http-exception.filter.ts b/src/common/filters/http-exception.filter.ts new file mode 100644 index 00000000..d09b6bf4 --- /dev/null +++ b/src/common/filters/http-exception.filter.ts @@ -0,0 +1,45 @@ +import { + ExceptionFilter, + Catch, + ArgumentsHost, + HttpException, + Logger, +} from '@nestjs/common'; +import { Request, Response } from 'express'; + +@Catch(HttpException) +export class HttpExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name); + + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const status = exception.getStatus(); + + const exceptionResponse = exception.getResponse(); + let message = 'An unexpected error occurred'; + let errors = null; + + if (typeof exceptionResponse === 'string') { + message = exceptionResponse; + } else if (typeof exceptionResponse === 'object') { + message = (exceptionResponse as any).message || message; + errors = (exceptionResponse as any).errors || null; + } + + this.logger.error( + `HTTP Exception: ${status} - ${message} - ${request.url} - Stack: ${exception.stack}`, + ); + + response.status(status).json({ + success: false, + statusCode: status, + timestamp: new Date().toISOString(), + path: request.url, + message, + errors, + stack: process.env.NODE_ENV === 'development' ? exception.stack : undefined, + }); + } +} \ No newline at end of file From 6ed9813e58875da3e09a05ff56ef20a36846001a Mon Sep 17 00:00:00 2001 From: ummarig Date: Mon, 27 Jul 2026 06:41:11 +0100 Subject: [PATCH 2/5] implemented the errors --- src/common/filters/all-exceptions.filter.ts | 36 ++++++++++ src/common/filters/prisma-exception.filter.ts | 71 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/common/filters/all-exceptions.filter.ts create mode 100644 src/common/filters/prisma-exception.filter.ts diff --git a/src/common/filters/all-exceptions.filter.ts b/src/common/filters/all-exceptions.filter.ts new file mode 100644 index 00000000..f24d1c8c --- /dev/null +++ b/src/common/filters/all-exceptions.filter.ts @@ -0,0 +1,36 @@ +import { + ExceptionFilter, + Catch, + ArgumentsHost, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { Request, Response } from 'express'; + +@Catch() +export class AllExceptionsFilter implements ExceptionFilter { + private readonly logger = new Logger(AllExceptionsFilter.name); + + catch(exception: unknown, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + const status = HttpStatus.INTERNAL_SERVER_ERROR; + const message = exception instanceof Error ? exception.message : 'Internal server error'; + + this.logger.error( + `Uncaught Exception: ${message} - ${request.url}`, + exception instanceof Error ? exception.stack : 'No stack trace available', + ); + + response.status(status).json({ + success: false, + statusCode: status, + timestamp: new Date().toISOString(), + path: request.url, + message: process.env.NODE_ENV === 'production' ? 'Internal server error' : message, + stack: process.env.NODE_ENV === 'development' && exception instanceof Error ? exception.stack : undefined, + }); + } +} \ No newline at end of file diff --git a/src/common/filters/prisma-exception.filter.ts b/src/common/filters/prisma-exception.filter.ts new file mode 100644 index 00000000..13b7c210 --- /dev/null +++ b/src/common/filters/prisma-exception.filter.ts @@ -0,0 +1,71 @@ +import { + ExceptionFilter, + Catch, + ArgumentsHost, + HttpStatus, + Logger, +} from '@nestjs/common'; +import { Request, Response } from 'express'; +import { Prisma } from '@prisma/client'; + +@Catch(Prisma.PrismaClientKnownRequestError) +export class PrismaExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(PrismaExceptionFilter.name); + + catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + + let status = HttpStatus.INTERNAL_SERVER_ERROR; + let message = 'Database error occurred'; + let errors = null; + + // Handle common Prisma errors + switch (exception.code) { + case 'P2002': // Unique constraint violation + status = HttpStatus.CONFLICT; + const target = (exception.meta?.target as string[])?.join(', ') || 'field'; + message = `Unique constraint failed on ${target}`; + errors = { [target]: 'must be unique' }; + break; + case 'P2025': // Record not found + status = HttpStatus.NOT_FOUND; + message = 'Record not found'; + break; + case 'P2003': // Foreign key constraint violation + status = HttpStatus.BAD_REQUEST; + message = 'Foreign key constraint failed - related record does not exist'; + break; + case 'P2014': // Relation violation + status = HttpStatus.BAD_REQUEST; + message = 'Invalid relation - cannot change record due to existing dependencies'; + break; + case 'P2000': // Value too long for column + status = HttpStatus.BAD_REQUEST; + message = 'Value too long for column'; + break; + case 'P2011': // Null constraint violation + status = HttpStatus.BAD_REQUEST; + message = 'Null constraint violation - cannot set required field to null'; + break; + default: + this.logger.error(`Unhandled Prisma error: ${exception.code}`, exception.stack); + } + + this.logger.error( + `Prisma Exception: ${exception.code} - ${message} - ${request.url} - Stack: ${exception.stack}`, + ); + + response.status(status).json({ + success: false, + statusCode: status, + timestamp: new Date().toISOString(), + path: request.url, + message, + errors, + prismaCode: process.env.NODE_ENV === 'development' ? exception.code : undefined, + stack: process.env.NODE_ENV === 'development' ? exception.stack : undefined, + }); + } +} \ No newline at end of file From 6e57b54c67fe822c85fefa43d56373d82b5ef647 Mon Sep 17 00:00:00 2001 From: ummarig Date: Mon, 27 Jul 2026 06:41:43 +0100 Subject: [PATCH 3/5] implemented the errors --- src/main.ts | 59 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main.ts b/src/main.ts index c20f63a9..8728539d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,10 @@ import { RateLimitService } from './auth/rate-limit.service'; import { RateLimitHeadersInterceptor } from './auth/interceptors/rate-limit-headers.interceptor'; import { setupSwagger } from './config/swagger.config'; import { validateEnvironment } from './utils/validate-env'; +// Import our exception filters +import { AllExceptionsFilter } from './common/filters/all-exceptions.filter'; +import { HttpExceptionFilter } from './common/filters/http-exception.filter'; +import { PrismaExceptionFilter } from './common/filters/prisma-exception.filter'; async function bootstrap() { validateEnvironment(); @@ -27,21 +31,50 @@ async function bootstrap() { `Node.js >= ${REQUIRED_NODE_MAJOR} required, found ${process.versions.node}. ` + `Please upgrade Node.js (see https://nodejs.org/).`, ); + process.exit(1); + } - // Setup Swagger documentation - setupSwagger(app); + const app = await NestFactory.create(AppModule); - app.enableShutdownHooks(); + // Register global exception filters + app.useGlobalFilters( + new AllExceptionsFilter(), + new HttpExceptionFilter(), + new PrismaExceptionFilter(), + ); - const port = process.env.PORT || 3000; - await app.listen(port); - logger.log(`PropChain API running on http://localhost:${port}`); - logger.log(`API Versioning enabled. Supported versions: v1, v2`); - logger.log(`📚 Swagger UI available at http://localhost:${port}/api/docs`); - logger.log(`📋 OpenAPI spec available at http://localhost:${port}/api/openapi.json`); - logger.log(`💾 Redis Caching enabled`); - logger.log(`🛡️ Rate Limiting enabled (per-user, per-endpoint, IP-based)`); - } + // Global validation pipe + app.useGlobalPipes(new ValidationPipe({ + whitelist: true, + transform: true, + forbidNonWhitelisted: true, + })); + + // Setup global guards and interceptors + const reflector = app.get(Reflector); + app.useGlobalGuards(new RateLimitGuard(new RateLimitService(), reflector)); + + app.useGlobalInterceptors( + new VersionHeaderInterceptor(), + new DeprecationWarningInterceptor(), + new CacheMetricsInterceptor(app.get(CacheMonitoringService)), + new RateLimitHeadersInterceptor(), + ); - bootstrap(); + // Setup Swagger documentation + setupSwagger(app); + + app.enableShutdownHooks(); + + const port = process.env.PORT || 3000; + await app.listen(port); + logger.log(`PropChain API running on http://localhost:${port}`); + logger.log(`API Versioning enabled. Supported versions: v1, v2`); + logger.log(`📚 Swagger UI available at http://localhost:${port}/api/docs`); + logger.log(`📋 OpenAPI spec available at http://localhost:${port}/api/openapi.json`); + logger.log(`💾 Redis Caching enabled`); + logger.log(`🛡️ Rate Limiting enabled (per-user, per-endpoint, IP-based)`); + logger.log(`✅ Global exception filters registered successfully`); } + +bootstrap(); \ No newline at end of file From 70094d5b83ad5271ccd920668f0d0f935b01a885 Mon Sep 17 00:00:00 2001 From: ummarig Date: Mon, 27 Jul 2026 06:42:37 +0100 Subject: [PATCH 4/5] implemented the errors --- src/transactions/transactions.service.ts | 208 ++++++++++------------- 1 file changed, 94 insertions(+), 114 deletions(-) diff --git a/src/transactions/transactions.service.ts b/src/transactions/transactions.service.ts index ab7041dd..f5e7f7b4 100644 --- a/src/transactions/transactions.service.ts +++ b/src/transactions/transactions.service.ts @@ -41,148 +41,128 @@ export class TransactionsService { * Create a new transaction */ async create(dto: CreateTransactionDto): Promise { - try { - // Validate that property and users exist - const [property, buyer, seller] = await Promise.all([ - this.prisma.property.findUnique({ where: { id: dto.propertyId } }), - this.prisma.user.findUnique({ where: { id: dto.buyerId } }), - this.prisma.user.findUnique({ where: { id: dto.sellerId } }), - ]); - - if (!property) { - throw new NotFoundException('Property not found'); - } - if (!buyer) { - throw new NotFoundException('Buyer not found'); - } - if (!seller) { - throw new NotFoundException('Seller not found'); - } + // Validate that property and users exist + const [property, buyer, seller] = await Promise.all([ + this.prisma.property.findUnique({ where: { id: dto.propertyId } }), + this.prisma.user.findUnique({ where: { id: dto.buyerId } }), + this.prisma.user.findUnique({ where: { id: dto.sellerId } }), + ]); - const feeBreakdown = this.transactionFeesService.calculateFees(Number(dto.amount)); + if (!property) { + throw new NotFoundException('Property not found'); + } + if (!buyer) { + throw new NotFoundException('Buyer not found'); + } + if (!seller) { + throw new NotFoundException('Seller not found'); + } - const transaction = await this.prisma.transaction.create({ - data: { - propertyId: dto.propertyId, - buyerId: dto.buyerId, - sellerId: dto.sellerId, - amount: dto.amount, - type: dto.type as any, - status: 'PENDING', - notes: dto.notes, - feeBreakdown: feeBreakdown as any, - }, - }); + const feeBreakdown = this.transactionFeesService.calculateFees(Number(dto.amount)); + + const transaction = await this.prisma.transaction.create({ + data: { + propertyId: dto.propertyId, + buyerId: dto.buyerId, + sellerId: dto.sellerId, + amount: dto.amount, + type: dto.type as any, + status: 'PENDING', + notes: dto.notes, + feeBreakdown: feeBreakdown as any, + }, + }); - await this.commissionsService.createCommissionsForTransaction(transaction.id); + await this.commissionsService.createCommissionsForTransaction(transaction.id); - this.logger.log(`Transaction created: ${transaction.id}`); - return this.toResponseDto(transaction); - } catch (error) { - this.logger.error(`Failed to create transaction: ${error.message}`, error.stack); - throw error; - } + this.logger.log(`Transaction created: ${transaction.id}`); + return this.toResponseDto(transaction); } /** * Find all transactions with filtering and pagination */ async findAll(query: TransactionListQueryDto) { - try { - const page = query.page ?? 1; - const limit = query.limit ?? 20; - const skip = (page - 1) * limit; - - const where: any = {}; - if (query.propertyId) where.propertyId = query.propertyId; - if (query.buyerId) where.buyerId = query.buyerId; - if (query.sellerId) where.sellerId = query.sellerId; - if (query.status) where.status = query.status; - if (query.type) where.type = query.type; - - const [transactions, total] = await Promise.all([ - this.prisma.transaction.findMany({ - where, - skip, - take: limit, - include: { - property: { select: { id: true, title: true, address: true } }, - buyer: { select: { id: true, email: true, firstName: true, lastName: true } }, - seller: { select: { id: true, email: true, firstName: true, lastName: true } }, - }, - orderBy: { createdAt: 'desc' }, - }), - this.prisma.transaction.count({ where }), - ]); + const page = query.page ?? 1; + const limit = query.limit ?? 20; + const skip = (page - 1) * limit; + + const where: any = {}; + if (query.propertyId) where.propertyId = query.propertyId; + if (query.buyerId) where.buyerId = query.buyerId; + if (query.sellerId) where.sellerId = query.sellerId; + if (query.status) where.status = query.status; + if (query.type) where.type = query.type; + + const [transactions, total] = await Promise.all([ + this.prisma.transaction.findMany({ + where, + skip, + take: limit, + include: { + property: { select: { id: true, title: true, address: true } }, + buyer: { select: { id: true, email: true, firstName: true, lastName: true } }, + seller: { select: { id: true, email: true, firstName: true, lastName: true } }, + }, + orderBy: { createdAt: 'desc' }, + }), + this.prisma.transaction.count({ where }), + ]); - return { - total, - page, - limit, - items: transactions.map((t: any) => this.toResponseDto(t)), - }; - } catch (error) { - this.logger.error(`Failed to list transactions: ${error.message}`, error.stack); - throw error; - } + return { + total, + page, + limit, + items: transactions.map((t: any) => this.toResponseDto(t)), + }; } /** * Find a single transaction by ID */ async findOne(id: string): Promise { - try { - const transaction = await this.prisma.transaction.findUnique({ - where: { id }, - include: { - property: { select: { id: true, title: true, address: true } }, - buyer: { select: { id: true, email: true, firstName: true, lastName: true } }, - seller: { select: { id: true, email: true, firstName: true, lastName: true } }, - }, - }); - - if (!transaction) { - throw new NotFoundException('Transaction not found'); - } + const transaction = await this.prisma.transaction.findUnique({ + where: { id }, + include: { + property: { select: { id: true, title: true, address: true } }, + buyer: { select: { id: true, email: true, firstName: true, lastName: true } }, + seller: { select: { id: true, email: true, firstName: true, lastName: true } }, + }, + }); - return this.toResponseDto(transaction); - } catch (error) { - this.logger.error(`Failed to find transaction ${id}: ${error.message}`, error.stack); - throw error; + if (!transaction) { + throw new NotFoundException('Transaction not found'); } + + return this.toResponseDto(transaction); } /** * Update a transaction */ async update(id: string, dto: UpdateTransactionDto): Promise { - try { - const transaction = await this.prisma.transaction.findUnique({ - where: { id }, - }); - - if (!transaction) { - throw new NotFoundException('Transaction not found'); - } + const transaction = await this.prisma.transaction.findUnique({ + where: { id }, + }); - const updated = await this.prisma.transaction.update({ - where: { id }, - data: { - status: dto.status as any, - notes: dto.notes, - }, - }); + if (!transaction) { + throw new NotFoundException('Transaction not found'); + } - if (dto.status) { - await this.commissionsService.updateCommissionsStatus(id, dto.status); - } + const updated = await this.prisma.transaction.update({ + where: { id }, + data: { + status: dto.status as any, + notes: dto.notes, + }, + }); - this.logger.log(`Transaction updated: ${id}`); - return this.toResponseDto(updated); - } catch (error) { - this.logger.error(`Failed to update transaction ${id}: ${error.message}`, error.stack); - throw error; + if (dto.status) { + await this.commissionsService.updateCommissionsStatus(id, dto.status); } + + this.logger.log(`Transaction updated: ${id}`); + return this.toResponseDto(updated); } /** @@ -729,4 +709,4 @@ export class TransactionsService { private roundPercentage(value: number): number { return Math.round(value * 100) / 100; } -} +} \ No newline at end of file From a4738d32c62330f41cba2e19628fa5baa1553bb4 Mon Sep 17 00:00:00 2001 From: ummarig Date: Mon, 27 Jul 2026 06:43:51 +0100 Subject: [PATCH 5/5] implemented the errors --- src/transactions/transactions.service.ts | 233 +++++++++++------------ 1 file changed, 106 insertions(+), 127 deletions(-) diff --git a/src/transactions/transactions.service.ts b/src/transactions/transactions.service.ts index f5e7f7b4..a504bf39 100644 --- a/src/transactions/transactions.service.ts +++ b/src/transactions/transactions.service.ts @@ -169,118 +169,102 @@ export class TransactionsService { * Record transaction on blockchain */ async recordOnBlockchain(id: string, dto: RecordTransactionOnChainDto): Promise { - try { - const transaction = await this.prisma.transaction.findUnique({ - where: { id }, - include: { - buyer: true, - seller: true, - property: true, - }, - }); + const transaction = await this.prisma.transaction.findUnique({ + where: { id }, + include: { + buyer: true, + seller: true, + property: true, + }, + }); - if (!transaction) { - throw new NotFoundException('Transaction not found'); - } + if (!transaction) { + throw new NotFoundException('Transaction not found'); + } - if (transaction.blockchainHash) { - throw new BadRequestException('Transaction already recorded on blockchain'); - } + if (transaction.blockchainHash) { + throw new BadRequestException('Transaction already recorded on blockchain'); + } - // Get wallet addresses - use provided or fallback to placeholder - const buyerAddress = dto.buyerAddress || `0x${transaction.buyerId.substring(0, 40)}`; + // Get wallet addresses - use provided or fallback to placeholder + const buyerAddress = dto.buyerAddress || `0x${transaction.buyerId.substring(0, 40)}`; - const sellerAddress = dto.sellerAddress || `0x${transaction.sellerId.substring(0, 40)}`; + const sellerAddress = dto.sellerAddress || `0x${transaction.sellerId.substring(0, 40)}`; - // Validate addresses - if ( - !this.blockchainService.isValidAddress(buyerAddress) || - !this.blockchainService.isValidAddress(sellerAddress) - ) { - this.logger.warn(`Invalid addresses for transaction ${id}. Using fallback hashing.`); - } + // Validate addresses + if ( + !this.blockchainService.isValidAddress(buyerAddress) || + !this.blockchainService.isValidAddress(sellerAddress) + ) { + this.logger.warn(`Invalid addresses for transaction ${id}. Using fallback hashing.`); + } - // Record on blockchain - const blockchainRecord = await this.blockchainService.recordTransactionOnBlockchain({ - transactionId: id, - propertyId: transaction.propertyId, - buyerAddress, - sellerAddress, - amount: transaction.amount.toNumber(), - metadata: { - transactionType: transaction.type, - propertyAddress: transaction.property?.address, - }, - }); + // Record on blockchain + const blockchainRecord = await this.blockchainService.recordTransactionOnBlockchain({ + transactionId: id, + propertyId: transaction.propertyId, + buyerAddress, + sellerAddress, + amount: transaction.amount.toNumber(), + metadata: { + transactionType: transaction.type, + propertyAddress: transaction.property?.address, + }, + }); - // Update transaction with blockchain data - const updated = await this.prisma.transaction.update({ - where: { id }, - data: { - blockchainHash: blockchainRecord.blockchainHash, - contractAddress: blockchainRecord.contractAddress, - }, - }); + // Update transaction with blockchain data + const updated = await this.prisma.transaction.update({ + where: { id }, + data: { + blockchainHash: blockchainRecord.blockchainHash, + contractAddress: blockchainRecord.contractAddress, + }, + }); - this.logger.log( - `Transaction ${id} recorded on blockchain: ${blockchainRecord.blockchainHash}`, - ); + this.logger.log( + `Transaction ${id} recorded on blockchain: ${blockchainRecord.blockchainHash}`, + ); - return { - transaction: this.toResponseDto(updated), - blockchain: blockchainRecord, - }; - } catch (error) { - this.logger.error( - `Failed to record transaction on blockchain: ${error.message}`, - error.stack, - ); - throw error; - } + return { + transaction: this.toResponseDto(updated), + blockchain: blockchainRecord, + }; } /** * Verify transaction on blockchain */ async verifyOnBlockchain(id: string): Promise { - try { - const transaction = await this.prisma.transaction.findUnique({ - where: { id }, - }); + const transaction = await this.prisma.transaction.findUnique({ + where: { id }, + }); - if (!transaction) { - throw new NotFoundException('Transaction not found'); - } + if (!transaction) { + throw new NotFoundException('Transaction not found'); + } - if (!transaction.blockchainHash) { - throw new BadRequestException('Transaction not recorded on blockchain'); - } + if (!transaction.blockchainHash) { + throw new BadRequestException('Transaction not recorded on blockchain'); + } - const verification = await this.blockchainService.verifyBlockchainTransaction({ - transactionHash: transaction.blockchainHash, - }); + const verification = await this.blockchainService.verifyBlockchainTransaction({ + transactionHash: transaction.blockchainHash, + }); - // Update transaction status if verified and not already completed - if (verification.verified && verification.status === 'success') { - await this.prisma.transaction.update({ - where: { id }, - data: { - status: 'COMPLETED', - }, - }); - await this.commissionsService.updateCommissionsStatus(id, 'COMPLETED'); - } + // Update transaction status if verified and not already completed + if (verification.verified && verification.status === 'success') { + await this.prisma.transaction.update({ + where: { id }, + data: { + status: 'COMPLETED', + }, + }); + await this.commissionsService.updateCommissionsStatus(id, 'COMPLETED'); + } - this.logger.log(`Transaction ${id} verification result: ${verification.verified}`); + this.logger.log(`Transaction ${id} verification result: ${verification.verified}`); - return verification; - } catch (error) { - this.logger.error( - `Failed to verify transaction on blockchain: ${error.message}`, - error.stack, - ); - throw error; - } + return verification; } /** @@ -381,49 +365,44 @@ export class TransactionsService { status: string, actorId?: string, ): Promise { - try { - const transaction = await this.prisma.transaction.findUnique({ - where: { id: transactionId }, - }); + const transaction = await this.prisma.transaction.findUnique({ + where: { id: transactionId }, + }); - if (!transaction) { - throw new NotFoundException('Transaction not found'); - } + if (!transaction) { + throw new NotFoundException('Transaction not found'); + } - // Enforce status lifecycle (#557) - const currentStatus = transaction.status as TransactionStatus; - const nextStatus = status as TransactionStatus; - if (!canTransitionTransactionStatus(currentStatus, nextStatus)) { - throw new BadRequestException( - `Invalid status transition from "${currentStatus}" to "${nextStatus}"`, - ); - } + // Enforce status lifecycle (#557) + const currentStatus = transaction.status as TransactionStatus; + const nextStatus = status as TransactionStatus; + if (!canTransitionTransactionStatus(currentStatus, nextStatus)) { + throw new BadRequestException( + `Invalid status transition from "${currentStatus}" to "${nextStatus}"`, + ); + } - const updated = await this.prisma.transaction.update({ - where: { id: transactionId }, - data: { status: status as any }, - }); + const updated = await this.prisma.transaction.update({ + where: { id: transactionId }, + data: { status: status as any }, + }); - // Audit log the transition (#557) - await this.transactionAuditService.log( - transactionId, - 'STATUS_TRANSITION', - { status: currentStatus }, - { status: nextStatus }, - { actorId }, - ); + // Audit log the transition (#557) + await this.transactionAuditService.log( + transactionId, + 'STATUS_TRANSITION', + { status: currentStatus }, + { status: nextStatus }, + { actorId }, + ); - await this.commissionsService.updateCommissionsStatus(transactionId, status); + await this.commissionsService.updateCommissionsStatus(transactionId, status); - // Auto-create timeline stage event (#560) - await this.timelineService.addStageEvent(transactionId, status); + // Auto-create timeline stage event (#560) + await this.timelineService.addStageEvent(transactionId, status); - this.logger.log(`Transaction ${transactionId} status updated to ${status}`); - return this.toResponseDto(updated); - } catch (error) { - this.logger.error(`Failed to update transaction status: ${error.message}`, error.stack); - throw error; - } + this.logger.log(`Transaction ${transactionId} status updated to ${status}`); + return this.toResponseDto(updated); } /**