Skip to content

Commit 68d87dd

Browse files
committed
feat(src): add tier 1 module
1 parent 086259b commit 68d87dd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3290
-6
lines changed

env/.develop.env

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
NODE_ENV=develop
22

33
## SERVER
4-
PORT=3001
4+
PORT=3007
55

66
## DATABASE
77
POSTGRES_DATABASE=fuap_dev
@@ -11,7 +11,7 @@ POSTGRES_USER=fuap
1111
POSTGRES_PASSWORD=fuap_dev
1212
MODE=DEV
1313
SYNCHRONIZE=true
14-
LOGGING=error, query,
14+
LOGGING='error, query',
1515

1616
## JWT
1717
AUTH_EXPIRED=24h
@@ -22,3 +22,7 @@ THROTTLER_NAME=THROTTLER
2222
THROTTLER_TTL=60000
2323
THROTTLER_LIMIT=10
2424

25+
## EQUIFAX
26+
EQUIFAX_BASE_URL=https://api.sandbox.equifax.com
27+
EQUIFAX_CLIENT_ID=185b5c5d9ada76aed787998acb9ac4f284c689993c6a24ff5249d4796717879ee1003eb44f5cfdf0c4049815ade39060
28+
EQUIFAX_CLIENT_SECRET=94cecd343affc1b67480c8e3b6e8819f9a9923c3d4f46c562193335deb871da9

env/.production.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ THROTTLER_NAME=THROTTLER
2222
THROTTLER_TTL=60000
2323
THROTTLER_LIMIT=10
2424

