Перейти к основному содержимому

GET /api/v1/profile/verification — Моя заявка на верификацию (getMyVerification)

Назначение

Вернуть текущую (активную) или, если активной нет, последнюю заявку на верификацию/квалификацию инвестора (KYC/AML) для отрисовки раздела «Верификация инвестора» ЛК: статус заявки, вехи решения (отправлена/взята в ревью/завершена), машиночитаемую и человекочитаемую причину отказа, а также денормализованный статус верификации аккаунта и флаги доступных действий (canSubmit/canRevoke). Расшифрованный снапшот данных не возвращается (self-просмотр видит только метаданные и статус; расшифровка — отдельная аудируемая admin-операция ревью, verification-request.md). За feature flag profile.verification.enabled. Вызывает инвестор из ЛК.

Metadata

ПолеЗначение
Сервис/контекстprofile
API areapublic
Feature groupverification
Статусdraft
Документdocs/06-services/profile/api/public/get-my-verification.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (Bearer JWT) — только UserType = Investor (INV-VRF-5, profile.md)
Auth policy / Permissionsauthenticated; permission profile.verification.submit (тот же гейт покрывает self-просмотр — отдельного view-permission нет)
Scope (RBAC)self (платформенный, вне tenant)
Record-levelтолько свои заявки: VerificationRequestEntity.OwnerUserId = UserId из claim sub
Tenant isolationне применяется (ADR-0052: данные инвестора вне tenant)
Auditread: не аудируется — SnapshotCipher не расшифровывается, наружу идут только метаданные/статус (расшифровка снапшота ревьюером — отдельное событие VerificationSnapshotAccessedEvent)

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/profile/verification
Success status200
Idempotency headerне применяется (безопасный read)
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
UserIdidentity claims (sub)GuidДавалидный JWT; NotEmpty401 без токена; record-level по OwnerUserId

Модель ответа MyVerificationModel

Дом полей заявки — verification-request.md, статуса аккаунта — profile.md; здесь только проекция (дельта) без чувствительных значений снапшота.

ПолеТипОбязательностьИсточникMaskingОписание
requestIdGuid?НетVerificationRequestEntity.Idnull, если у инвестора ещё не было ни одной заявки.
statusMyVerificationStatusДаVerificationRequestEntity.Status или NoneNone / Draft / Submitted / InReview / Approved / Rejected (enum ниже).
personIdGuid?НетVerificationRequestEntity.PersonIdПерсона-предмет проверки (Relation = Self); null без заявки.
requisiteTemplateIdGuid?НетVerificationRequestEntity.RequisiteTemplateIdШаблон, из которого предзаполнена заявка; информационно.
submittedAtDateTimeOffset?НетVerificationRequestEntity.SubmittedAtМомент отправки в очередь ревью.
reviewStartedAtDateTimeOffset?НетVerificationRequestEntity.ReviewStartedAtМомент взятия ревьюером (Submitted → InReview).
completedAtDateTimeOffset?НетVerificationRequestEntity.CompletedAtМомент решения (Approved/Rejected).
rejectionCodeVerificationRejectionCode?НетVerificationRequestEntity.RejectionCodeIllegibleScans / DataMismatch / DocumentExpired / SuspectedFraud / Other; только при Rejected.
rejectionReasonstring?НетVerificationRequestEntity.RejectionReasonЧеловекочитаемое пояснение отказа (без цитирования значений документов — правило ревьюера); DecisionComment наружу не отдаётся.
scanCountintДаVerificationRequestEntity.ScanList.Items.CountКол-во сканов, приложенных к заявке (метаданные, без содержимого/ссылок); 0 без заявки.
profileVerificationStatusProfileVerificationStatusДаProfileEntity.VerificationStatusNotVerified / Pending / Verified / Rejected — денормализованный статус аккаунта.
canSubmitboolДаcomputed (ниже)Можно инициировать/отправить верификацию.
canRevokeboolДаcomputed (ниже)Можно отозвать отправленную заявку в черновик.

Enum MyVerificationStatus

Enum передаётся строкой: значения VerificationRequestStatus (verification-request.md) плюс None — «заявок ещё не было».

ЗначениеУсловие
NoneУ инвестора нет ни одной заявки (requestId = null).
Draft / Submitted / InReview / Approved / RejectedКопия VerificationRequestEntity.Status найденной заявки.

Computed-флаги (canSubmit / canRevoke)

