diff --git a/src/config/config.ts b/src/config/config.ts index 018e4d20d9..99fad0cf6a 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -72,6 +72,14 @@ export class Configuration { expiresIn: process.env.JWT_EXPIRES_IN ?? 172800, }, }, + company: { + signOptions: { + expiresIn: process.env.JWT_EXPIRES_IN_COMPANY ?? 30, + }, + }, + challenge: { + expiresIn: +process.env.CHALLENGE_EXPIRES_IN ?? 10, + }, signMessage: 'By_signing_this_message,_you_confirm_that_you_are_the_sole_owner_of_the_provided_DeFiChain_address_and_are_in_possession_of_its_private_key._Your_ID:_', signMessageWallet: @@ -119,8 +127,10 @@ export class Configuration { apiKey: process.env.FIXER_API_KEY, }; - lock = { - apiKey: process.env.LOCK_API_KEY, + externalKycServices = { + 'LOCK.space': { + apiKey: process.env.LOCK_API_KEY, + }, }; mail: MailOptions = { diff --git a/src/user/models/auth/auth.controller.ts b/src/user/models/auth/auth.controller.ts index b5ee33f577..9bbd735c76 100644 --- a/src/user/models/auth/auth.controller.ts +++ b/src/user/models/auth/auth.controller.ts @@ -30,4 +30,9 @@ export class AuthController { signInCompany(@Body() credentials: AuthCredentialsDto): Promise<{ accessToken: string }> { return this.authService.companySignIn(credentials); } + + @Post('company/challenge') + companyChallenge(@Query('address') address: string): Promise<{ challenge: string }> { + return this.authService.getCompanyChallenge(address); + } } diff --git a/src/user/models/auth/auth.service.ts b/src/user/models/auth/auth.service.ts index 660628bf8f..fda4ce6cb7 100644 --- a/src/user/models/auth/auth.service.ts +++ b/src/user/models/auth/auth.service.ts @@ -21,9 +21,19 @@ import { Blockchain } from 'src/blockchain/shared/enums/blockchain.enum'; import { WalletRepository } from '../wallet/wallet.repository'; import { Wallet } from '../wallet/wallet.entity'; import { UserRole } from 'src/shared/auth/user-role.enum'; +import { Util } from 'src/shared/util'; +import { Interval } from '@nestjs/schedule'; +import { randomUUID } from 'crypto'; + +export interface ChallengeData { + created: Date; + challenge: string; +} @Injectable() export class AuthService { + private challengeList: Map = new Map(); + constructor( private readonly userService: UserService, private readonly userRepo: UserRepository, @@ -33,13 +43,24 @@ export class AuthService { private readonly refService: RefService, ) {} + @Interval(90000) + checkChallengeList() { + for (const [key, challenge] of this.challengeList.entries()) { + if (!this.isChallengeValid(challenge)) { + this.challengeList.delete(key); + } + } + } + + // --- AUTH METHODS --- // async signUp(dto: CreateUserDto, userIp: string): Promise<{ accessToken: string }> { const existingUser = await this.userRepo.getByAddress(dto.address); if (existingUser) { throw new ConflictException('User already exists'); } - if (!this.verifySignature(dto.address, dto.signature)) { + const { message } = this.getSignMessage(dto.address); + if (!this.verifySignature(message, dto.address, dto.signature)) { throw new BadRequestException('Invalid signature'); } @@ -56,7 +77,8 @@ export class AuthService { const user = await this.userRepo.getByAddress(address); if (!user) throw new NotFoundException('User not found'); - const credentialsValid = this.verifySignature(address, signature); + const { message } = this.getSignMessage(address); + const credentialsValid = this.verifySignature(message, address, signature); if (!credentialsValid) throw new UnauthorizedException('Invalid credentials'); // TODO: temporary code to update old wallet signatures @@ -67,17 +89,36 @@ export class AuthService { return { accessToken: this.generateUserToken(user) }; } - async companySignIn({ address, signature }: AuthCredentialsDto): Promise<{ accessToken: string }> { - const wallet = await this.walletRepo.getByAddress(address); + async companySignIn(dto: AuthCredentialsDto): Promise<{ accessToken: string }> { + const wallet = await this.walletRepo.findOne({ where: { address: dto.address } }); if (!wallet || !wallet.isKycClient) throw new NotFoundException('Wallet not found'); - // TODO add challenge response - const credentialsValid = this.verifyCompanySignature(address, signature); + const credentialsValid = this.verifyCompanySignature(dto); if (!credentialsValid) throw new UnauthorizedException('Invalid credentials'); return { accessToken: this.generateCompanyToken(wallet) }; } + async getCompanyChallenge(address: string): Promise<{ challenge: string }> { + const wallet = await this.walletRepo.findOne({ where: { address: address } }); + if (!wallet || !wallet.isKycClient) throw new BadRequestException('Wallet not found/invalid'); + + const challenge = randomUUID(); + + this.challengeList.set(address, { created: new Date(), challenge: challenge }); + + return { challenge: challenge }; + } + + async changeUser(id: number, changeUser: LinkedUserInDto): Promise<{ accessToken: string }> { + const user = await this.getLinkedUser(id, changeUser.address); + if (!user) throw new NotFoundException('User not found'); + if (user.stakingBalance > 0) throw new ForbiddenException('Change user not allowed'); + return { accessToken: this.generateUserToken(user) }; + } + + // --- HELPER METHODS --- // + getSignMessage(address: string): { message: string; blockchains: Blockchain[] } { const blockchains = this.cryptoService.getBlockchainsBasedOn(address); return { @@ -94,13 +135,6 @@ export class AuthService { }; } - async changeUser(id: number, changeUser: LinkedUserInDto): Promise<{ accessToken: string }> { - const user = await this.getLinkedUser(id, changeUser.address); - if (!user) throw new NotFoundException('User not found'); - if (user.stakingBalance > 0) throw new ForbiddenException('Change user not allowed'); - return { accessToken: this.generateUserToken(user) }; - } - private async getLinkedUser(id: number, address: string): Promise { return this.userRepo .createQueryBuilder('user') @@ -112,14 +146,16 @@ export class AuthService { .getRawOne(); } - private verifySignature(address: string, signature: string): boolean { - const signatureMessage = this.getSignMessage(address); - return this.cryptoService.verifySignature(signatureMessage.message, address, signature); + private verifySignature(message: string, address: string, signature: string): boolean { + return this.cryptoService.verifySignature(message, address, signature); } - private verifyCompanySignature(address: string, signature: string): boolean { - const signatureMessage = this.getCompanySignMessage(address); - return this.cryptoService.verifySignature(signatureMessage.message, address, signature); + private verifyCompanySignature(dto: AuthCredentialsDto): boolean { + const challengeData = this.challengeList.get(dto.address); + if (!this.isChallengeValid(challengeData)) throw new UnauthorizedException('Challenge invalid'); + this.challengeList.delete(dto.address); + + return this.verifySignature(challengeData.challenge, dto.address, dto.signature); } private generateUserToken(user: User): string { @@ -138,6 +174,10 @@ export class AuthService { address: wallet.address, role: UserRole.KYC_CLIENT_COMPANY, }; - return this.jwtService.sign(payload); + return this.jwtService.sign(payload, { expiresIn: Config.auth.company.signOptions.expiresIn }); + } + + private isChallengeValid(challenge: ChallengeData): boolean { + return challenge && Util.secondsDiff(challenge.created, new Date()) <= Config.auth.challenge.expiresIn; } } diff --git a/src/user/models/ident/ident.service.ts b/src/user/models/ident/ident.service.ts index 1605b54238..256f733d7c 100644 --- a/src/user/models/ident/ident.service.ts +++ b/src/user/models/ident/ident.service.ts @@ -21,7 +21,7 @@ export class IdentService { spiderData: { identIdentificationIds: Like(`%${result?.identificationprocess?.id}%`) }, }, ], - relations: ['spiderData'], + relations: ['spiderData', 'users', 'users.wallet'], }); if (!user) { diff --git a/src/user/models/kyc/kyc-process.service.ts b/src/user/models/kyc/kyc-process.service.ts index ea96163121..37831ade57 100644 --- a/src/user/models/kyc/kyc-process.service.ts +++ b/src/user/models/kyc/kyc-process.service.ts @@ -24,7 +24,8 @@ export class KycProcessService { // --- GENERAL METHODS --- // async startKycProcess(userData: UserData): Promise { - return await this.goToStatus(userData, KycStatus.CHATBOT); + const lockUser = userData.users.find((e) => e.wallet.name === 'LOCK.space'); + return await this.goToStatus(userData, lockUser ? KycStatus.ONLINE_ID : KycStatus.CHATBOT); } async checkKycProcess(userData: UserData): Promise { @@ -56,7 +57,7 @@ export class KycProcessService { userData.spiderData = await this.updateSpiderData(userData, initiateData); } - if (status === KycStatus.MANUAL) { + if (status === KycStatus.MANUAL && !userData.hasExternalUser) { if (userData.mail) { await this.notificationService.sendMail({ type: MailType.USER, @@ -95,24 +96,29 @@ export class KycProcessService { if (userData.kycStatus === KycStatus.ONLINE_ID) { userData = await this.goToStatus(userData, KycStatus.VIDEO_ID); - await this.notificationService - .sendMail({ - type: MailType.USER, - input: { - userData, - translationKey: 'mail.kyc.failed', - translationParams: { - url: `${Config.payment.url}/kyc?code=${userData.kycHash}`, + if (!userData.hasExternalUser) { + await this.notificationService + .sendMail({ + type: MailType.USER, + input: { + userData, + translationKey: 'mail.kyc.failed', + translationParams: { + url: `${Config.payment.url}/kyc?code=${userData.kycHash}`, + }, }, - }, - }) - .catch(() => null); + }) + .catch(() => null); + } return userData; } // notify support await this.notificationService.sendMail({ type: MailType.KYC_SUPPORT, input: { userData } }); + + //kyc Webhook external Services + await this.kycWebhookService.kycFailed(userData, 'Kyc step failed'); return this.updateKycState(userData, KycState.FAILED); } @@ -167,12 +173,14 @@ export class KycProcessService { async identCompleted(userData: UserData, result: IdentResultDto): Promise { userData = await this.storeIdentResult(userData, result); - await this.notificationService - .sendMail({ - type: MailType.USER, - input: { userData, translationKey: 'mail.kyc.ident', translationParams: {} }, - }) - .catch(() => null); + if (!userData.hasExternalUser) { + await this.notificationService + .sendMail({ + type: MailType.USER, + input: { userData, translationKey: 'mail.kyc.ident', translationParams: {} }, + }) + .catch(() => null); + } return await this.goToStatus(userData, KycStatus.CHECK); } diff --git a/src/user/models/kyc/kyc-webhook.service.ts b/src/user/models/kyc/kyc-webhook.service.ts index 36f24946fa..bf184e0879 100644 --- a/src/user/models/kyc/kyc-webhook.service.ts +++ b/src/user/models/kyc/kyc-webhook.service.ts @@ -1,13 +1,15 @@ import { Injectable } from '@nestjs/common'; import { HttpService } from 'src/shared/services/http.service'; -import { WalletRepository } from '../wallet/wallet.repository'; -import { KycCompleted, UserData } from '../user-data/user-data.entity'; -import { Config } from 'src/config/config'; +import { KycCompleted, KycStatus, UserData } from '../user-data/user-data.entity'; +import { UserRepository } from '../user/user.repository'; +import { SpiderDataRepository } from '../spider-data/spider-data.repository'; +import { WalletService } from '../wallet/wallet.service'; export enum KycWebhookStatus { NA = 'NA', LIGHT = 'Light', FULL = 'Full', + REJECTED = 'Rejected', } export enum KycWebhookResult { @@ -37,7 +39,12 @@ export class KycWebhookDto { @Injectable() export class KycWebhookService { - constructor(private readonly http: HttpService, private readonly walletRepo: WalletRepository) {} + constructor( + private readonly http: HttpService, + private readonly walletService: WalletService, + private readonly userRepo: UserRepository, + private readonly spiderRepo: SpiderDataRepository, + ) {} async kycChanged(userData: UserData): Promise { await this.triggerWebhook(userData, KycWebhookResult.STATUS_CHANGED); @@ -48,19 +55,13 @@ export class KycWebhookService { } private async triggerWebhook(userData: UserData, result: KycWebhookResult, reason?: string): Promise { - if (!userData.users) { - console.info(`Tried to trigger webhook for user ${userData.id}, but users were not loaded`); - return; - } + userData.users = await this.userRepo.find({ where: { userData: { id: userData.id } }, relations: ['wallet'] }); for (const user of userData.users) { try { - if (!user.wallet?.id) { - console.info(`Tried to trigger webhook for user ${userData.id}, but wallet were not loaded`); - continue; - } - const walletUser = await this.walletRepo.findOne({ where: { id: user.wallet.id } }); - if (!walletUser || !walletUser.isKycClient || !walletUser.apiUrl) continue; + if (!user.wallet.isKycClient || !user.wallet.apiUrl) continue; + + const spiderData = await this.spiderRepo.findOne({ where: { userData: { id: userData.id } } }); const data: KycWebhookDto = { id: user.address, @@ -74,19 +75,31 @@ export class KycWebhookService { city: userData.location, zip: userData.zip, phone: userData.phone, - //TODO change for KYC Update v2 - kycStatus: KycCompleted(userData.kycStatus) ? KycWebhookStatus.FULL : KycWebhookStatus.NA, + kycStatus: this.getKycWebhookStatus(userData.kycStatus, spiderData?.chatbotResult), kycHash: userData.kycHash, }, reason: reason, }; - await this.http.post(`${walletUser.apiUrl}/kyc/update`, data, { - headers: { 'x-api-key': Config.lock.apiKey }, + const apiKey = this.walletService.getApiKeyInternal(user.wallet.name); + if (!apiKey) throw new Error(`ApiKey for wallet ${user.wallet.name} not available`); + + await this.http.post(`${user.wallet.apiUrl}/kyc/update`, data, { + headers: { 'x-api-key': apiKey }, }); } catch (error) { console.error(`Exception during KYC webhook (${result}) for user ${userData.id}:`, error); } } } + + getKycWebhookStatus(kycStatus: KycStatus, chatbotResult: string): KycWebhookStatus { + if (KycCompleted(kycStatus)) { + return chatbotResult ? KycWebhookStatus.FULL : KycWebhookStatus.LIGHT; + } else if (kycStatus === KycStatus.REJECTED) { + return KycWebhookStatus.REJECTED; + } else { + return KycWebhookStatus.NA; + } + } } diff --git a/src/user/models/kyc/kyc.service.ts b/src/user/models/kyc/kyc.service.ts index 80fad001b2..1728705832 100644 --- a/src/user/models/kyc/kyc.service.ts +++ b/src/user/models/kyc/kyc.service.ts @@ -28,8 +28,8 @@ import { UpdateKycStatusDto } from '../user-data/dto/update-kyc-status.dto'; import { KycDataTransferDto } from './dto/kyc-data-transfer.dto'; import { WalletRepository } from '../wallet/wallet.repository'; import { HttpService } from 'src/shared/services/http.service'; -import { Config } from 'src/config/config'; import { UserRepository } from '../user/user.repository'; +import { WalletService } from '../wallet/wallet.service'; export interface KycInfo { kycStatus: KycStatus; @@ -51,6 +51,7 @@ export class KycService { private readonly userDataRepo: UserDataRepository, private readonly userRepo: UserRepository, private readonly walletRepo: WalletRepository, + private readonly walletService: WalletService, private readonly spiderService: SpiderService, private readonly spiderSyncService: SpiderSyncService, private readonly countryService: CountryService, @@ -73,7 +74,7 @@ export class KycService { } async updateKycStatus(userDataId: number, dto: UpdateKycStatusDto): Promise { - let userData = await this.userDataRepo.findOne({ where: { id: userDataId } }); + let userData = await this.userDataRepo.findOne({ where: { id: userDataId }, relations: ['users', 'users.wallet'] }); if (!userData) throw new NotFoundException('User data not found'); // update status @@ -137,9 +138,12 @@ export class KycService { if (!user) throw new NotFoundException('DFX user not found'); if (!KycCompleted(user.userData.kycStatus)) throw new ConflictException('KYC required'); + const apiKey = this.walletService.getApiKeyInternal(wallet.name); + if (!apiKey) throw new Error(`ApiKey for wallet ${wallet.name} not available`); + try { result = await this.http.get<{ kycId: string }>(`${wallet.apiUrl}/kyc/check`, { - headers: { 'x-api-key': Config.lock.apiKey }, + headers: { 'x-api-key': apiKey }, params: { address: user.address }, }); @@ -221,7 +225,7 @@ export class KycService { const users = await this.userDataService.getUsersByMail(user.mail); const completedUser = users.find((data) => KycCompleted(data.kycStatus)); - if (completedUser) { + if (completedUser && !user.hasExternalUser) { await this.linkService.createNewLinkAddress(user, completedUser); throw new ConflictException('User already has completed Kyc'); } @@ -287,7 +291,10 @@ export class KycService { } private async getUserByKycCode(code: string): Promise { - const userData = await this.userDataRepo.findOne({ where: { kycHash: code }, relations: ['users', 'spiderData'] }); + const userData = await this.userDataRepo.findOne({ + where: { kycHash: code }, + relations: ['users', 'users.wallet', 'spiderData'], + }); if (!userData) throw new NotFoundException('User not found'); return userData; } diff --git a/src/user/models/user-data/user-data.entity.ts b/src/user/models/user-data/user-data.entity.ts index b16eb62ee9..7d07cf000c 100644 --- a/src/user/models/user-data/user-data.entity.ts +++ b/src/user/models/user-data/user-data.entity.ts @@ -178,6 +178,10 @@ export class UserData extends IEntity { @OneToOne(() => SpiderData, (c) => c.userData, { nullable: true }) spiderData: SpiderData; + + get hasExternalUser(): boolean { + return !!this.users.find((e) => e.wallet.isKycClient === true); + } } export const KycInProgressStates = [KycStatus.CHATBOT, KycStatus.ONLINE_ID, KycStatus.VIDEO_ID]; diff --git a/src/user/models/user-data/user-data.service.ts b/src/user/models/user-data/user-data.service.ts index ab96f57b06..b1f0222a87 100644 --- a/src/user/models/user-data/user-data.service.ts +++ b/src/user/models/user-data/user-data.service.ts @@ -36,10 +36,11 @@ export class UserDataService { async getUserDataByUser(userId: number): Promise { return this.userDataRepo .createQueryBuilder('userData') - .innerJoin('userData.users', 'user') + .leftJoinAndSelect('userData.users', 'user') .leftJoinAndSelect('userData.country', 'country') .leftJoinAndSelect('userData.organizationCountry', 'organizationCountry') .leftJoinAndSelect('userData.language', 'language') + .leftJoinAndSelect('user.wallet', 'wallet') .where('user.id = :id', { id: userId }) .getOne(); } @@ -69,7 +70,7 @@ export class UserDataService { } async updateUserData(userDataId: number, dto: UpdateUserDataDto): Promise { - let userData = await this.userDataRepo.findOne(userDataId); + let userData = await this.userDataRepo.findOne({ where: { id: userDataId }, relations: ['users', 'users.wallet'] }); if (!userData) throw new NotFoundException('User data not found'); userData = await this.updateSpiderIfNeeded(userData, dto); diff --git a/src/user/models/wallet/dto/kyc-data.dto.ts b/src/user/models/wallet/dto/kyc-data.dto.ts new file mode 100644 index 0000000000..8cf4b8f1ee --- /dev/null +++ b/src/user/models/wallet/dto/kyc-data.dto.ts @@ -0,0 +1,7 @@ +import { KycWebhookStatus } from '../../kyc/kyc-webhook.service'; + +export class KycDataDto { + address: string; + kycStatus: KycWebhookStatus; + kycHash: string; +} diff --git a/src/user/models/wallet/dto/wallet.dto.ts b/src/user/models/wallet/dto/wallet.dto.ts new file mode 100644 index 0000000000..31e84d424e --- /dev/null +++ b/src/user/models/wallet/dto/wallet.dto.ts @@ -0,0 +1,3 @@ +export class WalletDto { + name: string; +} diff --git a/src/user/models/wallet/wallet.controller.ts b/src/user/models/wallet/wallet.controller.ts new file mode 100644 index 0000000000..15d25df04a --- /dev/null +++ b/src/user/models/wallet/wallet.controller.ts @@ -0,0 +1,64 @@ +import { Controller, Get, UseGuards } from '@nestjs/common'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { RoleGuard } from 'src/shared/auth/role.guard'; +import { AuthGuard } from '@nestjs/passport'; +import { UserRole } from 'src/shared/auth/user-role.enum'; +import { WalletService } from './wallet.service'; +import { WalletDto } from './dto/wallet.dto'; +import { Wallet } from './wallet.entity'; +import { GetJwt } from 'src/shared/auth/get-jwt.decorator'; +import { JwtPayload } from 'src/shared/auth/jwt-payload.interface'; +import { User } from '../user/user.entity'; +import { KycDataDto } from './dto/kyc-data.dto'; +import { KycWebhookService } from '../kyc/kyc-webhook.service'; +import { SpiderDataRepository } from '../spider-data/spider-data.repository'; + +@ApiTags('wallet') +@Controller('wallet') +export class WalletController { + constructor( + private readonly walletService: WalletService, + private readonly spiderRepo: SpiderDataRepository, + private readonly kycWebhookService: KycWebhookService, + ) {} + + @Get() + @ApiBearerAuth() + @UseGuards(AuthGuard(), new RoleGuard(UserRole.USER)) + async getAllExternalService(): Promise { + return this.walletService.getAllExternalServices().then((l) => this.toDtoList(l)); + } + + @Get('kycData') + @ApiBearerAuth() + @UseGuards(AuthGuard(), new RoleGuard(UserRole.KYC_CLIENT_COMPANY)) + async getAllKycData(@GetJwt() jwt: JwtPayload): Promise { + return this.walletService.getAllKycData(jwt.id).then((l) => this.toKycDataDtoList(l)); + } + + // --- DTO --- // + private async toDtoList(wallets: Wallet[]): Promise { + return Promise.all(wallets.map((b) => this.toDto(b))); + } + + private async toKycDataDtoList(users: User[]): Promise { + return Promise.all(users.map((b) => this.toKycDataDto(b))); + } + + private async toKycDataDto(user: User): Promise { + return { + address: user.address, + kycStatus: this.kycWebhookService.getKycWebhookStatus( + user.userData.kycStatus, + user.userData.spiderData?.chatbotResult, + ), + kycHash: user.userData.kycHash, + }; + } + + private async toDto(wallet: Wallet): Promise { + return { + name: wallet.name, + }; + } +} diff --git a/src/user/models/wallet/wallet.entity.ts b/src/user/models/wallet/wallet.entity.ts index fdbb4d7e6f..2036f9a579 100644 --- a/src/user/models/wallet/wallet.entity.ts +++ b/src/user/models/wallet/wallet.entity.ts @@ -23,5 +23,5 @@ export class Wallet extends IEntity { apiUrl: string; @OneToMany(() => User, (user) => user.wallet) - logs: User[]; + users: User[]; } diff --git a/src/user/models/wallet/wallet.service.ts b/src/user/models/wallet/wallet.service.ts index f488f41d31..48592d1884 100644 --- a/src/user/models/wallet/wallet.service.ts +++ b/src/user/models/wallet/wallet.service.ts @@ -1,18 +1,34 @@ import { Injectable } from '@nestjs/common'; +import { Config } from 'src/config/config'; import { WalletRepository } from 'src/user/models/wallet/wallet.repository'; +import { User } from '../user/user.entity'; import { Wallet } from './wallet.entity'; @Injectable() export class WalletService { - constructor(private walletRepo: WalletRepository) {} + constructor(private readonly walletRepo: WalletRepository) {} async getWalletOrDefault(id: number): Promise { return (await this.walletRepo.findOne(id)) ?? (await this.walletRepo.findOne(1)); } - // TODO: remove? - // private verifySignature(address: string, signature: string): boolean { - // const signatureMessage = Config.auth.signMessageWallet + address; - // return this.cryptoService.verifySignature(signatureMessage, address, signature); - // } + async getAllExternalServices(): Promise { + return await this.walletRepo.find({ where: { isKycClient: true } }); + } + + async getAllKycData(walletId: number): Promise { + const wallet = await this.walletRepo.findOne({ + where: { id: walletId }, + relations: ['users', 'users.userData', 'users.userData.spiderData'], + }); + return wallet.users; + } + + public getApiKeyInternal(name: string): string { + return ( + Object.entries(Config.externalKycServices) + .filter(([key, _]) => key === name) + .map(([_, value]) => value)[0]?.apiKey ?? undefined + ); + } } diff --git a/src/user/services/spider/spider-sync.service.ts b/src/user/services/spider/spider-sync.service.ts index 926b8ab8cd..5dfb40be24 100644 --- a/src/user/services/spider/spider-sync.service.ts +++ b/src/user/services/spider/spider-sync.service.ts @@ -118,7 +118,10 @@ export class SpiderSyncService { } async syncKycUser(userDataId: number, forceSync = false): Promise { - let userData = await this.userDataRepo.findOne({ where: { id: userDataId }, relations: ['spiderData'] }); + let userData = await this.userDataRepo.findOne({ + where: { id: userDataId }, + relations: ['spiderData', 'users', 'users.wallet'], + }); if (!userData) return; // update KYC data diff --git a/src/user/user.module.ts b/src/user/user.module.ts index 458d1e0ceb..9237b7c1a6 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -36,6 +36,7 @@ import { LinkAddressRepository } from './models/link/link-address.repository'; import { NotificationModule } from 'src/notification/notification.module'; import { LimitRequestController } from './models/limit-request/limit-request.controller'; import { KycWebhookService } from './models/kyc/kyc-webhook.service'; +import { WalletController } from './models/wallet/wallet.controller'; @Module({ imports: [ @@ -63,6 +64,7 @@ import { KycWebhookService } from './models/kyc/kyc-webhook.service'; KycController, LinkController, LimitRequestController, + WalletController, ], providers: [ UserService, diff --git a/thunder-tests/thunderCollection.json b/thunder-tests/thunderCollection.json index 80d7aac49f..e48b3e3994 100644 --- a/thunder-tests/thunderCollection.json +++ b/thunder-tests/thunderCollection.json @@ -194,6 +194,13 @@ "created": "2022-09-23T14:03:55.908Z", "sortNum": 300000 }, + { + "_id": "9baf5a17-7886-4959-ab65-eea8e3dae524", + "name": "Wallet", + "containerId": "", + "created": "2022-10-14T16:15:32.185Z", + "sortNum": 275000 + }, { "_id": "af415d12-0498-48a6-a81d-4d3ef4497cdf", "name": "Bank TX Return", diff --git a/thunder-tests/thunderclient.json b/thunder-tests/thunderclient.json index d3e345e18a..b7e313debd 100644 --- a/thunder-tests/thunderclient.json +++ b/thunder-tests/thunderclient.json @@ -1867,6 +1867,64 @@ "tests": [] }, { + "_id": "a0a206f0-13eb-493a-9b07-3a140b0eeedf", + "colId": "62eefab7-84cd-496b-8f93-253c15d4f841", + "containerId": "0bbfaa5e-1c48-4a6b-9dd3-bb28b78af825", + "name": "Company Challenge", + "url": "{{url}}/v1/auth/company/challenge?address=8an2Mi4dCV8GfiqbF2i2pJwDetq87AhFa4", + "method": "POST", + "sortNum": 85000, + "created": "2022-10-14T13:12:37.193Z", + "modified": "2022-10-20T10:15:12.521Z", + "headers": [], + "params": [ + { + "name": "address", + "value": "8an2Mi4dCV8GfiqbF2i2pJwDetq87AhFa4", + "isPath": false + } + ], + "auth": { + "type": "none" + }, + "tests": [] + }, + { + "_id": "8384887d-f0e7-43a4-9a28-1859576d0c1c", + "colId": "62eefab7-84cd-496b-8f93-253c15d4f841", + "containerId": "0bbfaa5e-1c48-4a6b-9dd3-bb28b78af825", + "name": "Company SignIn", + "url": "{{url}}/v1/auth/company/signin", + "method": "POST", + "sortNum": 82500, + "created": "2022-10-14T13:12:39.907Z", + "modified": "2022-10-20T10:28:14.156Z", + "headers": [], + "params": [], + "body": { + "type": "json", + "raw": "{\n \"address\": \"8an2Mi4dCV8GfiqbF2i2pJwDetq87AhFa4\",\n \"signature\": \"HzRZfDlmtAp4ywy7MuGpwCEEGoemH6rB82VNJUaKM7P1PCmiBRo2MyuuYfIBKE/nakafSymO8ep+84AGIzeEm/I=\"\n}", + "form": [] + }, + "auth": { + "type": "none" + }, + "tests": [] + }, + { + "_id": "14441e24-4e57-4e7c-88cc-dff408c5837e", + "colId": "62eefab7-84cd-496b-8f93-253c15d4f841", + "containerId": "9baf5a17-7886-4959-ab65-eea8e3dae524", + "name": "KycData", + "url": "{{url}}/v1/wallet/kycData", + "method": "GET", + "sortNum": 640000, + "created": "2022-10-14T16:15:32.185Z", + "modified": "2022-10-14T16:16:42.670Z", + "headers": [], + "params": [], + "tests": [] + },{ "_id": "fb9bc645-856b-46a8-98c3-445abc95a2c4", "colId": "62eefab7-84cd-496b-8f93-253c15d4f841", "containerId": "d74943e9-e28b-4c36-9df3-b6093cf39800",