25+
## EQUIFAX
26+
EQUIFAX_BASE_URL=https://api.sandbox.equifax.com
27+
EQUIFAX_CLIENT_ID=185b5c5d9ada76aed787998acb9ac4f284c689993c6a24ff5249d4796717879ee1003eb44f5cfdf0c4049815ade39060
28+
EQUIFAX_CLIENT_SECRET=94cecd343affc1b67480c8e3b6e8819f9a9923c3d4f46c562193335deb871da9

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
"reflect-metadata": "^0.2.2",
5050
"rxjs": "^7.8.1",
5151
"typeorm": "^0.3.20",
52-
"uuid": "^10.0.0"
52+
"uuid": "^10.0.0",
53+
"xml2js": "^0.6.2"
5354
},
5455
"devDependencies": {
5556
"@nestjs/cli": "^10.3.2",

src/app.module.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,20 @@ import { CreditCardModule } from './modules/credit-card/credit-card.module';
4343
import { HomeLoanModule } from './modules/home-loan/home-loan.module';
4444
import { BridgeLoanModule } from './modules/bridge-loan/bridge-loan.module';
4545
import { WebhookModule } from './modules/webhook/webhook.module';
46+
import equifaxConfig from './config/equifax.config';
47+
import { EquifaxModule } from './modules/equifax/equifax.module';
4648

4749
@Module({
4850
imports: [
4951
ConfigModule.forRoot({
5052
envFilePath: `./env/.${process.env.NODE_ENV}.env`,
51-
load: [serverConfig, databaseConfig, jwtConfig, throttlerConfig],
53+
load: [
54+
serverConfig,
55+
databaseConfig,
56+
jwtConfig,
57+
throttlerConfig,
58+
equifaxConfig,
59+
],
5260
isGlobal: true,
5361
}),
5462
TypeOrmModule.forRootAsync({
@@ -126,6 +134,7 @@ import { WebhookModule } from './modules/webhook/webhook.module';
126134
HomeLoanModule,
127135
BridgeLoanModule,
128136
WebhookModule,
137+
EquifaxModule,
129138
],
130139
providers: [
131140
{

src/common/adapters/axios.adapter.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,53 @@ export class AxiosAdapter implements HttpAdapter {
1212

1313
return data;
1414
} catch (error) {
15-
throw new Error(`error: ${error}`);
15+
throw new Error(
16+
`error: ${error?.response?.data ?? error?.message ?? error}`,
17+
);
18+
}
19+
}
20+
21+
async post<T>(
22+
url: string,
23+
data?: any,
24+
config?: AxiosRequestConfig,
25+
): Promise<T> {
26+
try {
27+
const { data: responseData } = await this.axios.post<T>(
28+
url,
29+
data,
30+
config,
31+
);
32+
return responseData;
33+
} catch (error) {
34+
const err = `${error?.response?.data ?? error?.message ?? error}`;
35+
throw new Error(`error: ${err}`);
36+
}
37+
}
38+
39+
async put<T>(
40+
url: string,
41+
data?: any,
42+
config?: AxiosRequestConfig,
43+
): Promise<T> {
44+
try {
45+
const { data: responseData } = await this.axios.put<T>(url, data, config);
46+
return responseData;
47+
} catch (error) {
48+
throw new Error(
49+
`error: ${error?.response?.data ?? error?.message ?? error}`,
50+
);
51+
}
52+
}
53+
54+
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
55+
try {
56+
const { data } = await this.axios.delete<T>(url, config);
57+
return data;
58+
} catch (error) {
59+
throw new Error(
60+
`error: ${error?.response?.data ?? error?.message ?? error}`,
61+
);
1662
}
1763
}
1864
}

src/common/constans/urls.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export const URLs = {
2+
equifax: {
3+
oauth: {
4+
token: '/v1/oauth/token',
5+
tokenV2: '/v2/oauth/token',
6+
},
7+
business: {
8+
asset: {
9+
scope: '/business/asset-view/v1',
10+
initiate: '/business/verification-of-assets/v1/initiate',
11+
retrieve: '/business/asset-view/v1/retrieve',
12+
refresh: '/business/asset-view/v1/refresh',
13+
},
14+
scoreAttributes: {
15+
scope: 'https://api.equifax.com/business/scores-and-attributes/v2',
16+
creditReport: '/business/scores-and-attributes/v2/credit-report',
17+
},
18+
bankTransactionData: {
19+
scope: 'https://api.equifax.com/business/bankingandlending/v1',
20+
userRegister: '/business/bankingandlending/v1/user-registrations',
21+
},
22+
oneView: {
23+
scope: 'https://api.equifax.com/business/oneview/consumer-credit/v1',
24+
consumerCredit:
25+
'/business/oneview/consumer-credit/v1/reports/credit-report',
26+
},
27+
chexAdvisor: {
28+
scope: '/business/datax/v1',
29+
request: '/business/datax/v1/core',
30+
},
31+
preApprovalOne: {
32+
scope: 'https://api.equifax.com/business/preapproval-of-one/v1',
33+
reportRequest: '/business/preapproval-of-one/v1/report-requests',
34+
},
35+
preQualification: {
36+
scope: 'https://api.equifax.com/business/prequal-of-one/v1',
37+
reportRequest: '/business/prequal-of-one/v1/report-requests',
38+
},
39+
},
40+
},
41+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as crypto from 'crypto';
2+
3+
export function encrypt(text: string, secretKey: string): string {
4+
const cipher = crypto.createCipheriv(
5+
this.algorithm,
6+
Buffer.from(secretKey, 'hex'),
7+
Buffer.from(this.iv, 'hex'),
8+
);
9+
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
10+
return encrypted.toString('hex');
11+
}
12+
13+
export function decrypt(encryptedText: string): string {
14+
const encryptedTextBuffer = Buffer.from(encryptedText, 'hex');
15+
const decipher = crypto.createDecipheriv(
16+
'aes-256-cbc',
17+
Buffer.from(
18+
'b41f7562df6b9a41d8dc1803d24cf4e3213d49cbf62317d1c674930dcaa02b0e',
19+
'hex',
20+
),
21+
Buffer.from('ff2f0a87a95e57eaaab63b7772d94d2a', 'hex'),
22+
);
23+
const decrypted = Buffer.concat([
24+
decipher.update(encryptedTextBuffer),
25+
decipher.final(),
26+
]);
27+
return decrypted.toString();
28+
}
29+
30+
// 'Encrypted Client ID:' '185b5c5d9ada76aed787998acb9ac4f284c689993c6a24ff5249d4796717879ee1003eb44f5cfdf0c4049815ade39060'
31+
// 'Encrypted Client Secret:' '94cecd343affc1b67480c8e3b6e8819f9a9923c3d4f46c562193335deb871da9'
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { AxiosRequestConfig } from 'axios';
2+
13
export interface HttpAdapter {
2-
get<T>(url: string): Promise<T>;
4+
get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
5+
post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
6+
put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
7+
delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
38
}

src/config/equifax.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { registerAs } from '@nestjs/config';
2+
3+
export default registerAs('equifax', () => ({
4+
baseUrl: process.env.EQUIFAX_BASE_URL,
5+
clientId: process.env.EQUIFAX_CLIENT_ID,
6+
clientSecret: process.env.EQUIFAX_CLIENT_SECRET,
7+
}));
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import {
2+
IsString,
3+
IsEmail,
4+
IsBoolean,
5+
IsObject,
6+
IsDateString,
7+
IsNotEmpty,
8+
} from 'class-validator';
9+
import { Type } from 'class-transformer';
10+
import { ApiProperty } from '@nestjs/swagger';
11+
12+
class AddressDTO {
13+
@ApiProperty({ description: 'Primary address line' })
14+
@IsString()
15+
@IsNotEmpty()
16+
address1: string;
17+
18+
@ApiProperty({ description: 'City of the address' })
19+
@IsString()
20+
@IsNotEmpty()
21+
city: string;
22+
23+
@ApiProperty({ description: 'State of the address' })
24+
@IsString()
25+
@IsNotEmpty()
26+
state: string;
27+
28+
@ApiProperty({ description: 'ZIP code of the address' })
29+
@IsString()
30+
@IsNotEmpty()
31+
zipCode: string;
32+
33+
@ApiProperty({ description: 'Country of the address' })
34+
@IsString()
35+
@IsNotEmpty()
36+
country: string;
37+
}
38+
39+
class ConsumerDTO {
40+
@ApiProperty({ description: 'Consumer identifier' })
41+
@IsString()
42+
@IsNotEmpty()
43+
identifier: string;
44+
45+
@ApiProperty({ description: 'Consumer first name' })
46+
@IsString()
47+
@IsNotEmpty()
48+
firstName: string;
49+
50+
@ApiProperty({ description: 'Consumer last name' })
51+
@IsString()
52+
@IsNotEmpty()
53+
lastName: string;
54+
55+
@ApiProperty({ description: 'Consumer email' })
56+
@IsEmail()
57+
@IsNotEmpty()
58+
email: string;
59+
60+
@ApiProperty({ description: 'Consumer date of birth' })
61+
@IsDateString()
62+
@IsNotEmpty()
63+
dob: string;
64+
65+
@ApiProperty({ description: 'Consumer address', type: AddressDTO })
66+
@IsObject()
67+
@Type(() => AddressDTO)
68+
address: AddressDTO;
69+
}
70+
71+
class ResellerDTO {
72+
@ApiProperty({ description: 'Is the reseller self?' })
73+
@IsBoolean()
74+
self: boolean;
75+
76+
@ApiProperty({ description: 'End user organization ID' })
77+
@IsString()
78+
@IsNotEmpty()
79+
endUserOrganizationId: string;
80+
81+
@ApiProperty({ description: 'End user name' })
82+
@IsString()
83+
@IsNotEmpty()
84+
endUserName: string;
85+
}
86+
87+
class ConnectorDTO {
88+
@ApiProperty({ description: 'Platform of the connector' })
89+
@IsString()
90+
@IsNotEmpty()
91+
platform: string;
92+
93+
@ApiProperty({ description: 'Intermediary of the connector' })
94+
@IsString()
95+
@IsNotEmpty()
96+
intermediary: string;
97+
98+
@ApiProperty({ description: 'End user of the connector' })
99+
@IsString()
100+
@IsNotEmpty()
101+
endUser: string;
102+
}
103+
104+
export class InitiateRequestDTO {
105+
@ApiProperty({ description: 'Permissible purpose' })
106+
@IsString()
107+
@IsNotEmpty()
108+
permissiblePurpose: string;
109+
110+
@ApiProperty({ description: 'Loan number' })
111+
@IsString()
112+
@IsNotEmpty()
113+
loanNumber: string;
114+
115+
@ApiProperty({ description: 'Monitoring duration' })
116+
@IsString()
117+
@IsNotEmpty()
118+
monitoringDuration: string;
119+
120+
@ApiProperty({ description: 'CC email' })
121+
@IsEmail()
122+
@IsNotEmpty()
123+
ccEmail: string;
124+
125+
@ApiProperty({ description: 'Order start date' })
126+
@IsDateString()
127+
@IsNotEmpty()
128+
orderStartDate: string;
129+
130+
@ApiProperty({ description: 'Order end date' })
131+
@IsDateString()
132+
@IsNotEmpty()
133+
orderEndDate: string;
134+
135+
@ApiProperty({ description: 'Consumer details', type: ConsumerDTO })
136+
@IsObject()
137+
@Type(() => ConsumerDTO)
138+
consumer: ConsumerDTO;
139+
140+
@ApiProperty({ description: 'Reseller details', type: ResellerDTO })
141+
@IsObject()
142+
@Type(() => ResellerDTO)
143+
reseller: ResellerDTO;
144+
145+
@ApiProperty({ description: 'Connector details', type: ConnectorDTO })
146+
@IsObject()
147+
@Type(() => ConnectorDTO)
148+
connector: ConnectorDTO;
149+
}

0 commit comments

Comments
 (0)