GET /api/v1/profile/verification — Моя заявка на верификацию (getMyVerification)
Назначение
Вернуть текущую (активную) или, если активной нет, последнюю заявку на верификацию/квалификацию
инвестора (KYC/AML) для отрисовки раздела «Верификация инвестора» ЛК: статус заявки, вехи решения
(отправлена/взята в ревью/завершена), машиночитаемую и человекочитаемую причину отказа, а также
денормализованный статус верификации аккаунта и флаги доступных действий (canSubmit/canRevoke).
Расшифрованный снапшот данных не возвращается (self-просмотр видит только метаданные и статус;
расшифровка — отдельная аудируемая admin-операция ревью, verification-request.md).
За feature flag profile.verification.enabled. Вызывает инвестор из ЛК.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | profile |
| API area | public |
| Feature group | verification |
| Статус | draft |
| Документ | docs/06-services/profile/api/public/get-my-verification.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | User (Bearer JWT) — только UserType = Investor (INV-VRF-5, profile.md) |
| Auth policy / Permissions | authenticated; permission profile.verification.submit (тот же гейт покрывает self-просмотр — отдельного view-permission нет) |
| Scope (RBAC) | self (платформенный, вне tenant) |
| Record-level | только свои заявки: VerificationRequestEntity.OwnerUserId = UserId из claim sub |
| Tenant isolation | не применяется (ADR-0052: данные инвестора вне tenant) |
| Audit | read: не аудируется — SnapshotCipher не расшифровывается, наружу идут только метаданные/статус (расшифровка снапшота ревьюером — отдельное событие VerificationSnapshotAccessedEvent) |
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/v1/profile/verification |
| Success status | 200 |
| Idempotency header | не применяется (безопасный read) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
UserId | identity claims (sub) | Guid | Да | валидный JWT; NotEmpty | 401 без токена; record-level по OwnerUserId |
Модель ответа MyVerificationModel
Дом полей заявки — verification-request.md, статуса аккаунта — profile.md; здесь только проекция (дельта) без чувствительных значений снапшота.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
requestId | Guid? | Нет | VerificationRequestEntity.Id | — | null, если у инвестора ещё не было ни одной заявки. |
status | MyVerificationStatus | Да | VerificationRequestEntity.Status или None | — | None / Draft / Submitted / InReview / Approved / Rejected (enum ниже). |
personId | Guid? | Нет | VerificationRequestEntity.PersonId | — | Персона-предмет проверки (Relation = Self); null без заявки. |
requisiteTemplateId | Guid? | Нет | VerificationRequestEntity.RequisiteTemplateId | — | Шаблон, из которого предзаполнена заявка; информационно. |
submittedAt | DateTimeOffset? | Нет | VerificationRequestEntity.SubmittedAt | — | Момент отправки в очередь ревью. |
reviewStartedAt | DateTimeOffset? | Нет | VerificationRequestEntity.ReviewStartedAt | — | Момент взятия ревьюером (Submitted → InReview). |
completedAt | DateTimeOffset? | Нет | VerificationRequestEntity.CompletedAt | — | Момент решения (Approved/Rejected). |
rejectionCode | VerificationRejectionCode? | Нет | VerificationRequestEntity.RejectionCode | — | IllegibleScans / DataMismatch / DocumentExpired / SuspectedFraud / Other; только при Rejected. |
rejectionReason | string? | Нет | VerificationRequestEntity.RejectionReason | — | Человекочитаемое пояснение отказа (без цитирования значений документов — правило ревьюера); DecisionComment наружу не отдаётся. |
scanCount | int | Да | VerificationRequestEntity.ScanList.Items.Count | — | Кол-во сканов, приложенных к заявке (метаданные, без содержимого/ссылок); 0 без заявки. |
profileVerificationStatus | ProfileVerificationStatus | Да | ProfileEntity.VerificationStatus | — | NotVerified / Pending / Verified / Rejected — денормализованный статус аккаунта. |
canSubmit | bool | Да | computed (ниже) | — | Можно инициировать/отправить верификацию. |
canRevoke | bool | Да | computed (ниже) | — | Можно отозвать отправленную заявку в черновик. |
Enum MyVerificationStatus
Enum передаётся строкой: значения VerificationRequestStatus
(verification-request.md) плюс None — «заявок ещё не было».
| Значение | Условие |
|---|---|
None | У инвестора нет ни одной заявки (requestId = null). |
Draft / Submitted / InReview / Approved / Rejected | Копия VerificationRequestEntity.Status найденной заявки. |
Computed-флаги (canSubmit / canRevoke)
| Поле | Правило вычисления |
|---|---|
canSubmit | Self-service разрешён (UserType = Investor ∧ ContactsManagedBy = SelfService ∧ Status = Active, INV-PRF-2..4) ∧ flag profile.verification.enabled ∧ profileVerificationStatus != Verified (INV-VRF-9) ∧ (status ∈ {None, Rejected} → создать новую, либо status = Draft → отправить). |
canRevoke | Self-service разрешён ∧ status = Submitted (отзыв Submitted → Draft; из InReview запрещён — INV-VRF-7, UC-PRF-003 A2). |
Алгоритм
- Контекст и доступ. Операцию выполняет инвестор (
UserIdиз токена); tenant не применяется (ADR-0052). Если feature flagprofile.verification.enabledвыключен →PROFILE_VERIFICATION_DISABLED(404, раздел недоступен). - Проверка входа.
UserIdприсутствует в claims. Состояние заявки чтение не ограничивает (видны все статусы, включая терминальные). - Загрузка. Найти профиль по
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, и всегда самая свежая; иначе — последняя терминальная). Снапшот и документы персоны не загружаются. - Маппинг. Спроецировать
VerificationRequestEntity → MyVerificationModel(таблица выше); при отсутствии заявки — пустая модель:requestId = null,status = None,scanCount = 0, остальные заявочные поляnull.profileVerificationStatus = profile.VerificationStatusв любом случае.canSubmit/canRevokeвычисляются изUserType+ContactsManagedBy+Status+profileVerificationStatus+status+ feature flag (правила выше).SnapshotCipherне расшифровывается — наружу только метаданные и статус. - Результат. Модель
MyVerificationModel; HTTP200. Freshness:profileVerificationStatusденормализован и меняется в одной транзакции с решением по заявке (INV-PRF-6), поэтому согласован сstatus.
Диаграмма
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
| — (auth) | 401 | Нет/просрочен JWT | без тела | re-login |
ValidationError | 400 | Пустой UserId в claims | перечень невалидных полей | no retry |
PROFILE_VERIFICATION_DISABLED | 404 | Feature flag profile.verification.enabled выключен | «Раздел верификации недоступен» | no retry |
PROFILE_NOT_FOUND | 404 | Профиль ещё не создан консюмером identity (лаг реплики) | «Профиль ещё создаётся, повторите» | retry с backoff (1–5 с) |
PROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE | 403 | UserType != 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. Dashboardprofile-overview; runbook — replay DLQ identity-sync (при всплескеPROFILE_NOT_FOUND).
Acceptance Criteria
- Given инвестор с отправленной заявкой (
Submitted), WhenGET, Then200;status = Submitted,submittedAtзаполнен,scanCount= число элементовScanList, снапшот не расшифрован;canRevoke = true,canSubmit = false. - Given у инвестора не было ни одной заявки, When
GET, Then200;requestId = null,status = None,scanCount = 0,profileVerificationStatus— из профиля;canSubmitвычислен по правилам. - Given заявка
Rejected, WhenGET, Then возвращаютсяrejectionCodeиrejectionReason(DecisionCommentнаружу не отдаётся);canSubmit = true(можно создать новую). - Given заявка
InReview, WhenGET, ThencanRevoke = false(INV-VRF-7). - Given у инвестора несколько заявок в истории, When
GET, Then возвращается самая свежая поCreatedAt(активная незавершённая единственна — INV-VRF-1). - Given feature flag
profile.verification.enabledвыключен, WhenGET, ThenPROFILE_VERIFICATION_DISABLED(404). - Given
UserType != Investor, WhenGET, ThenPROFILE_PERSONS_NOT_ALLOWED_FOR_USER_TYPE(403). - Given профиль ещё не создан консюмером identity, When
GET, ThenPROFILE_NOT_FOUND(404), retry. - Given любое решение по заявке, Then
profileVerificationStatusсогласован соstatus(меняются в одной транзакции, INV-PRF-6).
Обратные ссылки
Автоссылки: где используется этот документ.
- API (11): GET /api/v1/admin/profile/verification-queue — Очередь ревью KYC (listVerificationQueue), GET /api/v1/admin/profile/verification/{requestId}/snapshot — Аудируемый просмотр снапшота KYC (viewVerificationSnapshot), GET /api/v1/internal/profile/verification-status/{userId} — Статус верификации для m2m (getVerificationStatus), POST /api/v1/admin/profile/verification/{requestId}/approve — Одобрить заявку KYC (approveVerification), POST /api/v1/admin/profile/verification/{requestId}/reject — Отклонить заявку KYC (rejectVerification), POST /api/v1/admin/profile/verification/{requestId}/take — Взять заявку в ревью (takeVerificationInReview), POST /api/v1/profile/verification — Создание черновика заявки KYC (createVerificationRequest), POST /api/v1/profile/verification/{requestId}/revoke — Отзыв заявки KYC на доработку (revokeVerification), POST /api/v1/profile/verification/{requestId}/submit — Отправка заявки KYC на ревью (submitVerification), Profile API — реестр операций областей public / internal / admin, PUT /api/v1/profile/verification/{requestId} — Правка черновика заявки KYC (updateVerificationDraft)
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: verification-request.md (машина состояний, INV-VRF-1..9,
SnapshotCipher/ScanList), profile.md (денормализацияVerificationStatus, матрица возможностей, INV-PRF-2..6). - Действия раздела: create-verification-request.md, submit-verification.md, revoke-verification.md.
- Сценарий: UC-PRF-003-submit-and-approve-verification.md; соседний self-view — get-my-profile.md.
- События статуса наружу: verification-request-submitted.md, verification-completed.md.