Concepts
Operations, statuses, amounts, and currencies.
Operations
Everything in the API revolves around a single concept: an operation.
- A payment is an operation where money flows toward the merchant.
- A payout is an operation where money flows from the merchant to a client.
Each operation is identified in the public API by a server-issued
operation_id (UUID). It is deterministic — derived from your
(shop, merchant_operation_id) pair, not randomly generated — so
retrying the same create request always resolves to the same
operation_id (idempotency by construction). Your merchant_operation_id
is an idempotency key for creates, but it is not echoed in responses or
webhooks.
Status lifecycle
An operation moves through one of these statuses:
| Status | Meaning |
|---|---|
pending | The operation was accepted and is waiting on the provider. |
awaiting_confirmation | (Payments only) The route requires a receipt — the payment is waiting for the merchant to upload a receipt via POST /v1/confirm_payment. Non-terminal; a webhook fires on entry so you can prompt the user immediately. |
completed | Funds settled successfully. Terminal. |
failed | The provider was reached but rejected the operation. Terminal. |
rejected | No route accepted the operation, or it was rejected before being routed. Terminal. |
cancelled | The operation was explicitly cancelled through an active cancellation path. Terminal. |
A terminal webhook is delivered when the status passes the shop's callback
status filter; an empty filter means all terminal statuses. A one-shot,
best-effort callback is also attempted when the operation enters
awaiting_confirmation (see
Webhooks).
Amounts
Amounts are encoded as decimal strings, not numbers, to avoid
floating-point loss across systems. The fixed scale is 2 (two decimal
places):
"amount": "1500.00"That is 1500.00 RUB, i.e. 150 000 minor units (kopecks). The same
encoding is used for expected_amount and actual_amount in
responses.
When confirming a payment via multipart, the actual_amount form field
follows the same decimal-string format.
Settlement and commission
Operation responses and webhooks also carry what actually moved on your balance after the nspay commission:
commission_amount— the nspay commission for the operation.settlement_amount— the net balance movement. For a payment it is the amount credited to you (gross − commission_amount); for a payout it is the total charged to you (gross + commission_amount).settlement_currency— the currency both figures are expressed in (the ledger currency; equal tocurrencywhen the route did not convert, or torecorded_currencywhen it did).
Before an operation is terminal these reflect the expected figures for the
routed amount; on a partial capture they settle to the captured amount. They
always match what nspay books internally, so you can reconcile your balance
directly against settlement_amount.
Currencies
The currency is sent as an ISO 4217 alphabetic code:
"currency": "RUB"Today the production routing supports RUB via the White Triangle
payment methods. Additional currencies and methods are added without
API changes — the field stays a free string and is validated on the
server.
Payment methods
The method field on payment/payout requests names a logical method,
not a specific provider. Routing to a concrete provider is configured
per merchant. The currently supported values are:
WT_RUB_C2C— RUB via the card rail. Destination is keyed bynumber(card number).WT_RUB_PHONE— RUB via the consumer-to-consumer rail. Destination is keyed bynumber(phone number) plusbankname.
Sending an unknown method results in a 400 Bad Request.
Destinations
Payments and payouts share a unified destination shape with three fields:
| Field | WT_RUB_C2C | WT_RUB_PHONE |
|---|---|---|
number | card number | phone number |
name | cardholder name | recipient name |
bankname | ignored | required (see bank codes below) |
For payouts, the merchant fills the destination — that's the recipient the funds go to.
For payments, the destination is the inbound account the end user should transfer funds to. It is allocated by nspay once the operation is routed and surfaced back on the response and webhook so the merchant can display it to the user. Until allocation completes, the destination fields are absent from the response.
Bank codes
On the WT_RUB_PHONE rail the bank is required and must be one of the
following codes (case-sensitive). Sending any other value results in a
400 Bad Request.
| Code | Bank |
|---|---|
sber | Сбербанк |
tcs | Т-Банк (Тинькофф) |
alfa | Альфа-Банк |
vtb | ВТБ |
ozon | Озон Банк |
raif | Райффайзенбанк |
gazprom | Газпромбанк |
otkritie | Банк Открытие |
sovcom | Совкомбанк |
psb | Промсвязьбанк |
mts | МТС Банк |
yandex | Яндекс Банк |
rshb | Россельхозбанк |
pochta | Почта Банк |
rosbank | Росбанк |
uralsib | Уралсиб |
akbars | Ак Барс |
homecredit | Хоум Кредит |
rnkb | РНКБ |
yoomoney | ЮMoney |
wildberries | Вайлдберриз Банк (WB) |
zenit | Банк Зенит |
vbrr | Банк ВБРР |
Confirmation requests
Some payment methods require an out-of-band confirmation step. When the provider needs one, the operation response contains:
"external_confirmation": {
"required": true,
"methods": ["receipt"],
"reason": "manual receipt verification"
}When required is true, call POST /v1/confirm_payment with
confirmation_type=receipt to advance the operation. Public confirmation always
requires a receipt: a multipart receipt upload with a required file of up to
10 MB. Clean receipt validation can complete the payment automatically, while
failed, wrong-amount, or unavailable validation leaves the operation pending
for an operator, with the final status delivered by webhook. Confirming without
a receipt is not supported on the public API.