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

Payment manager

PspHmacClient — HMAC-подписанный HTTP к Payment Manager, два ключа.

Два HMAC ключа

serviceId Ключ в passwords.yaml Используется для
auth-center pmHmacSecret Все операции кроме NFC pull-charge
auth-center-merchant pmMerchantHmacSecret chargeByNfc (MerchantEndpoint) — pull-charge с маской user/agent → merchant

Оба ключа имеют общий pmBaseUrl. Инициализируются в server.dart через PspHmacClient.initialize() и PspHmacClient.initializeMerchant().

HMAC Signing

sequenceDiagram
    PspHmacClient->>PspHmacClient: canonical = ts+"\n"+METHOD+"\n"+path+"\n"+sha256(body)
    PspHmacClient->>PspHmacClient: sig = HMAC-SHA256(secret, canonical)
    PspHmacClient->>PM: запрос + X-Service-Id + X-Timestamp + X-Signature + X-User-Id
    PM->>PspHmacClient: ответ

Формат canonical-строки (байт-идентичен payment-manager/src/auth/hmac.ts):

${timestampSeconds}\n${METHOD}\n${path}\n${sha256hex(body)}
  • timestampSeconds — Unix timestamp в секундах
  • METHOD — uppercase (GET, POST)
  • body — пустая строка "" для GET-запросов
  • Тело для подписи и отправки идентично

Методы

Метод HTTP Path Используется в
createAccount POST /accounts AccountService — идемпотентный провизион TigerBeetle счёта
createIntent POST /intents PaymentEndpoint, MerchantEndpoint (PmKey.merchant для NFC)
getIntent GET /intents/:id PaymentEndpoint.getIntent, streamStatus()
getBalance GET /accounts/:name/balance WalletEndpoint.getBalance
previewIntent POST /intents/quote PaymentEndpoint.previewPayment — только расчёт комиссии, без DB write
confirmIntent POST /intents/:id/confirm PaymentEndpoint.confirmPayment
cancelIntent POST /intents/:id/cancel PaymentEndpoint.cancelPayment
evaluatePolicy POST /policies/evaluate PaymentEndpoint.evaluatePolicy — step-up требование
listInvoices GET /intents?issued_by_user_id=X MerchantEndpoint.listInvoices (DEFERRED — PM endpoint pending)

Error mapping

HTTP статус PM PaymentErrorCode Условие
401, 403 authFailed HMAC неверен или отозван
404 notFound intent или account не найден
422 PM error code as-is INSUFFICIENT_FUNDS, LIMIT_EXCEEDED и др. — code сохраняется
5xx pspUnavailable PM вернул server error
timeout (Dio) pspTimeout connectTimeout / receiveTimeout / sendTimeout
network error pspUnavailable соединение не установлено

callPm() — конвертация исключений

PaymentException — внутренний тип сервера — конвертируется в CloseloopException перед передачей Serverpod-клиенту. Маппинг в payment_endpoint.dart:

PaymentErrorCode CloseloopException code
authFailed payment_auth_failed
notFound payment_not_found
pspTimeout payment_timeout
pspUnavailable payment_unavailable
INSUFFICIENT_FUNDS insufficient_funds
LIMIT_EXCEEDED limit_exceeded
остальные передаётся код PM без изменений

Конфиг из passwords.yaml

Ключ Dev default Назначение
pmBaseUrl http://localhost:3000 Base URL Payment Manager
pmServiceId auth-center serviceId для обычных операций
pmHmacSecret dev-secret-auth-center-change-in-prod HMAC secret для PmKey.auth
pmMerchantServiceId auth-center-merchant serviceId для NFC pull-charge
pmMerchantHmacSecret <согласовать с PM> HMAC secret для PmKey.merchant