Перейти к содержанию

01 payment

PaymentEndpoint — платежи, история, preview, resolve, step-up.

Тонкий фасад над Payment Manager (PM). Не содержит платёжной бизнес-логики — аутентифицирует запрос, обогащает PaymentMetadata данными профилей и проксирует HMAC-запрос в PM.

Аутентификация: все методы требуют session.authenticated (JWT Serverpod). _requireUserId() двухшаговый: UUID из сессии → users.authUserId → числовой users.id.

Методы

Метод accountType PM вызов Возвращает
getMyTbAccountId(currency) любой авторизованный нет (детерминировано локально) String — TB UUID кошелька USER_WALLET
resolveRecipientByPhone(phone) consumer, wallet, agent нет (прямой SELECT) PhoneResolveResult? — null если не найден, неактивен или самоперевод
getTransactionHistory({page, limit}) любой авторизованный нет (SELECT из v_tx_history) List<VTxHistory> — сортировка DESC по createdAt
previewIntent(operationType, amount, currency, {recipientPhone?, recipientTbAccountId?}) любой авторизованный PspHmacClient.previewIntent IntentQuoteResponse — fee breakdown без создания интента в БД
createIntent(idempotencyKey, operationType, amount, currency, {recipientUserId?, toTbAccountId?, externalRef?, comment?, flow?}) любой авторизованный PspHmacClient.createIntent IntentDto — PM гарантирует idempotency по idempotencyKey
getIntent(intentId) любой авторизованный PspHmacClient.getIntent IntentDto — текущий статус интента
previewInvoice(qrRawPayload, appId) consumer, wallet PspHmacClient.getIntent + previewIntent + evaluatePolicy InvoicePreviewResponse — fee + stepUpRequired + статус
confirmInvoice(req) consumer, wallet PspHmacClient.evaluatePolicy + confirmIntent IntentDto — после валидации step-up proof

Детали методов

previewInvoice выполняет три последовательных PM-вызова: декодирует QR через InvoiceQrDecoder, верифицирует подпись через InvoiceQrSigner, проверяет срок действия и appScope (w=wallet, c=closeloop, a=all).

confirmInvoice — если stepUp.level != NONE, валидирует StepUpProof: тип (PIN/BIOMETRIC→PIN), freshness ≤ 5 мин, наличие pinHash.

_buildMetadata — строит PaymentMetadata в два параллельных раунда Future.wait. Поля: fromUserId/toUserId, accountType, accountTier, isVatPayer, tags, taxId, businessCategoryCode, referralAgentId/UserId, source='mobile_app', flow.

Error codes

PM HTTP PaymentErrorCode Клиент получает
401/403 AUTH_FAILED PaymentException
422 VALIDATION_ERROR PaymentException
403 FORBIDDEN PaymentException
404 NOT_FOUND PaymentException
5xx PSP_UNAVAILABLE PaymentException
timeout PSP_TIMEOUT PaymentException
нет строки в v_user_tb_accounts WALLET_MISSING PaymentException
session.authenticated == null UNAUTHENTICATED PaymentException
невалидный QR CloseloopException(code: 'INVALID_QR')
подпись QR не совпадает CloseloopException(code: 'INVALID_SIGNATURE')
QR истёк CloseloopException(code: 'INVOICE_EXPIRED')
не тот app scope CloseloopException(code: 'WRONG_APP_SCOPE')
инвойс не в статусе CREATED CloseloopException(code: 'INVOICE_NOT_PAYABLE')

Важно

  • Все суммы в сатангах (1 THB = 100 сатангов). PM принимает и возвращает только целые числа.
  • getMyTbAccountId возвращает UUID только для USER_WALLET — не использовать для MERCHANT/AGENT аккаунтов.
  • История транзакций читается из public.v_tx_history (view над pm.tx_history) напрямую без HMAC — это допустимо, т.к. история read-only.
  • flow в createIntent: 'phone' (через resolveRecipientByPhone) или 'qr' (через QR-сканирование). Используется PM для аналитики.