Перейти к содержанию

Приём по СБП

Плательщик переводит деньги по номеру телефона через Систему быстрых платежей из приложения своего банка. Вы создаёте заявку, API выдаёт номер телефона получателя и имя, вы показываете их плательщику и ждёте результат.

  1. Ваш сервер → APIPOST /api/v1/payments с payment_method=sbp
  2. APIПодбирает реквизиты, обычно до 15 секунд
  3. API ⇢ Ваш сервер201: status=processing, requisite=+7…, holder_name
  4. Ваш сервер → ПлательщикНомер телефона, получатель, сумма и срок
  5. ПлательщикПереводит по СБП в приложении своего банка
  6. API → Ваш серверCallback: completed или canceled
  7. Ваш сервер ⇢ 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.

Статусы

итоговые статусыреквизиты выданыистёк / отмена /не подтвержденореквизиты не выданыпроверка оплатыитог проверкиcreatedprocessingcompletedcancelederrorappeal
Статус Что значит Что делать
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 секунд.

Смотрите также