04 user journeys
Пользовательские сценарии в OneWallet — от регистрации до платежа.
Регистрация нового пользователя¶
Самостоятельная регистрация через email OTP. После успешного подтверждения пользователь переходит к KYC.
sequenceDiagram
participant U as Пользователь
participant App as onewallet_app
participant AC as Auth Center
participant Redis as Redis (OTP)
participant Email as SMTP
U->>App: ввести email
App->>AC: startRegistration(email)
AC->>Redis: createOrFindPendingUser → status=registration_in_progress
AC->>Email: OTP (6 цифр, TTL 10 мин)
U->>App: ввести OTP
App->>AC: verifyOtp(email, code)
AC->>Redis: validateOTP → del otp:email
U->>App: задать пароль + PIN (6 цифр)
App->>AC: finishRegistration(password, pin)
AC->>AC: linkAuthUser → status=kyc_pending
App->>App: перейти на KYC-онбординг
Шаги:
1. Приложение отправляет email в startRegistration — создаётся запись User со статусом registration_in_progress.
2. Auth Center генерирует 6-значный OTP, сохраняет в Redis (TTL — env REGISTRATION_OTP_TTL_MINUTES, по умолчанию 10 мин), отправляет SMTP-письмо.
3. Пользователь вводит OTP в приложении; попытки ограничены (REGISTRATION_OTP_MAX_ATTEMPTS).
4. После подтверждения OTP пользователь задаёт пароль и 6-значный PIN (требование NatBank).
5. linkAuthUser связывает Serverpod-запись с authUserId и переводит статус в kyc_pending.
6. Приложение открывает экран KYC-онбординга.
| Поле | Значение после регистрации |
|---|---|
user.status |
kyc_pending |
user.accountType |
consumer (по умолчанию) |
user.authUserId |
заполнен (UUID Serverpod Auth) |
P2P-перевод (phone flow)¶
Перевод с кошелька на кошелёк по номеру телефона. Перевод выполняется синхронно через PM (channel=INTERNAL).
sequenceDiagram
participant U as Отправитель
participant App as onewallet_app
participant AC as Auth Center
participant PM as Payment Manager
U->>App: ввести номер телефона получателя
App->>AC: resolveRecipientByPhone(phone)
AC-->>App: PhoneResolveResult (имя, tbAccountId)
U->>App: ввести сумму
App->>AC: previewIntent(amount, currency, recipientUserId)
AC->>PM: POST /intents/preview (HMAC)
PM-->>AC: IntentQuoteResponse (fee, итого)
AC-->>App: комиссия и итоговая сумма
U->>App: подтвердить PIN / биометрию
App->>AC: createIntent(idempotencyKey, P2P_TRANSFER, amount, ...)
AC->>PM: POST /intents (HMAC, channel=INTERNAL)
PM->>PM: settleSync() → TigerBeetle
PM-->>AC: IntentDto (status=COMPLETED)
AC-->>App: IntentDto
App->>AC: getIntent(intentId) — поллинг при необходимости
App-->>U: экран успеха
Шаги:
1. resolveRecipientByPhone — ищет активного пользователя по телефону, возвращает имя и tbAccountId для отображения.
2. previewIntent — запрашивает у PM расчёт комиссии без создания интента.
3. Пользователь подтверждает через PIN (6 цифр) или биометрию.
4. createIntent с flow='phone' и recipientUserId — PM выполняет синхронный расчёт (INTERNAL channel, без PSP-адаптера).
5. Поллинг через getIntent нужен только при возвращении статуса PENDING; в норме COMPLETED приходит сразу.
Пополнение через агента (TOPUP)¶
Агент (кассир) создаёт пополнение для потребителя через merchant_app. Перевод из агентского кошелька в кошелёк потребителя.
sequenceDiagram
participant A as Агент
participant MApp as merchant_app
participant AC as Auth Center
participant PM as Payment Manager
participant C as Потребитель
A->>MApp: ввести телефон потребителя и сумму
MApp->>AC: createTopup(consumerPhone, amount, currency)
AC->>AC: requireAccountType → merchant | agent
AC->>PM: POST /intents (HMAC, operationType=TOPUP)
PM->>PM: settleSync() → TigerBeetle
PM-->>AC: IntentDto (COMPLETED)
AC-->>MApp: IntentDto
MApp-->>A: экран успеха
C-->>C: получает push-уведомление о зачислении
Шаги:
1. createTopup доступен только аккаунтам с accountType = merchant | agent.
2. Auth Center передаёт в PM operationType=TOPUP с HMAC-подписью (X-Service-Id, X-Timestamp, X-Signature).
3. PM выполняет списание с агентского кошелька и зачисление на кошелёк потребителя через TigerBeetle.
4. Потребитель получает push-уведомление через Notifications Service.
Оплата QR-инвойса¶
Потребитель сканирует QR мерчанта и оплачивает инвойс. Мерчант создаёт инвойс заранее через createInvoice.
sequenceDiagram
participant U as Потребитель
participant App as onewallet_app
participant AC as Auth Center
participant PM as Payment Manager
participant M as Мерчант
M->>AC: createInvoice(amount, currency, ref)
AC-->>M: InvoiceQrPayload (QR с tbAccountId + invoiceId)
U->>App: сканировать QR
App->>AC: previewInvoice(qrRawPayload, appId)
AC->>AC: requireAccountType → consumer | wallet
AC->>AC: InvoiceQrDecoder.decode(qrRawPayload)
AC->>PM: POST /intents/preview (HMAC)
PM-->>AC: fee breakdown
AC-->>App: InvoicePreviewResponse (сумма, комиссия, мерчант)
U->>App: подтвердить PIN / биометрию
App->>AC: confirmInvoice(invoiceId, appId, idempotencyKey)
AC->>PM: POST /intents (HMAC, operationType=INVOICE_PAYMENT)
PM->>PM: settleSync() → TigerBeetle
PM-->>AC: IntentDto (COMPLETED)
AC-->>App: IntentDto
App-->>U: экран успеха
M-->>M: получает push о поступлении оплаты
Шаги:
1. Мерчант заранее создаёт инвойс (createInvoice); результат — QR-payload с tbAccountId и invoiceId.
2. previewInvoice декодирует QR, проверяет срок действия инвойса и запрашивает комиссию у PM без создания интента.
3. Потребитель подтверждает через PIN или биометрию.
4. confirmInvoice создаёт и сразу исполняет интент; invoiceId связывает платёж с записью инвойса.
5. Оба участника получают push-уведомления через Notifications Service.