Розробка системи KYC/AML для криптобіржі
KYC/AML для криптобіржі — це не форма "введіть паспорт". Це комплексна система, яка вирішує нормативні вимоги ліцензування, управляє ризиками та при цьому не убиває conversion rate. Більшість бірж втрачають 40-60% користувачів на KYC воронці — це проблема архітектури та UX, не тільки регуляції.
Багаторівнева структура KYC
Стандарт індустрії — багаторівнева верифікація:
Tier 0 (без KYC): тільки перегляд платформи. Без транзакцій.
Tier 1 (Email + AML check): до $1,000 USD еквіваленту на місяць. Тільки крипто депозит та виведення. Автоматичний скрининг гаманця через Chainalysis або Elliptic при кожному депозиті. Час верифікації: < 1 хвилини.
Tier 2 (Full KYC): до $50,000/місяць. Паспорт + liveness check. Провайдер: Sumsub, Onfido, Jumio. Час: автоматично 2-5 хвилин, ручна перевірка до 24 годин для складних випадків. Розблокується: фіат депозит/виведення, крипто виведення без обмежень.
Tier 3 (Enhanced Due Diligence): без обмежень. Source of Funds + Source of Wealth + розширена перевірка біографії. Тільки для VIP клієнтів, ручна обробка compliance офіцером.
Архітектура KYC сервісу
// KYC State Machine
enum KYCStatus {
NOT_STARTED = 'not_started',
PENDING = 'pending',
UNDER_REVIEW = 'under_review',
APPROVED = 'approved',
REJECTED = 'rejected',
RESUBMISSION_REQUIRED = 'resubmission_required',
}
interface UserKYCProfile {
userId: string;
tier: 0 | 1 | 2 | 3;
status: KYCStatus;
applicantId?: string; // Sumsub applicant ID
approvedAt?: Date;
expiresAt?: Date; // переверифікація кожні N років
riskScore: number; // 0-100, розраховується при апруве
pep: boolean; // Politically Exposed Person
sanctioned: boolean;
walletRiskLevels: Map<string, WalletRisk>; // кешовані оцінки гаманців
}
class KYCService {
async initiateKYC(userId: string, targetTier: number): Promise<{ accessToken: string }> {
const accessToken = await this.sumsubClient.createApplicantToken(
userId,
this.getLevelName(targetTier)
);
await this.db.updateUserKYC(userId, {
status: KYCStatus.PENDING,
initiatedAt: new Date()
});
return { accessToken };
}
async processWebhook(payload: SumsubWebhook): Promise<void> {
const { applicantId, reviewResult, type } = payload;
const userId = await this.db.getUserByApplicantId(applicantId);
if (type === 'applicantReviewed' && reviewResult.reviewAnswer === 'GREEN') {
const riskData = await this.calculateRiskScore(applicantId);
await this.db.updateUserKYC(userId, {
status: KYCStatus.APPROVED,
tier: 2,
approvedAt: new Date(),
expiresAt: new Date(Date.now() + 2 * 365 * 24 * 60 * 60 * 1000), // 2 роки
riskScore: riskData.score,
pep: riskData.pep,
});
await this.notifyUser(userId, 'kyc_approved');
await this.unlockFeatures(userId, 2);
} else if (reviewResult.reviewAnswer === 'RED') {
const reason = this.mapRejectionReason(reviewResult.reviewRejectType);
await this.handleRejection(userId, reason);
}
}
private async calculateRiskScore(applicantId: string): Promise<RiskData> {
const applicantData = await this.sumsubClient.getApplicant(applicantId);
// PEP перевірка
const pepResult = await this.amlProvider.checkPEP(
applicantData.info.firstName,
applicantData.info.lastName,
applicantData.info.dob,
applicantData.info.country
);
// Перевірка санкцій (OFAC, EU, UN)
const sanctionsResult = await this.amlProvider.checkSanctions(applicantData.info);
// Risk scoring
let score = 0;
if (pepResult.isPEP) score += 40;
if (sanctionsResult.isSanctioned) score = 100; // автоматичне відхилення
if (HIGH_RISK_COUNTRIES.includes(applicantData.info.country)) score += 20;
return { score, pep: pepResult.isPEP, sanctioned: sanctionsResult.isSanctioned };
}
}
On-chain AML скрининг
Кожен вхідний депозит та кожне виведення перевіряються через wallet screening:
class WalletScreeningService {
async screenDepositAddress(
walletAddress: string,
asset: string,
amount: number,
userId: string
): Promise<ScreeningResult> {
// Кеш: однаковий адрес не перепроверяємо кожен раз
const cached = await this.cache.get(`wallet:${walletAddress}`);
if (cached && cached.age < 3600) return cached.result; // 1 година TTL
const [chainalysisResult, ellipticResult] = await Promise.all([
this.chainalysis.getAddressRisk(walletAddress, asset),
this.elliptic.getWalletRisk(walletAddress),
]);
const riskScore = Math.max(chainalysisResult.riskScore, ellipticResult.riskScore);
const categories = [...new Set([
...chainalysisResult.categories,
...ellipticResult.categories,
])];
const result: ScreeningResult = {
allowed: riskScore < 70 && !this.hasBlockedCategory(categories),
riskScore,
categories,
requiresReview: riskScore >= 40 && riskScore < 70,
};
await this.cache.set(`wallet:${walletAddress}`, { result, age: Date.now() });
await this.logScreening(userId, walletAddress, result);
if (!result.allowed) {
await this.alertComplianceTeam(userId, walletAddress, result);
}
return result;
}
private hasBlockedCategory(categories: string[]): boolean {
const BLOCKED = ['darknet_market', 'ransomware', 'stolen_funds', 'sanctions'];
return categories.some(c => BLOCKED.includes(c));
}
}
Transaction Monitoring (TM)
Поточний моніторинг транзакцій для виявлення підозрілих паттернів після верифікації:
Structuring detection: багато транзакцій чуть нижче reporting threshold (класичний smurfing).
Velocity monitoring: різке збільшення активності — 10x від нормального обсягу на день.
Round-trip detection: кошти виводяться та повертаються через декілька хопів.
Mixing/tumbling indicators: транзакції через відомі mixing сервіси (Tornado Cash та аналоги).
class TransactionMonitor {
async analyzeTransaction(tx: Transaction): Promise<AlertLevel> {
const userHistory = await this.db.getUserTxHistory(tx.userId, 30); // 30 днів
const checks = await Promise.all([
this.checkStructuring(tx, userHistory),
this.checkVelocity(tx, userHistory),
this.checkGeographicAnomalies(tx),
this.checkTimePatterns(tx, userHistory),
]);
const maxLevel = Math.max(...checks.map(c => c.level));
if (maxLevel >= AlertLevel.HIGH) {
await this.createSAR(tx, checks.filter(c => c.level >= AlertLevel.MEDIUM));
}
return maxLevel;
}
private async checkStructuring(tx: Transaction, history: Transaction[]): Promise<Check> {
const threshold = await this.getReportingThreshold(tx.currency);
// Декілька транзакцій протягом 24 годин суммарно вище threshold,
// кожна по окремості нижче
const last24h = history.filter(h =>
Date.now() - h.timestamp < 86400000 && h.amount < threshold
);
const total24h = last24h.reduce((sum, h) => sum + h.amount, 0) + tx.amount;
if (total24h >= threshold * 0.9 && last24h.length >= 3) {
return { level: AlertLevel.HIGH, reason: 'structuring_detected' };
}
return { level: AlertLevel.NONE };
}
}
SAR (Suspicious Activity Report) автоматизація
При спрацюванні алертів — автоматичне формування чорновику SAR для compliance офіцера:
async function generateSARDraft(
userId: string,
transactions: Transaction[],
alerts: Alert[]
): Promise<SARDocument> {
const user = await getUserKYCData(userId);
return {
reportType: 'SUSPICIOUS_ACTIVITY',
filingEntity: COMPANY_DETAILS,
subject: {
name: `${user.firstName} ${user.lastName}`,
address: user.residenceAddress,
dob: user.dateOfBirth,
idNumber: user.documentNumber,
},
suspiciousActivity: {
dateRange: { from: transactions[0].date, to: transactions[transactions.length - 1].date },
totalAmount: transactions.reduce((sum, t) => sum + t.usdValue, 0),
description: generateNarrative(alerts, transactions),
alertTypes: alerts.map(a => a.type),
},
supportingTransactions: transactions.map(formatForSAR),
};
}
Технічний стек
| Компонент | Технологія |
|---|---|
| KYC провайдер | Sumsub (основний) / Onfido (резерв) |
| AML on-chain | Chainalysis KYT + Elliptic |
| PEP/Sanctions | Refinitiv World-Check або ComplyAdvantage |
| Transaction monitoring | кастомний + Chainalysis Reactor |
| SAR management | кастомний модуль compliance |
| Backend | Node.js + TypeScript + PostgreSQL |
| Queue | BullMQ (Redis) для async обробки |
Сроки розробки
| Компонент | Срок |
|---|---|
| KYC tier система + Sumsub інтеграція | 3-4 тиж |
| AML wallet скрининг | 2 тиж |
| Transaction monitoring двигун | 3-4 тиж |
| SAR management система | 2 тиж |
| Compliance dashboard | 2-3 тиж |
| Testing + compliance review | 2-3 тиж |
Повна KYC/AML система для біржі: 3-4 місяці розробки.







