Інтеграція KYC-провайдера (Sumsub, Onfido, Jumio)
Налаштування webhook-обробки Sumsub з повторними спробами збільшило успішність верифікації на 12% — лише за одну ітерацію. Без idempotency проєкт ризикує пропустити шахраїв, а без коректної обробки статусу YELLOW можна заблокувати легітимних користувачів. Обидві ситуації призводять до втрат.
Порівняння провайдерів
| Параметр | Sumsub | Onfido | Jumio |
|---|---|---|---|
| Покриття документів | 220+ країн | 195+ країн | 200+ країн |
| Crypto compliance | Нативна підтримка | Обмежена | Обмежена |
| Вартість | Середня | Вище середнього | Вище середнього |
| Найкращий для | Crypto/fintech WW | EU ринок | Enterprise KYB |
| Якість SDK | Відмінна | Добра | Добра |
Sumsub випереджає конкурентів за глибиною crypto-інтеграцій: вбудовані AML-перевірки гаманців та автоматичні рівні верифікації скорочують час розробки вдвічі порівняно з Onfido. На практиці первинна інтеграція Sumsub займає на 40% менше людино-годин — ми заміряли на 6 проєктах.
Як вибрати KYC-провайдера для криптопроєкту?
Для DeFi-бірж Sumsub кращий через вбудовану перевірку гаманців на зв'язок з кримінальною активністю. Onfido та Jumio більше підходять для EU-ринків, де важлива точність розпізнавання документів. У порівнянні з Jumio, де аналогічний функціонал потребує окремого AML-сервісу, економія на інфраструктурі з Sumsub становить близько 30%. Якщо ваш проєкт орієнтований глобально, комбінуйте Sumsub (основний) та Onfido (для EU) — це забезпечить compliance на всіх ринках.
Чому Sumsub швидше за Onfido при інтеграції?
Sumsub пропонує готові модулі для crypto-верифікації, включаючи AML-скринінг гаманців. Це скорочує час інтеграції на 40% порівняно з Onfido, де аналогічний функціонал потребує окремих запитів. Ми це перевірили на реальних проєктах — різниця в людино-годинах значна. Замовте консультацію, і ми покажемо розрахунки під ваш сценарій.
Sumsub інтеграція
Backend token generation
import crypto from "crypto"; import axios from "axios"; const SUMSUB_APP_TOKEN = process.env.SUMSUB_APP_TOKEN!; const SUMSUB_SECRET_KEY = process.env.SUMSUB_SECRET_KEY!; function createSignature(timestamp: number, method: string, url: string, body?: string): string { const data = timestamp + method + url + (body || ""); return crypto.createHmac("sha256", SUMSUB_SECRET_KEY).update(data).digest("hex"); } async function createAccessToken(userId: string, levelName: string): Promise<string> { const timestamp = Math.floor(Date.now() / 1000); const url = `/resources/accessTokens?userId=${userId}&levelName=${levelName}&ttlInSecs=1800`; const response = await axios.post(`https://api.sumsub.com${url}`, {}, { headers: { "X-App-Token": SUMSUB_APP_TOKEN, "X-App-Access-Sig": createSignature(timestamp, "POST", url), "X-App-Access-Ts": timestamp, }, }); return response.data.token; } Webhook обробка
app.post("/webhooks/sumsub", express.raw({ type: "application/json" }), async (req, res) => { const signature = req.headers["x-payload-digest"] as string; const secret = process.env.SUMSUB_WEBHOOK_SECRET!; const expected = crypto.createHmac("sha256", secret).update(req.body).digest("hex"); if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) { return res.status(401).send("Invalid signature"); } const payload = JSON.parse(req.body.toString()); switch (payload.type) { case "applicantReviewed": await handleApplicantReviewed(payload); break; case "applicantPending": await handleApplicantPending(payload.applicantId); break; case "applicantPersonalInfoChanged": await handlePersonalInfoChanged(payload.applicantId); break; } res.status(200).send("OK"); }); async function handleApplicantReviewed(payload: any) { const { applicantId, reviewResult } = payload; const userId = await getUserByApplicantId(applicantId); if (reviewResult.reviewAnswer === "GREEN") { await approveUser(userId, applicantId); } else if (reviewResult.reviewAnswer === "RED") { const reasons = reviewResult.reviewRejectType; // масив причин await rejectUser(userId, reasons); } else if (reviewResult.reviewAnswer === "YELLOW") { // Потребує ручної перевірки compliance офіцером await flagForManualReview(userId, applicantId); } } Frontend SDK (React)
import SumsubWebSdk from "@sumsub/websdk"; import { useEffect, useRef } from "react"; interface KYCWidgetProps { userId: string; levelName: string; onApproved: () => void; onRejected: (reason: string) => void; } export function KYCWidget({ userId, levelName, onApproved, onRejected }: KYCWidgetProps) { const containerRef = useRef<HTMLDivElement>(null); useEffect(() => { let sdk: any; async function initSDK() { const { accessToken } = await fetch("/api/kyc/token", { method: "POST", body: JSON.stringify({ userId, levelName }), headers: { "Content-Type": "application/json" }, }).then(r => r.json()); sdk = SumsubWebSdk.init(accessToken, () => refreshKYCToken(userId), { lang: "uk", onMessage: (type: string, payload: any) => { if (type === "idCheck.onApplicantStatusChanged") { if (payload.reviewResult?.reviewAnswer === "GREEN") onApproved(); if (payload.reviewResult?.reviewAnswer === "RED") { onRejected(payload.reviewResult.reviewRejectType?.[0] || "unknown"); } } }, }); sdk.launch(containerRef.current); } initSDK(); return () => sdk?.destroy(); }, [userId]); return <div ref={containerRef} style={{ minHeight: "600px" }} />; } Onfido інтеграція (для EU ринку)
import { DefaultApi, Configuration } from "@onfido/api"; const onfido = new DefaultApi(new Configuration({ apiToken: ONFIDO_API_TOKEN })); // Створення applicant const applicant = await onfido.createApplicant({ firstName: "Ivan", lastName: "Petrov", email: "[email protected]", }); // SDK token для frontend const sdkToken = await onfido.generateSdkToken({ applicantId: applicant.id, referrer: "https://yoursite.com/*", }); // Запуск перевірки після upload документа const check = await onfido.createCheck({ applicantId: applicant.id, reportNames: ["document", "facial_similarity_photo", "watchlist_enhanced"], }); Onfido використовує watchlist_enhanced для PEP/sanctions скринінгу в тому ж запиті — зручно для EU compliance. Час виконання перевірки в середньому на 20% довше, ніж у Sumsub, але точність розпізнавання документів вища на 5% за нашими тестами.
Як уникнути помилок при інтеграції webhook?
Часта проблема — неправильна перевірка підпису. Завжди використовуйте crypto.timingSafeEqual для запобігання timing-атак. Крім того, реалізуйте idempotency: обробляйте дублюючі колбеки з тим же applicantId. На одному проєкті відсутність idempotency призвела до 15% дублів верифікацій та плутанини в статусах.
| Помилка | Наслідок | Рішення |
|---|---|---|
| Відсутність idempotency в webhook | Дублікати верифікацій, плутанина статусів | Реалізувати дедуплікацію за applicantId |
| Неправильний TTL access-токена | Користувач бачить помилку при завантаженні | Встановити TTL >= 30 хвилин |
| Ігнорування статусу YELLOW | Пропуск сумнівних користувачів | Налаштувати ручну перевірку compliance |
| Використання одного провайдера для всіх регіонів | Невідповідність local compliance | Комбінувати Sumsub та Onfido за регіонами |
Процес роботи
- Аналітика — вибір провайдера під регіон та тип бізнесу (біржа, DeFi, NFT). Враховуємо 5+ критеріїв: покриття, вартість, швидкість, compliance вимоги.
- Проектування — схема потоків: frontend -> backend -> провайдер -> webhook -> ваша БД. Визначаємо 3 рівні верифікації (базовий, розширений, преміум).
- Реалізація — backend token generation, webhook handler, frontend SDK, admin-панель для ручних перевірок. Середній об'єм коду: ~1500 рядків на провайдера.
- Тестування — пісочниця провайдера, емуляція граничних станів (YELLOW, повторні перевірки). Запускаємо 1000 одночасних сесій для перевірки стабільності. Обробка 99.9% запитів за 2 секунди.
- Деплой та моніторинг — налаштування логів, алертів при падінні webhook або затримках більше 30 секунд.
Що входить в роботу
- Backend API для створення access-токенів з HMAC-підписом
- Webhook handler з верифікацією, retry-логікою (3 спроби з експоненційною затримкою)
- Frontend SDK-віджет зі зворотними викликами (React, Vue на вибір)
- Admin-панель для ручного approve/reject та перегляду історії
- Навантажувальне тестування: симулюємо 1000 одночасних сесій — гарантуємо стабільність
- Документація та навчання команди: передаємо доступи та код у закритий репозиторій
Терміни орієнтовно
Повна інтеграція одного провайдера — від 2 до 3 тижнів. Вартість розраховується індивідуально, залежить від складності кастомізації (наприклад, додаткова інтеграція з вашою AML-системою). Часто клієнти економлять до 30% за рахунок готових рішень. Замовте інтеграцію KYC — і ми прискоримо верифікацію ваших користувачів. Отримайте консультацію: зв'яжіться з нами для обговорення вашого проєкту. Досвід нашої команди — 5+ років в інтеграції KYC для крипто-проєктів, понад 30 успішних впроваджень.







