feat/working on update card control

This commit is contained in:
Abdalhamid Alhamad
2025-07-14 11:57:51 +03:00
parent 038b8ef6e3
commit 5a780eeb17
15 changed files with 263 additions and 28 deletions
@@ -1,16 +1,22 @@
import { Controller, Get } from '@nestjs/common';
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ApiTags } from '@nestjs/swagger';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { IJwtPayload } from '~/auth/interfaces';
import { CardService } from '~/card/services';
import { AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard } from '~/common/guards';
import { ApiDataResponse } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils';
import { Customer } from '~/customer/entities';
import { CustomerResponseDto } from '~/customer/dtos/response';
import { CustomerService } from '~/customer/services';
import { UpdateCardControlsRequestDto } from '../dtos/requests';
import { CreateApplicationResponse, InquireApplicationResponse } from '../dtos/response';
import { NeoLeapService } from '../services/neoleap.service';
@Controller('neotest')
@ApiTags('Neoleap Test API , for testing purposes only, will be removed in production')
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
export class NeoTestController {
constructor(
private readonly neoleapService: NeoLeapService,
@@ -19,21 +25,38 @@ export class NeoTestController {
private readonly configService: ConfigService,
) {}
@Get('inquire-application')
@Post('update-kys')
@ApiDataResponse(CustomerResponseDto)
async updateKys(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.updateKyc(user.sub);
return ResponseFactory.data(new CustomerResponseDto(customer));
}
@Post('inquire-application')
@ApiDataResponse(InquireApplicationResponse)
async inquireApplication() {
const data = await this.neoleapService.inquireApplication('15');
async inquireApplication(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.findCustomerById(user.sub);
const data = await this.neoleapService.inquireApplication(customer.waitingNumber.toString());
return ResponseFactory.data(data);
}
@Get('create-application')
@Post('create-application')
@ApiDataResponse(CreateApplicationResponse)
async createApplication() {
const customer = await this.customerService.findCustomerById(
this.configService.get<string>('MOCK_CUSTOMER_ID', 'ff462af1-1e0c-4216-8865-738e5b525ac1'),
);
const data = await this.neoleapService.createApplication(customer as Customer);
async createApplication(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.findCustomerById(user.sub);
const data = await this.neoleapService.createApplication(customer);
await this.cardService.createCard(customer.id, data);
return ResponseFactory.data(data);
}
@Post('update-card-controls')
async updateCardControls(
@AuthenticatedUser() user: IJwtPayload,
@Body() { amount, count }: UpdateCardControlsRequestDto,
) {
const card = await this.cardService.getActiveCardForCustomer(user.sub);
await this.neoleapService.updateCardControl(card.cardReference, amount, count);
return ResponseFactory.data({ message: 'Card controls updated successfully' });
}
}
@@ -1,2 +1,3 @@
export * from './account-transaction-webhook.request.dto';
export * from './card-transaction-webhook.request.dto';
export * from './update-card-controls.request.dto';
@@ -0,0 +1,15 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsNumber, IsOptional, IsPositive } from 'class-validator';
export class UpdateCardControlsRequestDto {
@ApiProperty()
@IsNumber()
@IsPositive()
amount!: number;
@IsNumber()
@IsPositive()
@IsOptional()
@ApiPropertyOptional()
count?: number;
}
@@ -1,2 +1,3 @@
export * from './create-application.response.dto';
export * from './inquire-application.response';
export * from './update-card-controls.response.dto';
@@ -0,0 +1 @@
export class UpdateCardControlsResponseDto {}
@@ -1,3 +1,4 @@
export * from './create-application.request.interface';
export * from './inquire-application.request.interface';
export * from './neoleap-header.request.interface';
export * from './update-card-control.request.interface';
@@ -0,0 +1,77 @@
import { INeoleapHeaderRequest } from './neoleap-header.request.interface';
export interface IUpdateCardControlRequest extends INeoleapHeaderRequest {
UpdateCardControlsRequestDetails: {
InstitutionCode: string;
CardIdentifier: {
Id: string;
InstitutionCode: string;
};
UsageTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosDailyLimit?: {
AmountLimit: number;
CountLimit: number;
lenient?: boolean;
};
InternationalPosDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalPosMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalPosTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
};
}
@@ -6,10 +6,15 @@ import moment from 'moment';
import { v4 as uuid } from 'uuid';
import { CountriesNumericISO } from '~/common/constants';
import { Customer } from '~/customer/entities';
import { Gender } from '~/customer/enums';
import { Gender, KycStatus } from '~/customer/enums';
import { CREATE_APPLICATION_MOCK, INQUIRE_APPLICATION_MOCK } from '../__mocks__/';
import { CreateApplicationResponse, InquireApplicationResponse } from '../dtos/response';
import { ICreateApplicationRequest, IInquireApplicationRequest, INeoleapHeaderRequest } from '../interfaces';
import { CreateApplicationResponse, InquireApplicationResponse, UpdateCardControlsResponseDto } from '../dtos/response';
import {
ICreateApplicationRequest,
IInquireApplicationRequest,
INeoleapHeaderRequest,
IUpdateCardControlRequest,
} from '../interfaces';
@Injectable()
export class NeoLeapService {
private readonly baseUrl: string;
@@ -26,9 +31,15 @@ export class NeoLeapService {
async createApplication(customer: Customer) {
const responseKey = 'CreateNewApplicationResponseDetails';
// if (customer.cards.length > 0) {
// throw new BadRequestException('CUSTOMER.ALREADY_HAS_CARD');
// }
if (customer.kycStatus !== KycStatus.APPROVED) {
throw new BadRequestException('CUSTOMER.KYC_NOT_APPROVED');
}
if (customer.cards.length > 0) {
throw new BadRequestException('CUSTOMER.ALREADY_HAS_CARD');
}
if (!this.useGateway) {
return plainToInstance(CreateApplicationResponse, CREATE_APPLICATION_MOCK[responseKey], {
excludeExtraneousValues: true,
@@ -132,6 +143,35 @@ export class NeoLeapService {
);
}
async updateCardControl(cardId: string, amount: number, count?: number) {
const responseKey = 'UpdateCardControlResponseDetails';
if (!this.useGateway) {
return;
}
const payload: IUpdateCardControlRequest = {
UpdateCardControlsRequestDetails: {
InstitutionCode: this.institutionCode,
CardIdentifier: {
InstitutionCode: this.institutionCode,
Id: cardId,
},
UsageTransactionLimit: {
AmountLimit: amount,
CountLimit: count || 10,
},
},
RequestHeader: this.prepareHeaders('UpdateCardControl'),
};
return this.sendRequestToNeoLeap<IUpdateCardControlRequest, UpdateCardControlsResponseDto>(
'cardcontrol/UpdateCardControl',
payload,
responseKey,
UpdateCardControlsResponseDto,
);
}
private prepareHeaders(serviceName: string): INeoleapHeaderRequest['RequestHeader'] {
return {
Version: '1.0.0',
@@ -156,7 +196,7 @@ export class NeoLeapService {
},
});
if (data.data?.ResponseHeader?.ResponseCode !== '000' || !data.data[responseKey]) {
if (data.data?.ResponseHeader?.ResponseCode !== '000') {
throw new BadRequestException(
data.data.ResponseHeader.ResponseDescription || 'Unexpected Error from Service Provider',
);