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 |