Приём по СБП¶
Плательщик переводит деньги по номеру телефона через Систему быстрых платежей из приложения своего банка. Вы создаёте заявку, API выдаёт номер телефона получателя и имя, вы показываете их плательщику и ждёте результат.
- Ваш сервер → API
POST /api/v1/paymentsсpayment_method=sbp - APIПодбирает реквизиты, обычно до 15 секунд
- API ⇢ Ваш сервер
201:status=processing,requisite=+7…,holder_name - Ваш сервер → ПлательщикНомер телефона, получатель, сумма и срок
- ПлательщикПереводит по СБП в приложении своего банка
- API → Ваш серверCallback:
completedилиcanceled - Ваш сервер ⇢ APIОтвечаете
2xx
Создать заявку¶
POST /api/v1/payments — подписанный запрос с JSON-телом.
POST {{base_url}}/api/v1/payments HTTP/1.1
Authorization: Bearer nl_test_...
Content-Type: application/json
Content-Digest: sha-256=:…:
X-Request-ID: 0b6d2c4e-8f1a-4d3b-9e7c-5a2f1b3c4d5e
X-Request-Nonce: 9f86d081884c7d659a2feaa0c55ad015
Signature-Input: sig1=(…);created=…;expires=…;keyid="ed25519-…";alg="ed25519"
Signature: sig1=:…:
{"merchant_payment_id":"order-42","amount":3400,"payment_method":"sbp","bank_code":"sber","callback_url":"https://merchant.example/callbacks"}
Поля запроса¶
| Поле | Тип | Обяз. | Правила |
|---|---|---|---|
merchant_payment_id |
строка | да | Ваш номер заказа. 1–64 символа A-Za-z0-9._:-. Уникален в проекте: это ключ идемпотентности |
amount |
число или строка | да | Сумма в основных единицах валюты: 3400 или "3400.50". Больше нуля, не больше 6 знаков после точки. Строка надёжнее числа: без ошибок округления |
payment_method |
строка | да | sbp |
currency |
строка | нет | Код валюты. По умолчанию RUB |
geo_code |
строка | нет | Страна, две буквы. По умолчанию RU |
bank_code |
строка | нет | Банк плательщика — код из справочника в нижнем регистре: sber, tinkoff, ozon. Это не БИК. Пусто — любой банк |
is_intrabank |
логическое | нет | true — перевод внутри одного банка. По умолчанию false |
callback_url |
строка | нет | Куда слать callback по этой заявке. Абсолютный https://, до 2048 символов, без логина и пароля, публичный хост. Иначе — адрес проекта |
Другие поля не принимаются: неизвестное поле — 400 bad_request. Тестовый режим
задаётся настройкой проекта, а не полем запроса. Запрос без currency и geo_code и тот же
запрос с "RUB" и "RU" — один и тот же запрос.
Срока оплаты в запросе нет: сколько живёт заявка, задаёт настройка проекта — по умолчанию 15 минут с момента создания. Изменить срок можно через вашего менеджера.
Ответ¶
201 Created — новая заявка. 200 OK — повтор с тем же merchant_payment_id
и тем же телом: вернулась уже созданная заявка, новой не появилось.
{
"id": "3f1c2b9e-6a8d-4c4b-9a71-2c7f5d1e0a42",
"merchant_payment_id": "order-42",
"flow": "runtime_live",
"status": "processing",
"amount": 3400,
"initial_amount": 3400,
"currency": "RUB",
"geo_code": "RU",
"payment_method": "sbp",
"bank_code": "sber",
"is_intrabank": false,
"callback_url": "https://merchant.example/callbacks",
"is_test": false,
"merchant_information": {
"course": 100.00,
"rate": 11.00,
"amount_usdt": "34.0000",
"amount_rate": "3026.0000",
"amount_usdt_rate": "30.2600"
},
"requisite": "+79991234567",
"holder_name": "Иван Петров",
"created_at": "2026-09-29T09:30:00Z",
"updated_at": "2026-09-29T09:30:01Z"
}
| Поле | Что значит |
|---|---|
id |
Идентификатор заявки. Сохраните его рядом с заказом |
merchant_payment_id |
Ваш номер заказа |
flow |
runtime_live — обычная заявка, project_test — тестовая заявка проекта |
status |
Текущий статус, см. ниже |
amount |
Сумма к оплате, в основных единицах валюты. Может измениться после проверки оплаты |
initial_amount |
Сумма из запроса |
currency, geo_code, payment_method, is_intrabank |
Как в запросе, с учётом значений по умолчанию |
bank_code |
Банк плательщика из запроса или null. Это не банк реквизита |
callback_url |
Адрес callback-а из запроса или null |
is_test |
true у тестовой заявки проекта |
merchant_information |
Расчёт по заявке в USDT; null, пока реквизиты не выданы |
requisite |
Номер телефона получателя для перевода по СБП; null, пока не выдан |
holder_name |
Имя получателя, как его покажет банк плательщика, или null |
qrcode_link, deeplink_url |
Только если они есть у реквизитов: можно показать QR-код или кнопку «Открыть банк» |
error_code, error_comment |
Только у заявки в статусе error: почему реквизиты не выданы |
created_at, updated_at |
Время создания и последнего изменения, UTC |
Поля bank_code, callback_url, merchant_information, requisite и holder_name есть в
ответе всегда, значение может быть null. Новые поля могут добавляться без смены версии
API: неизвестные поля игнорируйте.
Расчёт merchant_information¶
Сколько вы получите по заявке. Курс и ставка фиксируются, когда выдаются реквизиты, и потом не меняются: по текущему курсу расчёт не пересчитывается. После проверки оплаты расчёт идёт по новой сумме с теми же курсом и ставкой. Это те же цифры, что зачисляются на баланс проекта.
| Поле | Что значит | Тип, знаков после точки |
|---|---|---|
course |
Курс: сколько единиц валюты заявки за 1 USDT | число, 2 |
rate |
Ставка комиссии проекта, % | число, 2 |
amount_usdt |
Сумма заявки в USDT | строка, 4 |
amount_rate |
Сумма за вычетом комиссии, в валюте заявки | строка, 4 |
amount_usdt_rate |
Сумма за вычетом комиссии в USDT — её зачислят на баланс проекта | строка, 4 |
course и rate — числа ровно с двумя знаками (100.00), суммы — десятичные строки.
Лишние знаки отбрасываются, а не округляются вверх; комиссия считается с округлением вниз.
В примере выше: 3400 RUB по курсу 100 и ставке 11% — 34 USDT, комиссия 374 RUB (3,74 USDT),
к зачислению 30,26 USDT.
Читайте course и rate как decimal
Разбирайте их десятичным типом (Decimal, BigDecimal, json.Number), а не float —
иначе можно потерять точность.
Не пересчитывайте amount_usdt сами
Курс может храниться точнее двух знаков, поэтому amount / course иногда
расходится с amount_usdt в последнем знаке. Верное значение — amount_usdt.
Что показать плательщику¶
Показывайте реквизиты, только когда статус processing и есть requisite. Плательщик
переводит ровно amount на requisite.
- Номер телефона из
requisite— крупно, с кнопкой «Скопировать». - Получатель из
holder_name: плательщик сверит имя в приложении банка перед переводом. - Сумма — ровно
amount, до копейки. Предупредите: перевод другой суммы может не зачесться автоматически. - Срок — обратный отсчёт от
created_atна срок заявки вашего проекта (по умолчанию 15 минут). После срока реквизиты использовать нельзя. - Что дальше — «После перевода вернитесь на эту страницу». Статус обновляйте по callback-у или опросом.
Не кешируйте реквизиты
Реквизиты выдаются на одну заявку. Для нового заказа или повторной попытки
создайте новую заявку с новым merchant_payment_id.
Статусы¶
| Статус | Что значит | Что делать |
|---|---|---|
| created | Заявка принята, реквизиты ещё не выданы | Ждать. Обычно ответ сразу приходит уже в processing |
| processing | Реквизиты выданы, ждём перевод | Показать реквизиты плательщику |
| completed | Оплата подтверждена | Выдать товар или услугу |
| canceled | Не оплачено: срок заявки истёк, заявку отменили или оплату не удалось подтвердить | Предложить оплатить заново — новой заявкой |
| error | Реквизиты не выданы, причина в error_code |
Предложить оплатить заново — новой заявкой |
| appeal | Оплату по закрытой заявке перепроверяют | Ждать решения: если итог — completed или canceled, придёт callback |
error бывает только до выдачи реквизитов. Из processing заявка переходит только в
completed или canceled: если после выдачи реквизитов оплату не удалось подтвердить —
тоже canceled, без error_code. completed, canceled и error — финальные. Изменить их
может только проверка оплаты или решение службы поддержки. Все коды ошибок — в разделе
Статусы и ошибки.
Как узнать результат¶
Callback — основной способ. Когда заявка становится completed или canceled,
API отправляет POST на ваш адрес. Как проверить подпись и обработать — в разделе
Callback-и. О статусе error callback не приходит: он бывает только до
выдачи реквизитов и виден в ответе на создание и при чтении заявки. Если плательщик уже
увидел реквизиты, результат придёт callback-ом.
Опрос — страховка. Если callback не пришёл, прочитайте заявку:
curl -sS "{{base_url}}/api/v1/payments/3f1c2b9e-6a8d-4c4b-9a71-2c7f5d1e0a42" \
-H "Authorization: Bearer $API_TOKEN"
Ответ — та же форма, что при создании. Если в политике проекта включено
signature_required, подпишите и GET (тело пустое).
Рекомендуем так:
- Страница оплаты у плательщика опрашивает ваш сервер, а не API.
- Ваш сервер запрашивает заявку у API, только если callback не пришёл: например,
раз в минуту для заявок в
processing, у которых срок заявки уже прошёл. - Не опрашивайте чаще раза в 5–10 секунд на заявку: у проекта общий лимит запросов.
Крайние случаи¶
Ответ сразу со статусом error. 201 со status: "error" значит, что заявка
сохранена, но реквизитов нет. Причина — в error_code (provider_no_requisites,
provider_result_ambiguous). Callback-а об этой заявке не будет. Плательщику нечего
показывать — предложите попробовать позже, новой заявкой.
422 при создании. Заявку нельзя провести: payment_not_covered (метод или сумма
вне вашего договора), payment_rate_unavailable (нет курса), payment_not_routable
(сейчас платёж этим способом принять нельзя). Если заявка успела сохраниться, в теле есть
payment_id: заявка осталась в статусе error, номер занят, и повтор того же запроса
вернёт её (200). Для новой попытки нужен новый номер. Без payment_id заявка не
создана, и номер свободен.
Таймаут или обрыв связи. Вы не знаете, создана ли заявка. Повторите тот же запрос
с тем же merchant_payment_id и новыми nonce и подписью: если заявка есть, вернётся
она (200), если нет — создастся. Двойной заявки не будет.
Плательщик перевёл другую сумму. Оплату могут перепроверить и подтвердить
фактическую сумму. Тогда придёт callback с completed и новой суммой: amount
изменится, initial_amount останется прежним. Выдавайте заказ по сумме из callback-а.
Срок истёк. Неоплаченная к концу срока заявка становится canceled, и приходит
callback. Если плательщик перевёл деньги после срока, заявку разберут вручную: при
подтверждённой оплате придёт новый callback completed.
Оплату не удалось подтвердить после выдачи реквизитов. Тогда заявка становится
canceled без error_code, и приходит callback. Если оплата всё же прошла, её разберут вручную: придёт новый callback completed.
Долгий ответ. Реквизиты подбираются прямо во время запроса. Держите таймаут клиента не меньше 15 секунд.
Смотрите также¶
- Приём по карте — то же, но перевод на карту.
- Идемпотентность и лимиты — повторы запросов и лимиты.
- API reference — схема полей.