ПолеПравило вычисления
canSubmitSelf-service разрешён (UserType = InvestorContactsManagedBy = SelfServiceStatus = Active, INV-PRF-2..4) ∧ flag profile.verification.enabledprofileVerificationStatus != Verified (INV-VRF-9) ∧ (status ∈ {None, Rejected} → создать новую, либо status = Draft → отправить).
canRevokeSelf-service разрешён ∧ status = Submitted (отзыв Submitted → Draft; из InReview запрещён — INV-VRF-7, UC-PRF-003 A2).

Алгоритм

  1. Контекст и доступ. Операцию выполняет инвестор (UserId из токена); tenant не применяется (ADR-0052). Если feature flag profile.verification.enabled выключен → PROFILE_VERIFICATION_DISABLED (404, раздел недоступен).
  2. Проверка входа. UserId присутствует в claims. Состояние заявки чтение не ограничивает (видны все статусы, включая терминальные).
  3. Загрузка. Найти профиль по UserId; если его ещё нет (профиль ещё не создан консюмером pin.identity.users.v1, identity-users-sync.md) → PROFILE_NOT_FOUND (клиент повторяет). Тип пользователя — Investor; иначе PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE (403, INV-PRF-4/INV-VRF-5). Затем выбрать «текущую/последнюю заявку» владельца — самую свежую по CreatedAt (активная незавершённая заявка единственна, INV-VRF-1, и всегда самая свежая; иначе — последняя терминальная). Снапшот и документы персоны не загружаются.
  4. Маппинг. Спроецировать VerificationRequestEntity → MyVerificationModel (таблица выше); при отсутствии заявки — пустая модель: requestId = null, status = None, scanCount = 0, остальные заявочные поля null. profileVerificationStatus = profile.VerificationStatus в любом случае. canSubmit/canRevoke вычисляются из UserType + ContactsManagedBy + Status + profileVerificationStatus + status + feature flag (правила выше). SnapshotCipher не расшифровывается — наружу только метаданные и статус.
  5. Результат. Модель MyVerificationModel; HTTP 200. Freshness: profileVerificationStatus денормализован и меняется в одной транзакции с решением по заявке (INV-PRF-6), поэтому согласован с status.

Диаграмма

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
— (auth)401Нет/просрочен JWTбез телаre-login
ValidationError400Пустой UserId в claimsперечень невалидных полейno retry
PROFILE_VERIFICATION_DISABLED404Feature flag profile.verification.enabled выключен«Раздел верификации недоступен»no retry
PROFILE_NOT_FOUND404Профиль ещё не создан консюмером identity (лаг реплики)«Профиль ещё создаётся, повторите»retry с backoff (1–5 с)
PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE403UserType != Investor (INV-PRF-4, INV-VRF-5)«Верификация доступна только инвесторам»no retry

Совместимость и наблюдаемость

  • Новые optional поля ответа (в т.ч. новые флаги) — additive, без breaking change; удаление/переименование/смена типа — breaking (migration + rollout). Unknown значение rejectionCode/ status/profileVerificationStatus у клиента — трактовать консервативно («статус неизвестен», кнопки действий скрыть).
  • Logs: UserId, status, requestId, latency (без rejectionReason и значений PII); metrics: profile_api_operations_total{operation="getMyVerification",result}; traces — OTel. Dashboard profile-overview; runbook — replay DLQ identity-sync (при всплеске PROFILE_NOT_FOUND).

Acceptance Criteria

  • Given инвестор с отправленной заявкой (Submitted), When GET, Then 200; status = Submitted, submittedAt заполнен, scanCount = число элементов ScanList, снапшот не расшифрован; canRevoke = true, canSubmit = false.
  • Given у инвестора не было ни одной заявки, When GET, Then 200; requestId = null, status = None, scanCount = 0, profileVerificationStatus — из профиля; canSubmit вычислен по правилам.
  • Given заявка Rejected, When GET, Then возвращаются rejectionCode и rejectionReason (DecisionComment наружу не отдаётся); canSubmit = true (можно создать новую).
  • Given заявка InReview, When GET, Then canRevoke = false (INV-VRF-7).
  • Given у инвестора несколько заявок в истории, When GET, Then возвращается самая свежая по CreatedAt (активная незавершённая единственна — INV-VRF-1).
  • Given feature flag profile.verification.enabled выключен, When GET, Then PROFILE_VERIFICATION_DISABLED (404).
  • Given UserType != Investor, When GET, Then PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE (403).
  • Given профиль ещё не создан консюмером identity, When GET, Then PROFILE_NOT_FOUND (404), retry.
  • Given любое решение по заявке, Then profileVerificationStatus согласован со status (меняются в одной транзакции, INV-PRF-6).

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы