Skip to Content
One-time paymentsCards, SBP, SberPay

Bank card payments

The card payment flow does not require creating a contract first. Create an invoice and redirect the user to the returned payment URL.

5.1 Create invoice (card)

POST /api/v3/projects/{projectId}/invoices

Request body
{ "client_ip": "10.20.30.40", "customer": { "type": "undefined_bank_card", "source": "my_app", "country": "RU", "merchant_customer_id": "client_12345", "email": "test@test.com" }, "order": { "merchant_order_id": "order_123", "price": { "amount": "20.00", "currency": "RUB", "vat_included": true } } }
FieldTypeRequiredDescription
customer.typestringYes"undefined_bank_card" — the payer enters the details on the payment page
customer.sourcestring (max 64)YesUnique identifier of the merchant resource / brand. Guarantees global uniqueness of the source + merchant_customer_id pair
customer.countrystring (ISO 3166)YesClient country
customer.merchant_customer_idstring (max 128)YesUnique client ID in the merchant system
customer.emailstringNoClient email

undefined_bank_card and bank_card are different scenarios. undefined_bank_card means “the payer enters the card on the payment page” and covers everything described in §5.1 and §5.2; contracts are not created with this type — a contract block sent alongside it is rejected with 400 BAD_REQUEST. Per the specification bank_card means accepting the card on the merchant’s own form (the PCI DSS contour, §5.3). For new payment-page integrations use undefined_bank_card: bank_card stays a deprecated alias and will be removed in 3.0.0.

Response — 201 Created
{ "success": true, "data": { "invoice_id": "1aa2b436-0031-0028-1a54-8abd8eb88c40", "status": "Created", "payment_status": "NOT_PAID", "authorization": { "type": "AUTH_PAGE", "is_authorized": false, "url": "https://pay.callisto.pay/checkout/1aa2b436003100281a548abd8eb88c40" }, "price": { "amount": "20.00", "currency": "RUB", "vat_included": true }, "merchant_order_id": "order_123", "created_at": "2025-10-04T08:46:45Z", "expires_at": "2025-10-04T10:46:45Z", "billing_channel": "BankRtp", "customer": { "type": "undefined_bank_card", "source": "my_app", "country": "RU", "merchant_customer_id": "client_12345", "email": "test@test.com" } } }

After receiving the response, redirect the client to the address from the response: authorization.url in JSON, <AuthorizationUrl> in XML.

The 3-D Secure challenge is not a constant. Whether a confirmation page follows the card form depends on the acquirer, the integration type and fraud rules, so the integration has to work in both modes. The presence of the challenge does not affect the response payload of this flow: the redirect address always arrives. Both modes can be exercised in the sandbox, where 3-D Secure is a project setting — see Flow: 3-D Secure.

Returning the customer to your site

Once the payment finishes — successfully or not — the customer is sent back to the return address configured for your project. The platform appends the result parameters to it:

ParameterValue
invoiceIdInvoice identifier in the platform
merchantOrderIdOrder identifier in your system
successtrue — the payment went through; false — an error or a cancellation
Return address example
https://your-site.com/payment/result?invoiceId=1aa2b436-0031-0028-1a54-8abd8eb88c40&merchantOrderId=order_123&success=true

success is good enough for a result screen, but not for releasing goods. The parameters come from the customer’s browser and carry no signature, and the customer may never reach your site at all — a closed tab, a lost connection. The source of payment state is the webhook and GET /invoices/{invoice_id}.

POST /invoices — card, with redirect

RESPONSE=$(curl -sS -X POST "$BASE/api/v3/projects/$PROJECT_ID/invoices" \ -H "Content-Type: application/json" \ -H "X-Login: $LOGIN" \ -H "X-Timestamp: $TS" \ -H "X-Signature-Version: 3" \ -H "X-Signature: $SIG" \ -d '{ "client_ip": "10.20.30.40", "customer": { "type": "undefined_bank_card", "source": "my_app", "country": "RU", "merchant_customer_id": "client_12345" }, "order": { "merchant_order_id": "order_123", "price": { "amount": "20.00", "currency": "RUB", "vat_included": true } } }') echo "$RESPONSE" | jq -r '.data.authorization.url'

5.2 SBP and SberPay

SBP and SberPay have no request of their own. The invoice is created exactly as in §5.1, and the payment method is chosen with query parameters on the address from the response (authorization.url in JSON, <AuthorizationUrl> in XML) — the platform moved to this scheme in v2.78.0.

Parameters on the payment addressWhat opens
(no parameters)The bank card form
?useSbp=trueSBP — QR code
?payType=Sber&deviceType=DesktopSberPay for desktop
?payType=Sber&deviceType=MobileSberPay for mobile devices
Payment address with the method selected
https://pay.callisto.pay/checkout/1aa2b436003100281a548abd8eb88c40?useSbp=true

The payment_method field in the request body is deprecated. The payment-method selector came from the May 2026 revision of the specification and has been dropped by the platform. The field is accepted for one more minor version and goes away in 3.0.0; it does not change the response — you get the same authorization.url, only with the parameters from the table above already appended.

One invoice can be opened any way until it is paid. The payer can come back to the payment address and pick a different method — there is no need to create a separate invoice per method.

SBP bindings are not available yet. Repeat charges over SBP through a contract are on the roadmap; the protocol has no such flow today. Two paths are available for charges without re-confirmation: a contract on the phone number confirmed with a PIN code (DCB) and card binding (§5.3).

5.3 Card binding (card-on-file)

PCI DSS certification is required. In this scenario card data passes through the merchant, and the spec makes that a hard condition.

Without certification, use the “Payment via Bank Payment Form” scenario — §5.1 above: the invoice is created without card data, and the customer enters it on the platform side after being redirected to the payment URL from the response.

Card data is entered on the merchant side and passed as a <BankCard> block inside <BankCardCustomer> (XML) or as the customer.bank_card object with customer.type = "bank_card" (JSON). The card is bound via a CreateCustomerContract / POST /contracts contract (the response returns AuthorizationType: SKIP, no PIN confirmation), and subsequent charges go through the regular invoice creation referencing the contract, without re-entering the card details. The presence of the card-data block in requests is governed by the integration type and the individual requirements of the acquirer — it is enabled per partner.

customer.bank_card fieldRequiredDescription
panYesCard number, 16 digits
expiration_monthYesExpiry month, 1–12
expiration_yearYesExpiry year, 2 or 4 digits
security_codeYesCVV/CVC, 3–4 digits. Never stored, in any form
cardholderNoCardholder name, up to 128 characters

The block is only allowed together with contract.merchant_contract_id — the contour always works through contracts. The card is returned in no response and is not stored: only the last 4 digits of the number remain, and the request log receives the body already masked.

Card data goes to a dedicated host. The specification reserves a separate address for the PCI DSS contour: only invoice creation and contract creation there accept customer.bank_card, while everything else — authorization, cancellation, refunds, invoice lookups — is served by the main host, which rejects a card sent to it. Ask the platform team for the contour address.

A step-by-step walkthrough with sandbox emulation rules — Flow: card binding; the method format — Create contract.