Платёжные сценарии¶
Простыми словами: что происходит, когда пользователь переводит деньги, пополняет кошелёк, выводит средства, платит мерчанту или мини-аппу. Все деньги — в THB (в системе хранятся в satang: 1 THB = 100 satang).
Все платежи проходят через единый endpoint Payment Manager: POST /intents
(подписывается HMAC). У каждого «намерения» (intent) есть статус-машина из 9
состояний: CREATED → VALIDATED → AUTHORIZED → SETTLING → SETTLED, плюс
ответвления FAILED / MANUAL_REVIEW / CANCELED / EXPIRED.
На какие вопросы отвечает¶
- Что значит «перевод прошёл мгновенно», а что — «ожидает подтверждения»?
- Чем top-up отличается от вывода средств, и почему вывод может занять время?
- Как устроена оплата по счёту мерчанта (invoice) и оплата на кассе (POS)?
- Что такое charge/credit для мини-аппа и оплата NFC-меткой?
- Что из этого уже работает, а что запланировано на Phase 2B?
Сценарии и статусы¶
| Сценарий | operationType | Что происходит | Статус |
|---|---|---|---|
| P2P-перевод между пользователями | P2P_TRANSFER |
Канал INTERNAL_P2P — деньги списываются и зачисляются синхронно в одном запросе. |
Готово |
| Пополнение кошелька | QP_TOPUP |
Внешний PSP-канал зачисляет средства; финал приходит асинхронно. | Готово (PSP Phase 1 in-process) |
| Вывод на банк/счёт (IPPS) | IPPS_WITHDRAWAL |
Списание в кошельке, перевод через IPPS; финал асинхронно. | Готово (PSP Phase 1 in-process) |
| Вывод по Thai QR | THAI_QR_PAY |
Оплата по QR во внешнюю сеть; финал асинхронно. | Готово (PSP Phase 1 in-process) |
| Прочий вывод | WITHDRAWAL |
Обобщённый вывод, требует externalRef. |
Готово |
| Счёт мерчанта (invoice) | INVOICE_PAYMENT |
Двухфазно: резерв счёта → подтверждение/отмена (см. ниже). | Готово |
| Оплата на кассе (POS) | INVOICE_PAYMENT / NFC_CHARGE |
Мерчант формирует QR-инвойс или принимает NFC; QR-инвойс = тот же двухфазный invoice. | Готово |
| Списание у мини-аппа | MINIAPP_CHARGE |
Мини-апп списывает с кошелька покупателя в пользу мерчанта. | Готово |
| Зачисление от мини-аппа | MINIAPP_CREDIT |
Мини-апп зачисляет средства пользователю (возврат/бонус). | Готово |
| Оплата NFC-меткой | NFC_CHARGE |
Pull-charge: списание с кошелька клиента, зачисление мерчанту (metadata.nfcTagId). |
Готово |
| Сервисное зачисление | SERVICE_DEPOSIT |
Зачисление пользователю от сервиса (metadata.recipientUserId). |
Готово |
| Перевод оператором | ADMIN_TRANSFER |
Ручная операция из admin-panel. | Готово |
Внешние Redis-Streams PSP-адаптеры (
stream.ipps.*,stream.qp.*, webhook gateway) — Phase 2B, не реализовано. Сейчас PSP-логика IPPS работает in-process внутри PM (psp-worker, очередь в таблицеpsp_tx_map).
Синхронный vs асинхронный финал¶
- INTERNAL P2P (
channel=INTERNAL_P2P) проходитCREATED → SETTLEDсинхронно в одном HTTP-запросе. Ответ сразу содержитstatus: SETTLED,requiresMonitoring: false. - Внешние PSP-каналы (top-up, вывод) возвращаются на
AUTHORIZEDсrequiresMonitoring: true; клиент подписывается на Redis-каналintent.{id}и получает финальный статус позже (PM → Auth Center → Flutter).
Пример P2P-запроса (значения иллюстративные):
POST /intents
{ "idempotencyKey": "…uuid…", "operationType": "P2P_TRANSFER",
"amount": 15000, "currency": "THB", "recipientUserId": 4821,
"comment": "обед" }
→ 200 { "status": "SETTLED", "requiresMonitoring": false }
amount: 15000 satang = 150.00 THB.)
P2P-перевод (синхронно)¶
sequenceDiagram
participant App as Flutter (one_loop_app)
participant AC as Auth Center
participant PM as Payment Manager
participant TB as TigerBeetle
App->>AC: перевод 150 THB пользователю
AC->>PM: POST /intents (P2P_TRANSFER, HMAC)
PM->>TB: списание + зачисление (синхронно)
TB-->>PM: ok
PM-->>AC: 200 status=SETTLED
AC-->>App: перевод выполнен
Merchant invoice — двухфазный поток¶
Счёт мерчанта оплачивается в две фазы:
- reserve —
POST /intentsсINVOICE_PAYMENTсоздаёт счёт без обращения к TigerBeetle, сохраняетexpiresAtи подписьqrSignature(для QR на кассе). Статус инвойса —CREATED. - confirm/cancel — плательщик подтверждает (
/intents/:id/confirm) — деньги реально движутся в TigerBeetle, статус →SETTLED; либо отмена (/intents/:id/cancel, только изCREATED). - expire — фоновая задача
invoice-expiryпереводит просроченные счёта вEXPIRED(атомарно, только изCREATED).
sequenceDiagram
participant M as Мерчант (POS / merchant_app)
participant PM as Payment Manager
participant P as Плательщик (Flutter)
participant TB as TigerBeetle
M->>PM: POST /intents INVOICE_PAYMENT (reserve)
PM-->>M: счёт CREATED + qrSignature + expiresAt
M-->>P: показывает QR / NFC
P->>PM: POST /intents/:id/confirm
PM->>TB: списание + зачисление мерчанту
TB-->>PM: ok
PM-->>P: SETTLED
Note over PM: если не подтверждён до expiresAt —<br/>invoice-expiry → EXPIRED
NFC-метки и POS¶
- POS-инвойс (QR) — мерчант (one_merchant_app) формирует QR-инвойс, это тот
же двухфазный
INVOICE_PAYMENTс подписьюqrSignature. - NFC на кассе / NFC-метка —
NFC_CHARGE(pull-charge): списание с кошелька клиента и зачисление мерчанту;metadata.nfcTagId(целое) идентифицирует метку.recipientUserId,toTbAccountId,externalRefдля NFC запрещены.
См. также¶
- dev/04-payments-and-ledger.md — техника intent saga, two-phase TigerBeetle, каналы и роутинг.
- 05-limits-and-fees.md — лимиты и комиссии по сценариям.
- 02-actors-and-accounts.md — кто участвует и какие счёта затрагиваются.
- Payment Manager · docs/business — подробные сценарии (03-payment-scenarios.md), типы операций (04-operation-types.md), merchant invoice (07-merchant-invoice-flow.md).