REST API
REST поверх HTTPS, JSON в обе стороны. База — https://api.balancedpay.pro. Bearer-ключ из ЛК, опционально с HMAC-подписью; Idempotency-Key на create-операциях.
Платежи
Создание платежа
/api/v1/public/paymentsamountСумма в минорных единицах. Для RUB — копейки. Обязательно.currencyКод валюты ISO-4217. По умолчанию RUB.methodСпособ оплаты: sbp, cards, tpay, sberpay, recurrent или any. По умолчанию any — покупатель выбирает на нашей странице.order_idВаш идентификатор заказа. Если не передан, сгенерируется ord_<timestamp>.customer_idВаш идентификатор клиента. Сохраняется в metadata и используется для группировки операций.return_urlURL, на который вернётся покупатель после оплаты. Требуется схема https://.descriptionНазначение платежа. Отображается в чеке покупателя.webhook_urlАдрес webhook-получателя только для этого платежа. Переопределяет настройки магазина.metadataПроизвольные пары ключ-значение. До 20 пар, значения — строки до 500 символов.curl -X POST https://api.balancedpay.pro/api/v1/public/payments \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7f2a-pay-1042' \
-d '{
"amount": 150000,
"currency": "RUB",
"method": "sbp",
"order_id": "ORDER-1042",
"customer_id": "user-7821",
"return_url": "https://example.com/order/1042/done"
}'{
"payment": {
"id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"order_id": "ORDER-1042",
"amount": 150000,
"currency": "RUB",
"method": "sbp",
"status": "pending",
"expires_at": "2026-05-05T13:42:00Z",
"created_at": "2026-05-05T12:42:00Z"
},
"payment_url": "https://pay.balancedpay.pro/pay/5a331a39-…",
"mode": "live",
"bank": "Сбер", // имя банка-эквайера, если он ответил sync
"bank_ref": "262512345678" // ID операции в банке (только для h2h)
}Поля idempotent: true, failure_code и failure_message возвращаются при повторе запроса по Idempotency-Key или при синхронном отказе банка. В тестовом режиме добавляются служебные поля симулятора — описаны на странице Тестовая среда.
Проверка статуса платежа
/api/v1/public/payments/{id}curl -X GET https://api.balancedpay.pro/api/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"shop_name": "Web checkout",
"order_id": "ORDER-1042",
"status": "succeeded",
"amount": 150000,
"amount_refunded": 0,
"currency": "RUB",
"method": "sbp",
"rrn": "262512345678",
"bank_name": "Сбер",
"return_url": "https://example.com/order/1042/done",
"created_at": "2026-05-05T12:42:00Z",
"captured_at": "2026-05-05T12:43:11Z",
"finalized_at": "2026-05-05T12:43:11Z",
"expires_at": "2026-05-05T13:42:00Z",
"processing_until":"2026-05-05T13:02:00Z",
"metadata": { "customer_id": "user-7821" },
"customer_card_brand": "visa",
"customer_card_mask": "411111******1111",
"customer_card_holder": "IVAN IVANOV",
"customer_phone_mask": "+7•••••••12-34",
"customer_payer_bank": "Сбер",
"refunds": [
{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"amount": 50000,
"reason": "частичный по запросу",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z",
"completed_at": "2026-05-05T15:10:08Z"
}
]
}Отмена платежа
/api/v1/public/payments/{id}/cancelcurl -X POST https://api.balancedpay.pro/api/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f/cancel \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"status": "cancelled"
}Метод any (мультиформа)
Когда мерчант создаёт платёж с method: "any" (или вообще без поля method), в ответе payment_url ведёт на нашу страницу https://pay.balancedpay.pro/pay/<id>. Покупатель сам выбирает способ оплаты: карта, СБП, T-Pay, SberPay или recurrent.
Жизненный цикл
- Создаётся платёж со статусом
pending, methods=any. Banking-сессии ещё нет,processing_untilпустой. - Покупатель открывает
payment_url, видит кнопки методов. - Жмёт «Оплатить через СБП» (например). Метод фиксируется: статус остаётся
pending, полеmethodменяется сanyнаsbp. Webhook на этом шаге не отправляется. - Открывается банк-сессия: статус становится
processing,processing_untilзаполняется на длительность TTL метода (для СБП — 20 мин). Webhook'ом этот переход не сопровождается. - Банк присылает финальный статус:
succeededилиfailed. Прилетает webhookpayment.succeededилиpayment.failed.
processing_until банк не вернёт статус, платёж переходит в expired, и мерчант получает webhook payment.expired. Это другая семантика, чем failed: банк не отказал, просто не закрыл сессию.До выбора метода платёж нельзя ни отменить (cancel разрешён только в pending — и здесь он сработает), ни вернуть (succeeded ещё не наступил). Мерчант может смотреть статус через GET /payments/{id} или ждать webhook.
Поле metadata
metadata — произвольные пары ключ-значение, которые вы передаёте при создании платежа. Мы их не интерпретируем, только храним и отдаём обратно. Удобно прокидывать ID корзины, источник трафика, A/B-метку и т.п.
Лимиты
- Все значения — строки. Числа и булевы передавайте как
"42"/"true", мы не трогаем. - Рекомендуем не больше 20 пар на платёж и 500 символов в значении. Жёсткого лимита нет, но сверху всё это лежит в JSONB-колонке БД.
- Кодировка — UTF-8.
Где их потом увидеть
- В ответе
POST /payments— полеpayment.metadata. - В ответе
GET /payments/{id}— то же поле. - В
data-объекте webhook'а.
Зарезервированные ключи
Помимо ваших данных в metadata могут оказаться служебные ключи, которые проставляем мы. Ничего страшного, просто не удивляйтесь:
| ключ | когда появляется |
|---|---|
mode | всегда. Значение test или live по используемому ключу. |
webhook_url | если вы передали webhook_url в теле POST /payments. |
customer_id | если вы передали customer_id в теле POST /payments. |
bank_ref | live-режим: ID операции на стороне банка-эквайера. |
failure_code | на failed/expired: наш унифицированный код отказа (см. раздел «Коды отказов»). |
bank_failure_code | на failed: сырой код от банка. Только для аудита, программно опираться лучше на failure_code. |
simulator_outcome | test-режим: запланированный исход симулятора (succeeded, failed, requires_action, pending). |
simulator_failure_code | test-режим: код, который симулятор подсунет как банковский ответ. |
Не используйте эти имена для своих ключей: при пересечении мы перезапишем ваше значение.
Возврат покупателя в магазин
Если в POST /payments вы указали return_url, после достижения финального статуса мы перенаправим покупателя по этому адресу с двумя query-параметрами:
https://example.com/order/1042/done?payment_id=5a331a39-…&status=succeeded
| параметр | значение |
|---|---|
payment_id | UUID платежа. |
status | succeeded | failed | cancelled | refunded | expired. |
Существующие query-параметры в вашем return_url сохраняются. Если в URL уже был ?ref=email, он останется, плюс добавятся наши два.
payment.succeeded или явный GET /payments/{id}.Когда происходит редирект
- succeeded: через ~2 секунды после показа экрана «Платёж выполнен».
- failed / cancelled / refunded / expired: сразу.
- Если
return_urlне указан, мы оставляем покупателя на нашей странице с состоянием.
Возвраты
Возврат — самостоятельный объект, привязанный к платежу. На одном платеже допускается несколько частичных возвратов; когда их сумма достигает amount исходного платежа, его статус становится refunded. На каждый успешный возврат отправляется webhook payment.refunded.
Объект Refund
| Поле | Тип | Описание |
|---|---|---|
id | UUID | Идентификатор возврата. |
payment_id | UUID | Платёж, по которому сделан возврат. |
merchant_id | UUID | Мерчант. Определяется по API-ключу. |
amount | int64 | Сумма возврата в минорных единицах. |
reason | string | Причина возврата. Попадает в чек и audit-лог. |
status | enum | pending → succeeded или failed. |
created_at | time | Когда возврат был создан. |
completed_at | time | Момент перехода в финальный статус. |
{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"amount": 50000,
"reason": "частичный возврат по запросу клиента",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z",
"completed_at": "2026-05-05T15:10:02Z"
}Возврат средств
/api/v1/public/payments/{id}/refundamountСумма возврата в минорных единицах. Если поле не передано или равно 0 — возвращается весь остаток: amount − amount_refunded.reasonПричина возврата. Отображается покупателю в чеке и фиксируется в audit-логе.curl -X POST https://api.balancedpay.pro/api/v1/public/payments/5a331a39-32bf-4940-afb1-855e2fc6757f/refund \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-d '{
"amount": 50000,
"reason": "частичный возврат по запросу клиента"
}'{
"id": "9b2c1f8a-1111-2222-3333-444444444444",
"payment_id": "5a331a39-32bf-4940-afb1-855e2fc6757f",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"amount": 50000,
"reason": "частичный возврат по запросу клиента",
"status": "succeeded",
"created_at": "2026-05-05T15:10:00Z"
}Отдельного эндпоинта получения возврата по id в публичном API нет. Актуальный список возвратов всегда возвращается в массиве refunds[] ответа GET /api/v1/public/payments/{id}. Сводный отчёт по всем возвратам магазина доступен в личном кабинете.
Выплаты
Выплата по СБП на счёт физлица
/api/v1/public/payoutsamountСумма выплаты в минорных единицах (копейки). Обязательно.methodСпособ выплаты. Сейчас доступен только payouts_sbp; используется по умолчанию.phoneТелефон получателя в формате +7XXXXXXXXXX. Обязательно.bank_idmember_id банка-получателя в реестре НСПК (12 цифр). Например, 100000000111 — Сбербанк. Обязательно.full_nameФИО получателя в одну строку. Используется банком для дополнительной сверки.curl -X POST https://api.balancedpay.pro/api/v1/public/payouts \
-H 'Authorization: Bearer sk_test_x9k…' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: 7f2a-payout-001' \
-d '{
"amount": 250000,
"method": "payouts_sbp",
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
}'{
"payout": {
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"amount": 250000,
"currency": "RUB",
"method": "payouts_sbp",
"status": "pending",
"recipient": {
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
},
"created_at": "2026-05-05T16:01:00Z"
},
"mode": "live",
"bank": "Сбер", // имя банка-эквайера, если он ответил sync
"bank_ref": "PAY2026050601234"
}Поля failure_reason и idempotent возвращаются при отказе или повторе запроса по Idempotency-Key. В тестовом режиме добавляются служебные поля симулятора — описаны на странице Тестовая среда.
Статус выплаты
/api/v1/public/payouts/{id}curl -X GET https://api.balancedpay.pro/api/v1/public/payouts/f47ac10b-58cc-4372-a567-0e02b2c3d479 \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"merchant_id": "22222222-2222-2222-2222-222222222222",
"shop_id": "33333333-3333-3333-3333-333333333333",
"amount": 250000,
"currency": "RUB",
"method": "payouts_sbp",
"status": "succeeded",
"fail_reason": "",
"recipient": {
"phone": "+79001234567",
"bank_id": "100000000111",
"full_name": "Иванов Иван Иванович"
},
"created_at": "2026-05-05T16:01:00Z",
"completed_at": "2026-05-05T16:01:09Z"
}Банки СБП
Поле bank_id в POST /payouts — это member_id участника НСПК (12 цифр). Полный реестр ведёт НСПК; мы зеркалим его через интеграцию с Банком 131 и отдаём одним списком.
Справочник банков СБП
/api/v1/public/banks/sbpcurl -X GET https://api.balancedpay.pro/api/v1/public/banks/sbp \ -H 'Authorization: Bearer sk_test_x9k…' \ -H 'Content-Type: application/json'
{
"items": [
{ "member_id": "100000000004", "name": "Тинькофф Банк", "name_en": "T-Bank" },
{ "member_id": "100000000005", "name": "ВТБ", "name_en": "VTB" },
{ "member_id": "100000000007", "name": "Альфа-Банк", "name_en": "Alfa-Bank" },
{ "member_id": "100000000111", "name": "Сбербанк", "name_en": "Sberbank" },
// … и так весь реестр НСПК (~200 участников), отсортирован по name
]
}Если нужного банка нет в списке
Справочник — список для UI, не allow-list. Принимается любой member_id из официального реестра НСПК; ограничения нет, валидация проходит на стороне банка-получателя.
Валидация на стороне balancedpay
bank_idне из 12 цифр —400 bad_requestвозвращается до отправки в банк.member_idформально корректен, но в НСПК отсутствует — банк-эквайер вернёт отказ, операция перейдёт вfailedсfailure_codedo_not_honorилиbank_unknown, либо запрос отдаст502 router_error.