Перейти к содержимому
API-документация

Деньги: баланс, котировки, резервы

Как устроен баланс, как считается цена операции и когда деньги списываются.

Формат сумм

Все суммы — десятичные строки в USD: "50", "57.0464", "0.2". Хвостовые нули не печатаются. Точность — до 4 знаков после точки.

Не превращайте суммы во float

0.1 + 0.2 во float — это 0.30000000000000004. Храните и считайте суммы десятичным типом: decimal.Decimal в Python, BigDecimal в Java, numeric в Postgres, bcmath в PHP; в JavaScript — библиотека вроде decimal.js или целые десятитысячные доли.

Баланс

У аккаунта один предоплаченный счёт в USD. Пополняется он по договорённости с менеджером, через API — нет.

{ "currency": "USD", "balance": "1250.5", "hold": "57.0464", "available": "1193.4536" }
ПолеСмысл
balanceУчтённые деньги
holdРезерв под операции в процессе
availablebalance − hold. Новая операция принимается, только если available её покрывает

balance может стать отрицательным: комиссия за транзакцию по карте (tx_fee_ok, tx_fee_declined) списывается после того, как карта уже заплатила мерчанту, и отказать в ней нельзя. Выпущенные карты при этом работают, новые операции — нет, пока вы не пополните счёт.

Жизнь резерва

  1. POST /cards или POST /cards/{id}/topup → сумма операции (hold_amount) уходит в hold.
  2. Операция succeeded → резерв списывается с balance.
  3. Операция failed → резерв снимается целиком, balance не меняется.
  4. Операция needs_review → резерв остаётся, пока мы не разберёмся.

Закрытие карты синхронное: комиссия закрытия списывается в том же вызове, только если карта действительно закрылась.

Тариф

{
  "id": "3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34",
  "issue_fee": "5",
  "topup_fee_pct": "3",
  "topup_fee_const": "0.5",
  "close_fee": "1",
  "close_refund_fee_pct": "0",
  "tx_fee_ok": "0.2",
  "tx_fee_declined": "0.5",
  "min_topup": "10",
  "max_topup": "5000"
}
ПолеКогда берётся
issue_feeОдин раз при выпуске
topup_fee_pct, topup_fee_constС каждого зачисления на карту, включая начальное при выпуске
close_feeПри закрытии карты
close_refund_fee_pctПроцент, удерживаемый с остатка закрытой карты. 0 — остаток возвращается целиком
tx_fee_ok, tx_fee_declinedЗа каждую одобренную / отклонённую транзакцию по карте
min_topup, max_topupГраницы суммы зачисления на карту. Нет поля — нет границы

Цена фиксируется тарифом, на котором карта выпущена: пополнение всегда считается по нему.

Как считается цена

Пусть p = topup_fee_pct / 100, c = topup_fee_const.

amount_type: credit (по умолчанию) — вы задаёте, сколько придёт на карту:

debit = credit / (1 − p) + c          округление вверх до 0.0001

amount_type: debit — вы задаёте бюджет, мы считаем, сколько придёт на карту:

credit = (debit − c) × (1 − p)        округление вниз до 0.0001

При выпуске к debit добавляется issue_fee. Округление всегда в нашу пользу и не больше чем на 0.0001 USD — котировка никогда не меньше реального списания.

Пример

Тариф выше, выпуск с 50 USD на карте:

50 / 0.97      = 51.54639175…
+ 0.5          = 52.04639175… → 52.0464   (пополнение)
+ 5            = 57.0464                  (выпуск)
topup_fee      = 52.0464 − 50 = 2.0464

Проверить расчёт до операции можно через POST /quote — побочных эффектов нет:

BODY='{"kind":"issue","tariff_id":"3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34","amount":"50"}'
TS=$(date +%s)
SIG=$(printf '%s\n%s\n%s\n%s' "$TS" "POST" "/api/partner/v1/quote" "$BODY" \
  | openssl dgst -sha256 -hmac "$PP_SECRET" -hex | awk '{print $NF}')

curl -sS -X POST "https://api.plativputi.com/api/partner/v1/quote" \
  -H "X-Key-Id: $PP_KEY_ID" -H "X-Timestamp: $TS" -H "X-Signature: $SIG" \
  -H "Content-Type: application/json" -d "$BODY"
const quote = await pp("POST", "/quote", {
  kind: "issue",
  tariff_id: "3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34",
  amount: "50",
});
quote = pp("POST", "/quote", body={
    "kind": "issue",
    "tariff_id": "3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34",
    "amount": "50",
})
$quote = pp('POST', '/quote', [
    'kind' => 'issue',
    'tariff_id' => '3f6c2a1e-8b4d-4e2a-9c71-5d0e8f1a2b34',
    'amount' => '50',
]);
{ "debit": "57.0464", "credit": "50", "issue_fee": "5", "close_fee": "0", "topup_fee": "2.0464" }

Пополнение на 100 USD с amount_type: debit: (100 − 0.5) × 0.97 = 96.515 придёт на карту.

Возврат остатка при закрытии

При закрытии остаток карты в том же вызове возвращается на ваш баланс за вычетом close_refund_fee_pct:

"refund": { "status": "refunded", "residual": "37.4", "fee": "0", "amount": "37.4" }
refund.statusЗначит
refundedamount зачислен на баланс
zeroКарта была пустой
unknownБаланс карты прочитать не удалось. Ничего пока не зачислено, остаток урегулируем вручную

Лимиты аккаунта

Кроме границ тарифа у аккаунта могут быть свои лимиты: максимум на одну операцию (422 amount_above_limit) и число выпусков в сутки (429 daily_issue_limit). Их задаёт менеджер.