mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-08-25 13:49:40 +00:00
refactor: handle kyc journey for customers
This commit is contained in:
@ -1,9 +1,16 @@
|
||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
||||
import { Transactional } from 'typeorm-transactional';
|
||||
import { DocumentService, OciService } from '~/document/services';
|
||||
import { GuardianService } from '~/guardian/services';
|
||||
import { CreateJuniorRequestDto } from '~/junior/dtos/request';
|
||||
import { DeviceService } from '~/user/services';
|
||||
import { UpdateCustomerRequestDto, UpdateNotificationsSettingsRequestDto } from '../dtos/request';
|
||||
import {
|
||||
CreateCustomerRequestDto,
|
||||
CustomerFiltersRequestDto,
|
||||
RejectCustomerKycRequestDto,
|
||||
UpdateCustomerRequestDto,
|
||||
} from '../dtos/request';
|
||||
import { Customer } from '../entities';
|
||||
import { KycStatus } from '../enums';
|
||||
import { CustomerRepository } from '../repositories/customer.repository';
|
||||
|
||||
@Injectable()
|
||||
@ -12,26 +19,9 @@ export class CustomerService {
|
||||
constructor(
|
||||
private readonly customerRepository: CustomerRepository,
|
||||
private readonly ociService: OciService,
|
||||
private readonly deviceService: DeviceService,
|
||||
private readonly documentService: DocumentService,
|
||||
private readonly guardianService: GuardianService,
|
||||
) {}
|
||||
async updateNotificationSettings(userId: string, data: UpdateNotificationsSettingsRequestDto, deviceId: string) {
|
||||
this.logger.log(`Updating notification settings for user ${userId}`);
|
||||
const customer = await this.findCustomerById(userId);
|
||||
|
||||
const notificationSettings = (await this.customerRepository.updateNotificationSettings(customer, data))
|
||||
.notificationSettings;
|
||||
|
||||
if (data.isPushEnabled && deviceId) {
|
||||
this.logger.log(`Updating device ${deviceId} with fcmToken`);
|
||||
await this.deviceService.updateDevice(deviceId, {
|
||||
fcmToken: data.fcmToken,
|
||||
userId: userId,
|
||||
});
|
||||
}
|
||||
this.logger.log(`Notification settings updated for user ${userId}`);
|
||||
return notificationSettings;
|
||||
}
|
||||
|
||||
async updateCustomer(userId: string, data: UpdateCustomerRequestDto): Promise<Customer> {
|
||||
this.logger.log(`Updating customer ${userId}`);
|
||||
@ -42,14 +32,12 @@ export class CustomerService {
|
||||
return this.findCustomerById(userId);
|
||||
}
|
||||
|
||||
createJuniorCustomer(guardianId: string, juniorId: string, body: CreateJuniorRequestDto) {
|
||||
this.logger.log(`Creating customer for user ${juniorId}`);
|
||||
return this.customerRepository.createJuniorCustomer(guardianId, juniorId, body);
|
||||
}
|
||||
async createJuniorCustomer(guardianId: string, juniorId: string, body: CreateJuniorRequestDto) {
|
||||
this.logger.log(`Creating junior customer for user ${juniorId}`);
|
||||
|
||||
createGuardianCustomer(userId: string) {
|
||||
this.logger.log(`Creating guardian customer for user ${userId}`);
|
||||
return this.customerRepository.createGuardianCustomer(userId);
|
||||
await this.validateCivilIdForCustomer(guardianId, body.civilIdFrontId, body.civilIdBackId);
|
||||
|
||||
return this.customerRepository.createCustomer(juniorId, body, false);
|
||||
}
|
||||
|
||||
async findCustomerById(id: string) {
|
||||
@ -69,6 +57,61 @@ export class CustomerService {
|
||||
return customer;
|
||||
}
|
||||
|
||||
async approveKycForCustomer(customerId: string) {
|
||||
const customer = await this.findCustomerById(customerId);
|
||||
|
||||
if (customer.kycStatus === KycStatus.APPROVED) {
|
||||
this.logger.error(`Customer ${customerId} is already approved`);
|
||||
throw new BadRequestException('CUSTOMER.ALREADY_APPROVED');
|
||||
}
|
||||
|
||||
this.logger.debug(`Approving KYC for customer ${customerId}`);
|
||||
await this.customerRepository.updateCustomer(customerId, { kycStatus: KycStatus.APPROVED, rejectionReason: null });
|
||||
this.logger.log(`KYC approved for customer ${customerId}`);
|
||||
}
|
||||
|
||||
findCustomers(filters: CustomerFiltersRequestDto) {
|
||||
this.logger.log(`Finding customers with filters ${JSON.stringify(filters)}`);
|
||||
return this.customerRepository.findCustomers(filters);
|
||||
}
|
||||
|
||||
@Transactional()
|
||||
async createGuardianCustomer(userId: string, body: CreateCustomerRequestDto) {
|
||||
this.logger.log(`Creating guardian customer for user ${userId}`);
|
||||
const existingCustomer = await this.customerRepository.findOne({ id: userId });
|
||||
|
||||
if (existingCustomer) {
|
||||
this.logger.error(`Customer ${userId} already exists`);
|
||||
throw new BadRequestException('CUSTOMER.ALRADY_EXISTS');
|
||||
}
|
||||
|
||||
await this.validateCivilIdForCustomer(userId, body.civilIdFrontId, body.civilIdBackId);
|
||||
|
||||
const customer = await this.customerRepository.createCustomer(userId, body, true);
|
||||
this.logger.log(`customer created for user ${userId}`);
|
||||
|
||||
await this.guardianService.createGuardian(customer.id);
|
||||
this.logger.log(`Guardian created for customer ${customer.id}`);
|
||||
|
||||
return customer;
|
||||
}
|
||||
|
||||
async rejectKycForCustomer(customerId: string, { reason }: RejectCustomerKycRequestDto) {
|
||||
const customer = await this.findCustomerById(customerId);
|
||||
|
||||
if (customer.kycStatus === KycStatus.REJECTED) {
|
||||
this.logger.error(`Customer ${customerId} is already rejected`);
|
||||
throw new BadRequestException('CUSTOMER.ALREADY_REJECTED');
|
||||
}
|
||||
|
||||
this.logger.debug(`Rejecting KYC for customer ${customerId}`);
|
||||
await this.customerRepository.updateCustomer(customerId, {
|
||||
kycStatus: KycStatus.REJECTED,
|
||||
rejectionReason: reason,
|
||||
});
|
||||
this.logger.log(`KYC rejected for customer ${customerId}`);
|
||||
}
|
||||
|
||||
private async validateProfilePictureForCustomer(userId: string, profilePictureId?: string) {
|
||||
if (!profilePictureId) return;
|
||||
|
||||
@ -86,4 +129,37 @@ export class CustomerService {
|
||||
throw new BadRequestException('DOCUMENT.NOT_CREATED_BY_USER');
|
||||
}
|
||||
}
|
||||
|
||||
private async validateCivilIdForCustomer(userId: string, civilIdFrontId: string, civilIdBackId: string) {
|
||||
this.logger.log(`Validating customer documents`);
|
||||
|
||||
if (!civilIdFrontId || !civilIdBackId) {
|
||||
this.logger.error('Civil id front and back are required');
|
||||
throw new BadRequestException('CUSTOMER.CIVIL_ID_REQUIRED');
|
||||
}
|
||||
|
||||
const [civilIdFront, civilIdBack] = await Promise.all([
|
||||
this.documentService.findDocumentById(civilIdFrontId),
|
||||
this.documentService.findDocumentById(civilIdBackId),
|
||||
]);
|
||||
|
||||
if (!civilIdFront || !civilIdBack) {
|
||||
this.logger.error('Civil id front or back not found');
|
||||
throw new BadRequestException('CUSTOMER.CIVIL_ID_REQUIRED');
|
||||
}
|
||||
|
||||
if (civilIdFront.createdById !== userId || civilIdBack.createdById !== userId) {
|
||||
this.logger.error(`Civil id front or back not created by user with id ${userId}`);
|
||||
throw new BadRequestException('CUSTOMER.CIVIL_ID_NOT_CREATED_BY_USER');
|
||||
}
|
||||
|
||||
const customerWithTheSameId = await this.customerRepository.findCustomerByCivilId(civilIdFrontId, civilIdBackId);
|
||||
|
||||
if (customerWithTheSameId) {
|
||||
this.logger.error(
|
||||
`Customer with civil id front ${civilIdFrontId} and civil id back ${civilIdBackId} already exists`,
|
||||
);
|
||||
throw new BadRequestException('CUSTOMER.CIVIL_ID_ALREADY_EXISTS');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user