Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/shared/auth/ip-country.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ export class IpCountryGuard implements CanActivate {
const req = context.switchToHttp().getRequest();
const ip = getClientIp(req);

const ipLog = await this.ipLogService.create(ip, req.url, req.body?.address ?? req.user.address);
const ipLog = await this.ipLogService.create(
ip,
req.url,
req.body?.address ?? req.user.address,
req.body?.specialCode,
);
if (!ipLog.result) throw new ForbiddenException('The country of IP address is not allowed');

if (req.body?.region && !Config.loginCountries[req.body?.region]?.includes(ipLog.country))
Expand Down
1 change: 1 addition & 0 deletions src/shared/auth/jwt-payload.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export interface JwtPayload {
address?: string; // user/wallet address
role: UserRole;
blockchains?: Blockchain[];
specialCodes?: number[]; // special code id's
ip: string;
}
3 changes: 3 additions & 0 deletions src/shared/models/ip-log/ip-log.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export class IpLog extends IEntity {
@Column({ length: 256 })
url: string;

@Column({ length: 256, nullable: true })
specialCode: string;

@Column()
result: boolean;

Expand Down
3 changes: 2 additions & 1 deletion src/shared/models/ip-log/ip-log.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class IpLogService {
private readonly repos: RepositoryFactory,
) {}

async create(ip: string, url: string, address: string): Promise<IpLog> {
async create(ip: string, url: string, address: string, specialCode?: string): Promise<IpLog> {
const { country, result, user } = await this.checkIpCountry(ip, address);
const ipLog = this.ipLogRepo.create({
ip,
Expand All @@ -26,6 +26,7 @@ export class IpLogService {
url,
address,
user,
specialCode,
});

return this.ipLogRepo.save(ipLog);
Expand Down
12 changes: 9 additions & 3 deletions src/subdomains/core/buy-crypto/routes/buy/buy.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export class BuyController {
targetAmount,
paymentMethod,
specialCode,
specialCodes,
} = await this.paymentInfoService.buyCheck(dto);

const {
Expand All @@ -124,8 +125,7 @@ export class BuyController {
CryptoPaymentMethod.CRYPTO,
true,
undefined,
dto.wallet,
specialCode ? [specialCode] : [],
specialCodes ?? (specialCode ? [specialCode] : []),
);

return {
Expand Down Expand Up @@ -353,7 +353,13 @@ export class BuyController {
),
};

await this.transactionRequestService.create(TransactionRequestType.BUY, dto, buyDto, user.id);
await this.transactionRequestService.create(
TransactionRequestType.BUY,
dto,
buyDto,
user.id,
user.userData.specialCodes,
);

return buyDto;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ export class GetBuyQuoteDto {
@IsEnum(FiatPaymentMethod)
paymentMethod: FiatPaymentMethod = FiatPaymentMethod.BANK;

@ApiPropertyOptional({ description: 'This field is deprecated, use "specialCode" instead.', deprecated: true })
@ApiPropertyOptional({ description: 'This field is deprecated, use "specialCodes" instead.', deprecated: true })
@IsOptional()
@IsString()
discountCode: string;

@ApiPropertyOptional({ description: 'Special code' })
@ApiPropertyOptional({ description: 'This field is deprecated, use "specialCodes" instead.', deprecated: true })
@IsOptional()
@IsString()
specialCode: string;

@ApiPropertyOptional()
@ApiPropertyOptional({ description: 'Special codes', isArray: true })
@IsOptional()
@IsString()
wallet: string;
specialCodes: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ export class GetSwapQuoteDto {
@IsString()
discountCode: string;

@ApiPropertyOptional({ description: 'Special code' })
@ApiPropertyOptional({ description: 'This field is deprecated, use "specialCodes" instead.', deprecated: true })
@IsOptional()
@IsString()
specialCode: string;

@ApiPropertyOptional()
@ApiPropertyOptional({ description: 'Special codes', isArray: true })
@IsOptional()
@IsString()
wallet: string;
specialCodes: string[];
}
12 changes: 9 additions & 3 deletions src/subdomains/core/buy-crypto/routes/swap/swap.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export class SwapController {
targetAsset,
targetAmount,
specialCode,
specialCodes,
} = await this.paymentInfoService.swapCheck(dto);

const {
Expand All @@ -117,8 +118,7 @@ export class SwapController {
CryptoPaymentMethod.CRYPTO,
true,
undefined,
dto.wallet,
specialCode ? [specialCode] : [],
specialCodes ?? (specialCode ? [specialCode] : []),
);

return {
Expand Down Expand Up @@ -291,7 +291,13 @@ export class SwapController {
error,
};

await this.transactionRequestService.create(TransactionRequestType.SWAP, dto, swapDto, user.id);
await this.transactionRequestService.create(
TransactionRequestType.SWAP,
dto,
swapDto,
user.id,
user.userData.specialCodes,
);

return swapDto;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ export class GetSellQuoteDto {
@IsString()
discountCode: string;

@ApiPropertyOptional({ description: 'Special code' })
@ApiPropertyOptional({ description: 'This field is deprecated, use "specialCodes" instead.', deprecated: true })
@IsOptional()
@IsString()
specialCode: string;

@ApiPropertyOptional()
@ApiPropertyOptional({ description: 'Special codes', isArray: true })
@IsOptional()
@IsString()
wallet: string;
specialCodes: string[];
}
12 changes: 9 additions & 3 deletions src/subdomains/core/sell-crypto/route/sell.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export class SellController {
currency,
targetAmount,
specialCode,
specialCodes,
} = await this.paymentInfoService.sellCheck(dto);

const {
Expand All @@ -120,8 +121,7 @@ export class SellController {
FiatPaymentMethod.BANK,
true,
undefined,
dto.wallet,
specialCode ? [specialCode] : [],
specialCodes ?? (specialCode ? [specialCode] : []),
);

return {
Expand Down Expand Up @@ -294,7 +294,13 @@ export class SellController {
error,
};

await this.transactionRequestService.create(TransactionRequestType.SELL, dto, sellDto, user.id);
await this.transactionRequestService.create(
TransactionRequestType.SELL,
dto,
sellDto,
user.id,
user.userData.specialCodes,
);

return sellDto;
}
Expand Down
4 changes: 2 additions & 2 deletions src/subdomains/generic/user/models/auth/auth-alby.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class AuthAlbyService {
{
client_id: Config.alby.clientId,
client_secret: Config.alby.clientSecret,
code: code,
code,
grant_type: 'authorization_code',
redirect_uri: this.redirectUri(id),
},
Expand All @@ -84,7 +84,7 @@ export class AuthAlbyService {
// construct session and create IP log
const session = { address: LightningHelper.addressToLnurlp(lightning_address), signature: identifier };

const ipLog = await this.ipLogService.create(userIp, requestUrl, session.address);
const ipLog = await this.ipLogService.create(userIp, requestUrl, session.address, dto.specialCode);
if (!ipLog.result) throw new ForbiddenException('The country of IP address is not allowed');

const { accessToken } = await this.authService.signIn(session, userIp, true).catch((e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export class AuthLnUrlService {
const authCacheEntry = this.authCache.get(k1);
const { servicesIp, servicesUrl } = authCacheEntry;

const ipLog = await this.ipLogService.create(servicesIp, servicesUrl, address);
const ipLog = await this.ipLogService.create(servicesIp, servicesUrl, address, signupDto.specialCode);

if (!ipLog.result) {
this.authCache.delete(k1);
Expand Down
8 changes: 5 additions & 3 deletions src/subdomains/generic/user/models/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { RefService } from 'src/subdomains/core/referral/process/ref.service';
import { MailContext, MailType } from 'src/subdomains/supporting/notification/enums';
import { MailKey, MailTranslationKey } from 'src/subdomains/supporting/notification/factories/mail.factory';
import { NotificationService } from 'src/subdomains/supporting/notification/services/notification.service';
import { FeeService } from 'src/subdomains/supporting/payment/services/fee.service';
import { SpecialCodeService } from 'src/subdomains/supporting/payment/services/special-code.service';
import { CustodyProviderService } from '../custody-provider/custody-provider.service';
import { KycType, UserData, UserDataStatus } from '../user-data/user-data.entity';
import { UserDataService } from '../user-data/user-data.service';
Expand Down Expand Up @@ -71,7 +71,7 @@ export class AuthService {
private readonly cryptoService: CryptoService,
private readonly lightningService: LightningService,
private readonly refService: RefService,
private readonly feeService: FeeService,
private readonly specialCodeService: SpecialCodeService,
private readonly userDataService: UserDataService,
private readonly notificationService: NotificationService,
private readonly ipLogService: IpLogService,
Expand Down Expand Up @@ -187,7 +187,7 @@ export class AuthService {

try {
if (dto.specialCode || dto.discountCode)
await this.feeService.addSpecialCodeUser(user, dto.specialCode ?? dto.discountCode);
await this.specialCodeService.addSpecialCodeUser(user, dto.specialCode ?? dto.discountCode);
} catch (e) {
this.logger.warn(`Error while adding specialCode in user signIn ${user.id}:`, e);
}
Expand Down Expand Up @@ -396,6 +396,7 @@ export class AuthService {
role: user.role,
account: user.userData.id,
blockchains: user.blockchains,
specialCodes: user.userData.specialCodeList,
ip,
};
return this.jwtService.sign(payload);
Expand All @@ -406,6 +407,7 @@ export class AuthService {
role: UserRole.ACCOUNT,
account: userData.id,
blockchains: [],
specialCodes: userData.specialCodeList,
ip,
};
return this.jwtService.sign(payload);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';

export class CreateIntegratorDto {
@IsNotEmpty()
@IsString()
name: string;

@IsOptional()
@IsString()
mail: string;

@IsNotEmpty()
@IsString()
masterKey: string;
}
60 changes: 60 additions & 0 deletions src/subdomains/generic/user/models/integrator/integrator.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { IEntity } from 'src/shared/models/entity';
import { AmlRule } from 'src/subdomains/core/aml/enums/aml-rule.enum';
import { KycStepType } from 'src/subdomains/generic/kyc/enums/kyc.enum';
import { Column, Entity } from 'typeorm';
import { WebhookType } from '../../services/webhook/dto/webhook.dto';
import { KycType } from '../user-data/user-data.entity';
import { WebhookConfig, WebhookConfigOption } from '../wallet/wallet.entity';

@Entity()
export class Integrator extends IEntity {
@Column({ length: 256, nullable: true })
name?: string;

@Column({ nullable: true })
customKyc?: KycType;

@Column({ length: 256, nullable: true })
identMethod?: KycStepType;

@Column({ length: 256, nullable: true })
apiUrl?: string;

@Column({ length: 256, nullable: true })
apiKey?: string;

@Column({ length: 'MAX', nullable: true })
webhookConfig?: string; // JSON string

// TODO: nullable or default?
@Column({ nullable: true })
amlRule: AmlRule;

// --- ENTITY METHODS --- //

get webhookConfigObject(): WebhookConfig | undefined {
return this.webhookConfig ? (JSON.parse(this.webhookConfig) as WebhookConfig) : undefined;
}

isValidForWebhook(type: WebhookType, consented: boolean): boolean {
if (!this.apiUrl) return false;

switch (type) {
case WebhookType.KYC_CHANGED:
case WebhookType.KYC_FAILED:
case WebhookType.ACCOUNT_CHANGED:
return this.isOptionValid(this.webhookConfigObject?.kyc, consented);

case WebhookType.PAYMENT:
return this.isOptionValid(this.webhookConfigObject?.payment, consented);
}
}

private isOptionValid(option: WebhookConfigOption | undefined, consented: boolean): boolean {
return (
option === WebhookConfigOption.TRUE ||
(option === WebhookConfigOption.CONSENT_ONLY && consented) ||
(option === WebhookConfigOption.WALLET_ONLY && !consented)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import { CachedRepository } from 'src/shared/repositories/cached.repository';
import { EntityManager } from 'typeorm';
import { Integrator } from './integrator.entity';

@Injectable()
export class IntegratorRepository extends CachedRepository<Integrator> {
constructor(manager: EntityManager) {
super(Integrator, manager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateIntegratorDto } from './dto/create-integrator.dto';
import { Integrator } from './integrator.entity';
import { IntegratorRepository } from './integrator.repository';

@Injectable()
export class IntegratorService {
constructor(private readonly repo: IntegratorRepository) {}

async createIntegrator(dto: CreateIntegratorDto): Promise<Integrator> {
const entity = this.repo.create(dto);
return this.repo.save(entity);
}

async updateIntegrator(id: number, dto: CreateIntegratorDto): Promise<Integrator> {
const entity = await this.repo.findOneBy({ id });
if (!entity) throw new NotFoundException('Integrator not found');

Object.assign(entity, dto);

return this.repo.save(entity);
}
}
Loading
Loading