From 8497a5779da7f3adad74cbdd9dd8b817edde6631 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 10 Jun 2025 15:02:43 +0700 Subject: [PATCH 1/3] feat: fix validation generate otp --- src/core/helpers/otp/otp-service.ts | 8 +++++-- .../data/services/otp-verification.service.ts | 22 +++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/core/helpers/otp/otp-service.ts b/src/core/helpers/otp/otp-service.ts index 9b0e33e..0ba5e26 100644 --- a/src/core/helpers/otp/otp-service.ts +++ b/src/core/helpers/otp/otp-service.ts @@ -38,6 +38,10 @@ export class OtpService { return Object.values(counts).some((count) => count > 2); } + private hasMatchLength(str: string) { + return str.length !== this.otpLength; + } + public generateSecureOTP(): string { let otp: string; @@ -46,11 +50,11 @@ export class OtpService { Math.floor(Math.random() * 10).toString(), ).join(''); } while ( + this.hasMatchLength(otp) || this.hasSequentialDigits(otp) || this.hasRepeatedDigits(otp) || this.isPalindrome(otp) || - this.hasPartiallyRepeatedDigits(otp) || - otp?.length < this.otpLength + this.hasPartiallyRepeatedDigits(otp) ); return otp; } diff --git a/src/modules/configuration/otp-verification/data/services/otp-verification.service.ts b/src/modules/configuration/otp-verification/data/services/otp-verification.service.ts index 11420d2..148325e 100644 --- a/src/modules/configuration/otp-verification/data/services/otp-verification.service.ts +++ b/src/modules/configuration/otp-verification/data/services/otp-verification.service.ts @@ -74,7 +74,9 @@ export class OtpVerificationService { const createdAtMoment = moment(Number(activeOTP.created_at)); const nowMoment = moment(Number(dateNow)); const diffSeconds = nowMoment.diff(createdAtMoment, 'seconds'); - if (diffSeconds < 60) { + const isProduction = process.env.NODE_ENV === 'true'; + + if (diffSeconds < 60 && isProduction) { throw new BadRequestException( 'An active OTP request was made recently. Please try again later.', ); @@ -116,10 +118,13 @@ export class OtpVerificationService { ); } + let otp: any; + // Build a where condition with OR between target_id and reference - const otp = await this.otpVerificationRepo.findOne({ - where: [ - { + + if (target_id) { + otp = await this.otpVerificationRepo.findOne({ + where: { otp_code, action_type, target_id, @@ -127,7 +132,10 @@ export class OtpVerificationService { is_used: false, is_replaced: false, }, - { + }); + } else if (reference) { + otp = await this.otpVerificationRepo.findOne({ + where: { otp_code, action_type, reference, @@ -135,8 +143,8 @@ export class OtpVerificationService { is_used: false, is_replaced: false, }, - ], - }); + }); + } if (!otp) { throw new BadRequestException('Invalid or expired OTP.'); From 16df6945b7564f88bc9916f9149b68f550b474c8 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 10 Jun 2025 15:17:35 +0700 Subject: [PATCH 2/3] feat: fix validation generate otp --- src/core/helpers/otp/otp-service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/helpers/otp/otp-service.ts b/src/core/helpers/otp/otp-service.ts index 0ba5e26..0b4e3e1 100644 --- a/src/core/helpers/otp/otp-service.ts +++ b/src/core/helpers/otp/otp-service.ts @@ -38,10 +38,14 @@ export class OtpService { return Object.values(counts).some((count) => count > 2); } - private hasMatchLength(str: string) { + private hasNoMatchLength(str: string) { return str.length !== this.otpLength; } + private hasStartWithZero(str: string) { + return str.split('')[0] === '0'; + } + public generateSecureOTP(): string { let otp: string; @@ -50,11 +54,12 @@ export class OtpService { Math.floor(Math.random() * 10).toString(), ).join(''); } while ( - this.hasMatchLength(otp) || + this.hasNoMatchLength(otp) || this.hasSequentialDigits(otp) || this.hasRepeatedDigits(otp) || this.isPalindrome(otp) || - this.hasPartiallyRepeatedDigits(otp) + this.hasPartiallyRepeatedDigits(otp) || + this.hasStartWithZero(otp) ); return otp; } From 94fbec0c78751222fbac6e4cb0f1107736ce353f Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:02:05 +0700 Subject: [PATCH 3/3] feat: save otp code when reject reconciliation --- .../managers/cancel-reconciliation.manager.ts | 9 +++++++++ .../usecases/reconciliation-data.orchestrator.ts | 3 ++- .../infrastructure/dto/cancel-top-dto.ts | 14 ++++++++++++++ .../reconciliation-data.controller.ts | 8 ++++++-- 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/modules/transaction/reconciliation/infrastructure/dto/cancel-top-dto.ts diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts index 0782fa6..71a71be 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts @@ -15,6 +15,12 @@ import { TransactionEntity } from 'src/modules/transaction/transaction/domain/en @Injectable() export class CancelReconciliationManager extends BaseUpdateStatusManager { + protected payloadBody: any; + + setCustomBodyRequest(body) { + this.payloadBody = body; + } + getResult(): string { return `Success active data ${this.result.id}`; } @@ -50,6 +56,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager { if (this.data.is_recap_transaction) { Object.assign(this.data, { + otp_code: this.payloadBody?.otp_code, reconciliation_confirm_by: null, reconciliation_confirm_date: null, reconciliation_status: STATUS.PENDING, @@ -58,6 +65,7 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager { + async cancel(dataId, body): Promise { this.cancelManager.setData(dataId, STATUS.REJECTED); this.cancelManager.setService(this.serviceData, TABLE_NAME.TRANSACTION); + this.cancelManager.setCustomBodyRequest(body); await this.cancelManager.execute(); return this.cancelManager.getResult(); } diff --git a/src/modules/transaction/reconciliation/infrastructure/dto/cancel-top-dto.ts b/src/modules/transaction/reconciliation/infrastructure/dto/cancel-top-dto.ts new file mode 100644 index 0000000..042a863 --- /dev/null +++ b/src/modules/transaction/reconciliation/infrastructure/dto/cancel-top-dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class OtpVerifyDto { + @ApiProperty({ + name: 'otp_code', + type: String, + required: true, + example: '2345', + }) + @IsString() + @IsNotEmpty() + otp_code: string; +} diff --git a/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts b/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts index c983d2f..4f76225 100644 --- a/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts +++ b/src/modules/transaction/reconciliation/infrastructure/reconciliation-data.controller.ts @@ -16,6 +16,7 @@ import { Public } from 'src/core/guards'; import { TransactionEntity } from '../../transaction/domain/entities/transaction.entity'; import { UpdateReconciliationDto } from './dto/reconciliation.dto'; import { RecapReconciliationDto } from './dto/recap.dto'; +import { OtpVerifyDto } from './dto/cancel-top-dto'; @ApiTags(`${MODULE_NAME.RECONCILIATION.split('-').join(' ')} - data`) @Controller(`v1/${MODULE_NAME.RECONCILIATION}`) @@ -40,8 +41,11 @@ export class ReconciliationDataController { } @Patch(':id/cancel') - async cancel(@Param('id') dataId: string): Promise { - return await this.orchestrator.cancel(dataId); + async cancel( + @Param('id') dataId: string, + @Body() body: OtpVerifyDto, + ): Promise { + return await this.orchestrator.cancel(dataId, body); } @Put('/batch-cancel')