CasherMatch

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-23456789abcd

The 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

FieldTypeRequiredNotes
merchant_operation_idstringyesIdempotency 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_idstringyesYour end-user identifier. Any non-empty string.
client_ipstringnoClient IP address, used for antifraud.
methodstringyesLogical payment method, e.g. WT_RUB_C2C. See Payment methods.
amountstringyesDecimal string with scale 2, e.g. "1500.00".
currencystringyesISO 4217 code, e.g. "RUB".
numberstringnoDestination identifier — only consulted for payouts and ignored on payments. Card number for the C2C methods; phone number for the phone methods.
namestringnoHolder/recipient name on the destination. For payouts, omitted or empty values default to аноним.
banknamestringnoDestination 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.
  • 401X-API-Key missing or invalid.
  • 412merchant_operation_id collision with a different body.

On this page