Skip to content

Commit

Permalink
[NestJS] Milestone 2: ID Verification
Browse files Browse the repository at this point in the history
  • Loading branch information
javiertoledo committed Apr 14, 2023
1 parent f9e5ad2 commit 224b56c
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 1 deletion.
2 changes: 2 additions & 0 deletions kyc-nest/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProfileModule } from './profile/profile.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { KycModule } from './kyc/kyc.module';

@Module({
imports: [
Expand All @@ -13,6 +14,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
synchronize: true,
}),
ProfileModule,
KycModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
13 changes: 13 additions & 0 deletions kyc-nest/src/kyc/kyc.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Controller, Post, Body } from '@nestjs/common';
import { KYCService } from './kyc.service';
import { WebhookMessage } from './webhook-message.interface';

@Controller('kyc')
export class KYCController {
constructor(private readonly kycService: KYCService) {}

@Post('webhook')
async handleWebhook(@Body() message: WebhookMessage): Promise<void> {
await this.kycService.handleWebhook(message);
}
}
11 changes: 11 additions & 0 deletions kyc-nest/src/kyc/kyc.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { KYCService } from './kyc.service';
import { KYCController } from './kyc.controller';
import { ProfileModule } from 'src/profile/profile.module';

@Module({
imports: [ProfileModule],
providers: [KYCService],
controllers: [KYCController],
})
export class KycModule {}
31 changes: 31 additions & 0 deletions kyc-nest/src/kyc/kyc.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@nestjs/common';
import { WebhookMessage } from './webhook-message.interface';
import { ProfileService } from '../profile/profile.service';

@Injectable()
export class KYCService {
constructor(private readonly profileService: ProfileService) {}

async handleWebhook(message: WebhookMessage): Promise<void> {
// In a real application, you should verify a signature of the message here
const userId = message.userId;

console.log('Received webhook message:', message);

if (message.result === 'success') {
await this.profileService.update(userId, {
kycStatus: 'KYCIDVerified',
idVerificationId: message.verificationId,
idVerifiedAt: message.timestamp,
});
} else if (message.result === 'rejected') {
await this.profileService.update(userId, {
kycStatus: 'KYCIDRejected',
idVerificationId: message.verificationId,
idRejectedAt: message.timestamp,
});
} else {
console.error('Unknown ID verification result:', message.result);
}
}
}
30 changes: 30 additions & 0 deletions kyc-nest/src/kyc/webhook-message.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Webhook message interface that is sent by the ID verification service. This service is simulated
* in this project, so it lacks a signature which is normally used to verify the authenticity of
* the request.
*
* Some examples of messages are:
*
* Successful verification:
*
* {
* "userId": "5f9f1b9b-7b1e-4b9f-9f1b-9b7b1e4b9f9f",
* "result": "success",
* "verificationId": "5f9f1b9b-7b1e-4b9f-9f1b-9b7b1e4b9f9f",
* "timestamp": "2020-01-01T00:00:00.000Z"
* }
*
* Rejected verification:
* {
* "userId": "5f9f1b9b-7b1e-4b9f-9f1b-9b7b1e4b9f9f",
* "result": "rejected",
* "verificationId": "5f9f1b9b-7b1e-4b9f-9f1b-9b7b1e4b9f9f",
* "timestamp": "2020-01-01T00:00:00.000Z"
* }
*/
export interface WebhookMessage {
userId: string;
result: 'success' | 'rejected';
verificationId: string;
timestamp: string;
}
11 changes: 10 additions & 1 deletion kyc-nest/src/profile/profile.entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

export type KYCStatus = 'KYCPending';
export type KYCStatus = 'KYCPending' | 'KYCIDVerified' | 'KYCIDRejected';

@Entity()
export class Profile {
Expand Down Expand Up @@ -42,4 +42,13 @@ export class Profile {

@Column({ default: 'KYCPending' })
kycStatus: KYCStatus;

@Column({ nullable: true })
idVerificationId?: string;

@Column({ nullable: true })
idVerifiedAt?: string;

@Column({ nullable: true })
idRejectedAt?: string;
}
1 change: 1 addition & 0 deletions kyc-nest/src/profile/profile.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import { Profile } from './profile.entity';
imports: [TypeOrmModule.forFeature([Profile])],
controllers: [ProfileController],
providers: [ProfileService],
exports: [ProfileService],
})
export class ProfileModule {}
28 changes: 28 additions & 0 deletions kyc-nest/src/profile/profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ export class ProfileService {
return newProfile;
}

async update(userId: string, profileData: Partial<Profile>): Promise<void> {
const profile = await this.findById(userId);

if (
profileData.kycStatus &&
!this.isValidTransition(profile.kycStatus, profileData.kycStatus)
) {
throw new BadRequestException(
`Invalid status transition from '${profile.kycStatus}' to '${profileData.kycStatus}'`,
);
}

await this.profileRepository.update(userId, profileData);
}

async findAll(): Promise<Profile[]> {
return this.profileRepository.find();
}
Expand All @@ -35,4 +50,17 @@ export class ProfileService {
}
return profile;
}

private isValidTransition(
currentState: KYCStatus,
newState: KYCStatus,
): boolean {
const allowedTransitions: Record<KYCStatus, Array<KYCStatus>> = {
KYCPending: ['KYCIDVerified', 'KYCIDRejected'],
KYCIDVerified: [],
KYCIDRejected: [],
};

return allowedTransitions[currentState]?.includes(newState);
}
}

0 comments on commit 224b56c

Please sign in to comment.