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

POST /api/investors/auth/sign-up — Регистрация инвестора (signUp)

Назначение

Явная регистрация нового инвестора: телефон + имя, отправка СМС-кода подтверждения. Вызывается анонимным клиентом. В отличие от sign-up-or-sign-in.md (AddOrGet) занятый номер — ошибка (паттерн DUPLICATE_PHONE), а firstName обязателен. Вход завершает confirm-sign-in.md по выданному signInConfirmationRequestId.

Metadata

ПолеЗначение
Сервис/контекстidentity
API areapublic
Feature groupauth
Статусapproved
Документdocs/06-services/identity/api/investor/sign-up.md

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

ПроверкаЗначение
Actor typeAnonymous
Auth policy / Permissionsnone
Scope (RBAC)не применяется
Record-levelсвязка по PhoneNumber
Tenant isolationне применяется — создаётся инвестор (CompanyId = null, ADR-0052)
Auditcreate: RegisteredVia = SmsSignUp, CreatedByUserId = null

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/investors/auth/sign-up
Success status200
Idempotency headerне применяется; повтор с тем же номером → IDENTITY_USER_PHONE_TAKEN
Correlationсквозной trace через traceparent

Входные данные / параметры запроса (body SignUpForm)

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
phoneNumberbodystringДаmin 1, max 32; E.164; уникальность (INV-1)PII, маскирование
firstNamebodystringДаmin 1, max 128PII
IpAddress / UserAgentmiddlewarestring?Нет≤ 45 / ≤ 512rate-limit / анти-фрод
Реферальная привязка

referrerUrl в форме SignUpForm отсутствует — реферальная привязка при этом пути передаётся полем referrerUrl последующего confirm-sign-in.md либо через signUpOrSignIn.

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

ПолеТипОбязательностьИсточникMaskingОписание
signInConfirmationRequestIdstring (Guid)ДаSignInConfirmationRequest.IdПередаётся в confirm-sign-in.
realConfirmationCodeDispatchedboolДаverifierfalse — код реально не отправлялся (тестовый контур).

Алгоритм

  1. Контекст и доступ. Актор анонимный; из middleware известны IpAddress/UserAgent.
  2. Проверка входа и лимиты. phoneNumber непустой ≤ 32, firstName непустой ≤ 128 — иначе ValidationError; нормализация E.164. Rate-limit (INV-S4) — превышение ⇒ IDENTITY_CONFIRMATION_RATE_LIMITED (429). Уникальность: номер уже зарегистрирован ⇒ IDENTITY_USER_PHONE_TAKEN (паттерн DUPLICATE_PHONE; клиент предлагает вход).
  3. Что читаем. Только проверка дубля номера; других сущностей не требуется.
  4. Правила и маппинг. Создаётся инвестор — Type = Investor, CompanyId = null (INV-2), Status = Active, Name = FullName(firstName, lastName: ""), PhoneNumber = нормализованный, RegisteredVia = SmsSignUp, ContactsManagedBy = SelfService, SyncSource = Pin; фиксируется доменное событие UserRegisteredEvent. Сгенерировать код (CSPRNG); создать запрос подтверждения (цель SignIn, UserId, PhoneNumber = нормализованный, хэш кода (HMAC-SHA256), ExpiresAt = now + 5 мин, IpAddress, UserAgent) + доменное ConfirmationCodeRequestedEvent.
  5. Что меняется. В одном согласованном изменении создаются пользователь и запрос подтверждения (гашение прежних не требуется — номер новый; при гонке действует INV-S1 на шаге confirm). Гонка двойной регистрации отсекается ограничением уникальности номера → IDENTITY_USER_PHONE_TAKEN.
  6. События. UserUpsertedKafkaEventpin.identity.users.v1 (ключ = UserId; Id, Type = Investor, CompanyId = null, Status, Name, PhoneNumber в E.164 — доверенные потребители, RegisteredVia = SmsSignUp, ReferrerUrl = null, Version). Доменное ConfirmationCodeRequestedEvent — отправка СМС через verifier.
  7. Результат. signInConfirmationRequestId, realConfirmationCodeDispatched; токен не выдаётся — его вернёт confirm-sign-in; HTTP 200.

