Create payment
POST /v1/payment
Create an inbound payment. The merchant collects funds; CasherMatch routes the operation to a configured provider and returns the current state.
Endpoint
POST /v1/payment
Content-Type: application/json
X-API-Key: 01234567-89ab-4cde-8f01-23456789abcdThe X-API-Key header is required — see
Authentication. The key fixes
the merchant and shop scope; do not send merchant_id or shop_id in
the body.
Request
| Field | Type | Required | Notes |
|---|---|---|---|
merchant_operation_id | string | yes | Idempotency key. Any non-empty string (UUID, hex, integer-as-string, …). Re-sending the same key with the same body returns the original operation; with a different body it returns 412 Precondition Failed. |
client_id | string | yes | Your end-user identifier. Any non-empty string. |
client_ip | string | no | Client IP address, used for antifraud. |
method | string | yes | Logical payment method, e.g. WT_RUB_C2C. See Payment methods. |
amount | string | yes | Decimal string with scale 2, e.g. "1500.00". |
currency | string | yes | ISO 4217 code, e.g. "RUB". |
number | string | no | Destination identifier — only consulted for payouts and ignored on payments. Card number for the C2C methods; phone number for the phone methods. |
name | string | no | Holder/recipient name on the destination. For payouts, omitted or empty values default to аноним. |
bankname | string | no | Destination bank — required for WT_RUB_PHONE payouts and must belong to the method's currency (e.g. sber for RUB); ignored for the card rails. |
The destination fields (number, name, bankname) form a unified
shape shared with POST /v1/payout. For payments
they are accepted but unused on the request — the inbound destination
the end user must transfer to is allocated by CasherMatch and returned on
the response.
Example
curl -X POST https://api.casheradminmatch.com/v1/payment \
-H "Content-Type: application/json" \
-H "X-API-Key: 01234567-89ab-4cde-8f01-23456789abcd" \
-d '{
"merchant_operation_id": "8a26f5cf-1f56-4cda-8d8a-2a915b5e4b58",
"client_id": "57b8a4ca-3a4b-4f01-9a5e-1f29c1cefb9b",
"client_ip": "203.0.113.10",
"method": "WT_RUB_PHONE",
"amount": "1500.00",
"currency": "RUB"
}'Response
200 OK with the operation object. The gateway waits up to
15 seconds for a provider to accept the payment and produce usable requisites
or a confirmation requirement. It does not return a synthetic pending response
when no provider accepted the operation; that path rejects. An accepted payment
may remain non-terminal and its final state then arrives by
webhook.
When the chosen route requires a receipt confirmation, the create response
status is awaiting_confirmation (instead of pending). This means the
end user must upload a receipt via POST /v1/confirm_payment before the
payment can proceed. A webhook fires immediately on entry to
awaiting_confirmation so you can prompt the user; the terminal callback
fires once the receipt is reviewed and the payment reaches a terminal
status.
Operation object
{
"operation_id": "f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
"type": "payment",
"status": "pending",
"method": "WT_RUB_PHONE",
"expected_amount": "1500.00",
"actual_amount": "0.00",
"currency": "RUB",
"fx_source": "rapira",
"fx_rate": "0.013000",
"settlement_amount": "19.11",
"settlement_currency": "USDT",
"commission_amount": "0.39",
"external_confirmation": {
"required": true,
"methods": ["receipt"]
},
"number": "+79991234567",
"name": "Ivan Ivanov",
"bankname": "sber",
"bankname_details": "Сбербанк"
}expected_amount mirrors what you sent. actual_amount is the physical fact
known for the operation; do not infer it from status or replace zero with the
requested amount. It is normally non-zero after money is processed; internal
operator surfaces may additionally show attempt-level physical facts that are
not fields of this public payment response.
When a route uses auto-conversion, fx_source and fx_rate are present as
soon as the route is chosen — this is a quote (expected amount × rate),
available well before the operation resolves. recorded_currency and
recorded_amount are the ledger fact (actual amount × rate) and appear
only once actual_amount is non-zero, i.e. once the operation has actually
processed money (completed, or a self-matching partial). A pending
response therefore carries only the fx_rate/fx_source quote — never
recorded_amount/recorded_currency. Example of a completed response on the
same operation:
{
"operation_id": "f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
"type": "payment",
"status": "completed",
"method": "WT_RUB_PHONE",
"expected_amount": "1500.00",
"actual_amount": "1500.00",
"currency": "RUB",
"recorded_currency": "USDT",
"recorded_amount": "19.50",
"fx_source": "rapira",
"fx_rate": "0.013000",
"number": "+79991234567",
"name": "Ivan Ivanov",
"bankname": "sber",
"bankname_details": "Сбербанк"
}settlement_amount is the amount that moves on your balance net of the CasherMatch
commission, in settlement_currency (the ledger currency, equal to currency
without conversion): for a payment it is what is credited to you after
commission, for a payout the total charged to you including commission.
commission_amount is the CasherMatch commission. Before a terminal status these are
the expected figures for the routed amount and settle to the captured amount at
terminal.
The destination fields (number, name, bankname, bankname_details) are populated
once CasherMatch has allocated an inbound destination — until then they are
absent from the response. See
Destinations for which fields each
method uses.
expires_at (RFC3339, e.g. "2026-08-18T12:30:00Z") is present when the
routed provider/integration reports a real expiry for this operation's timing
window (e.g. how long generated requisites remain payable); it is omitted when
there is none.
Internal provider and routing identifiers are not exposed in public responses.
While status is manual_check, the operation also contains
manual_check_reason: potential_fake when the receipt may be forged or
otherwise suspicious, or not_terminal when the shown transfer is still in
progress — or when the uploaded file is not a PDF and could not be validated,
which is never treated as a fraud signal. It is omitted for every other status. The upstream reason is optional
internally for rolling compatibility; if it is missing, CasherMatch conservatively
returns potential_fake.
Hosted payment page — POST /v1/payment/link
Instead of rendering the requisites yourself, you can hand the payer a
ready-made hosted checkout page — where the hosted page is enabled for
your account. POST /v1/payment/link takes the exact same request as
POST /v1/payment and creates the inbound payment identically — the only
difference is the response, which adds a payment_url field. The endpoint
always works and always creates the operation; if the hosted page is not
enabled for your account, payment_url is null and you should render the
requisites yourself from the rest of the response, exactly like
POST /v1/payment. Do not assume payment_url is present —
check for it before redirecting the payer.
POST /v1/payment/link
Content-Type: application/json
X-API-Key: 01234567-89ab-4cde-8f01-23456789abcd{
"operation_id": "f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
"type": "payment",
"status": "awaiting_confirmation",
"method": "WT_RUB_PHONE",
"expected_amount": "1500.00",
"currency": "RUB",
"payment_url": "https://pay.casheradminmatch.com/f6c2e7d4-3e5b-4f1f-8a02-9d6c7e8a1b22",
"external_confirmation": { "required": true, "methods": ["receipt"] },
"number": "+79991234567",
"name": "Ivan Ivanov",
"bankname": "sber",
"bankname_details": "Сбербанк"
}When payment_url is present, redirect the payer to it. The hosted page shows the amount and
requisites, walks the payer through paying the exact amount, and — when
the route requires a receipt — accepts a PDF receipt upload and runs it
through the same checks as POST /v1/confirm_payment,
then reflects the outcome (paid / under review / declined / expired). You keep
receiving the authoritative result by webhook exactly as with
the server-to-server flow; the page is only an alternative front end for the
same operation.
payment_url is <host>/<operation_id>. Everything else in the response is
identical to POST /v1/payment. The plain /v1/payment response
never carries payment_url.
Errors
See Errors. The most common ones for this endpoint:
400— invalid JSON or a required field is missing/malformed.401—X-API-Keymissing or invalid.412—merchant_operation_idcollision with a different body.