Перейти к содержанию

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).