Диаграмма

Побочные эффекты

ТипДетали
Интеграционные событияUserUpsertedKafkaEvent / pin.identity.users.v1 / ключ = UserId (потребители: referral, profile, crm, messenger, user-activity, notifications)
Внешние вызовыСМС-verifier (timeout 5 с, 1 retry)
Проекции / поискrate-limit-счётчик
УведомленияСМС с кодом

Кросс-сервисный контракт и окно согласованности

  • Предоставляет: UserUpsertedKafkaEvent в топик pin.identity.users.v1, ContractVersion = 1, ключ = UserId; снапшот {Id, Type = Investor, CompanyId = null, Status, Name, PhoneNumber (E.164), RegisteredVia = SmsSignUp, ReferrerUrl = null, Version}. Потребители: referral, profile, crm, messenger, user-activity, notifications. В отличие от signUpOrSignIn реферальная привязка здесь не передаётся (поля referrerUrl в форме нет) — она приходит позже через confirmSignIn.referrerUrl.
  • Ожидает от других: ничего синхронно, кроме внешнего СМС-verifier (timeout 5 с, 1 retry).
  • Окно согласованности: учётка доступна в identity сразу; профиль, контакт, связывание anonymous-сессии в downstream — асинхронно (лаг — секунды). Токен на этом шаге не выдаётся.

Предусловия и постусловия

  • Пред: актор анонимный; phoneNumber (E.164) свободен (INV-1); firstName непустой ≤ 128; лимиты INV-S4 не превышены.
  • Пост (успех): создан инвестор (Type = Investor, CompanyId = null, Status = Active, RegisteredVia = SmsSignUp, ContactsManagedBy = SelfService, SyncSource = Pin); зафиксирован UserRegisteredEvent; опубликован UserUpsertedKafkaEvent; создан Pending-запрос SignIn; отправлен код. Вход завершит confirm-sign-in.md.

Acceptance Criteria

  • Given свободный номер и непустой firstName, When POST /sign-up, Then 200 {signInConfirmationRequestId, realConfirmationCodeDispatched}, создан инвестор, опубликован UserUpsertedKafkaEvent (key = UserId, ReferrerUrl = null), отправлен СМС-код.
  • Given номер уже зарегистрирован, When POST /sign-up, Then IDENTITY_USER_PHONE_TAKEN (400, паттерн DUPLICATE_PHONE); клиент предлагает вход. (Осознанное отличие от signUpOrSignIn, где занятый номер = вход.)
  • Given пустой/слишком длинный firstName или невалидный номер, When POST /sign-up, Then ValidationError (400).
  • Given два параллельных sign-up на один свободный номер, When оба доходят до создания, Then уникальность номера пропускает одного, второй получает IDENTITY_USER_PHONE_TAKEN.
  • Given превышен лимит СМС (INV-S4), When POST /sign-up, Then IDENTITY_CONFIRMATION_RATE_LIMITED (429).
  • Given verifier отказал, When POST /sign-up, Then IDENTITY_SMS_DISPATCH_FAILED (400).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Пустые/длинные поляСписок ошибок валидацииno retry
IDENTITY_USER_PHONE_TAKEN400Номер уже зарегистрирован (INV-1; паттерн DUPLICATE_PHONE)«Номер уже используется — войдите»no retry (перейти ко входу)
IDENTITY_CONFIRMATION_RATE_LIMITED429INV-S4«Слишком много запросов кода»retry ≥ 60 сек
IDENTITY_SMS_DISPATCH_FAILED400Отказ verifier«Не удалось отправить код»retry

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

  • Новые optional поля — additive.
  • Logs: маскированный номер, результат; metrics: identity_sign_ups_total{result}, identity_duplicate_phone_total; traces — OTel.

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

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

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