05 kyc flow
KYC-верификация личности: от загрузки документов до активного кошелька.
Что видит пользователь¶
| Экран в приложении | Технический статус | Что нужно сделать |
|---|---|---|
| Нужна верификация | kyc_pending |
Загрузить документы и селфи |
| Обрабатывается | in_review |
Ждать (OCR ~1–3 мин) |
| Проверьте данные | ocr_complete |
Подтвердить OCR-результат |
| Ошибка распознавания | ocr_failed |
Повторить / переснять документ |
| На проверке | pending_operator_review |
Ждать оператора (до 24 ч) |
| Кошелёк активен | fully_verified |
Готово |
Статус auto_approved — промежуточный технический; пользователь его не видит (обрабатывается автоматически и сразу ведёт к fully_verified).
Полный процесс¶
sequenceDiagram
participant U as Пользователь
participant App as onewallet_app
participant AC as Auth Center
participant KYC as kyc-service
participant S3 as S3 (документы)
participant Op as Оператор
U->>App: загрузить фото документа + селфи
App->>S3: загрузить файлы (presigned URL)
App->>AC: submitKycDocuments(s3Keys)
AC->>KYC: запрос OCR + сравнение лиц (async)
KYC-->>KYC: OCR извлекает данные
KYC-->>KYC: faceMatchScore = сходство (0.0–1.0)
KYC-->>AC: webhook — ocrResult + faceMatchScore
alt faceMatchScore >= 0.85
AC->>AC: autoVerify → kyc.status=auto_approved
AC->>AC: user_profile.accountTier → STANDARD
App->>AC: confirmKycData(fullName, phone, ...)
AC->>AC: kyc.status=pending_operator_review
AC->>AC: WalletCreationService.ensureWallet → TigerBeetle account
Op->>AC: finalizeVerification(userId)
AC->>AC: kyc.status=fully_verified
AC->>AC: user.status=active
AC->>AC: user_profile.isLocked=true
AC-->>App: push — кошелёк активен
else faceMatchScore < 0.85
AC->>AC: submitForManualReview → kyc.status=manual_review
App->>AC: confirmKycData(fullName, phone, ...)
AC->>AC: kyc.status=pending_operator_review
AC->>AC: WalletCreationService.ensureWallet → TigerBeetle account
Op->>AC: operatorApprove(userId)
AC->>AC: kyc.status=approved
Op->>AC: finalizeVerification(userId)
AC->>AC: kyc.status=fully_verified
AC->>AC: user.status=active
AC->>AC: user_profile.isLocked=true
AC-->>App: push — кошелёк активен
end
Note over AC,Op: [PLAN] После active — регистрация внешнего IPPS-кошелька
AC-.->Op: [PLAN] POST /accounts/external/ipps via PM → IPPS adapter
Ключевые точки:
- TigerBeetle-аккаунт создаётся в
confirmKycData(до апрува оператора) — пользователь получаетtbAccountIdраньше, чем становитсяactive. - Порог автоверификации:
faceMatchScore >= 0.85. Настраивается без изменения кода. - При
auto_approvedоператор всё равно вызываетfinalizeVerification— это точка ответственности за активацию.
Роль оператора¶
Оператор работает через Admin Panel (SvelteKit). Три действия в ручном процессе:
| Действие | Результат |
|---|---|
operatorApprove |
kyc.status = approved — документы приняты, ждём финализации |
operatorReject |
kyc.status = pending, user.status = kyc_pending — пользователь может повторно загрузить документы |
finalizeVerification |
kyc.status = fully_verified, user.status = active, профиль заблокирован |
Отклонение (operatorReject) сбрасывает статус пользователя до kyc_pending — это позволяет мобильному приложению открыть экран загрузки повторно. Без сброса пользователь застрял бы на экране ожидания.
После верификации¶
После успешного finalizeVerification происходит:
| Изменение | Детали |
|---|---|
user.status = active |
Пользователь получает доступ ко всем функциям кошелька |
user_profile.isLocked = true |
Персональные данные нельзя изменить без участия оператора |
user_profile.accountTier = STANDARD |
Если tier не был переопределён администратором вручную |
| TigerBeetle кошелёк активен | tbAccountId уже существовал с момента confirmKycData; транзакции теперь разблокированы |
AuditLog запись |
action = profile_locked, performedBy = operator |
[PLAN] IPPS внешний банковский кошелёк — после перехода в active Auth Center должен инициировать регистрацию внешнего кошелька через PM → IPPS adapter (POST /accounts/external/ipps). PM запишет ipps_wallet_id в pm.tb_account_map. PII передаётся только в памяти PM-процесса и никогда не сохраняется в pm.*. Endpoint на стороне PM пока не реализован (см. TODO(IPPS) в kyc_service.dart).