Перейти к содержимому
Последнее обновление

Котировка и заказ

Котировка

POST /sbp/quotes

Окончательная цена до выпуска QR. amountUsdt показывает, сколько получит клиент (на карту cardId или на баланс компании) за вычетом всех комиссий и вашей наценки. rate — сколько рублей стоит 1 полученный USDT. Покажите это клиенту и создайте заказ по quoteId, пока котировка не истекла (expiresAt, около двух минут). Клиент должен быть верифицирован.

ПараметрТипОбяз.Описание
customerIdstringдаВаш идентификатор клиента
amountRubstringдаСколько рублей клиент отправит по QR: десятичная строка, до 2 знаков
cardIdstringсм. нижеКарта компании, которую пополнить купленным
partnerFeeBpsintegerнетВаша наценка в базисных пунктах от суммы заказа (100 = 1%), от 0 до 9999, по умолчанию 0. См. Ваша наценка
purposestringнетtopup (по умолчанию) — покупка USDT. card_issue — оплата выпуска карты, см. Выпуск карты за рубли

cardId обязателен, если покупки вашей компании зачисляются только на карту. Без него в режиме «на карту или на баланс» USDT лягут на баланс компании.

curl -X POST 'https://b2b.lumowallet.io/sbp/quotes' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "cust-8841",
    "amountRub": "10000",
    "cardId": "3f1d2a4b-8c7e-4f61-9a0b-5d2e1c3b4a59"
  }'
{
  "quoteId": "b7a3c1e2-9d4f-4a8b-8e6c-1f2d3a4b5c6d",
  "customerId": "cust-8841",
  "target": "card",
  "cardId": "3f1d2a4b-8c7e-4f61-9a0b-5d2e1c3b4a59",
  "amountRub": "10000.00",
  "amountUsdt": "117.42",
  "partnerFeeUsdt": "0.00000000",
  "rate": "85.164367",
  "expiresAt": "2026-10-24T10:17:02.000Z"
}

При зачислении на карту amountUsdt — сумма пополнения карты с точностью до цента, комиссия пополнения уже вычтена. Если после округления остаётся меньше цента, этот остаток ложится на баланс компании. Сумма меньше минимального пополнения карты — ответ 400 SBP_AMOUNT_OUT_OF_RANGE ещё до выпуска QR.

Ваша наценка

Чтобы зарабатывать на покупках, передайте в котировке partnerFeeBps: свою наценку в базисных пунктах от суммы заказа (100 = 1%). Размер выбираете сами и можете менять от котировки к котировке. Нужна фиксированная сумма в рублях — пересчитайте её в bps от amountRub.

Наценка удерживается из купленного так же, как наша комиссия: amountUsdt и rate уже её учитывают, а сама она приходит отдельным полем partnerFeeUsdt. После оплаты мы зачисляем на баланс компании всю покупку, пополняем карту на amountUsdt, а partnerFeeUsdt остаётся у вас на балансе.

curl -X POST 'https://b2b.lumowallet.io/sbp/quotes' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "customerId": "cust-8841",
    "amountRub": "10000",
    "cardId": "3f1d2a4b-8c7e-4f61-9a0b-5d2e1c3b4a59",
    "partnerFeeBps": 200
  }'
{
  "quoteId": "c8b4d2f3-0e5a-4b9c-9f7d-2a3e4b5c6d7e",
  "customerId": "cust-8841",
  "target": "card",
  "cardId": "3f1d2a4b-8c7e-4f61-9a0b-5d2e1c3b4a59",
  "amountRub": "10000.00",
  "amountUsdt": "111.61",
  "partnerFeeUsdt": "2.37107291",
  "rate": "89.597706",
  "expiresAt": "2026-10-24T10:17:02.000Z"
}

Здесь клиент заплатит 10 000 ₽, на карту ляжет 111,61 USD, а 2,37 USDT останутся на балансе компании. Если наценка не оставляет клиенту ничего или сумма пополнения выходит меньше минимальной, котировка ответит 400 SBP_AMOUNT_OUT_OF_RANGE.

Заказ

POST /sbp/orders

Выпускает QR СБП по котировке. Сумма и курс заказа берутся ровно из неё. Нужен заголовок Idempotency-Key.

ПараметрТипОбяз.Описание
quoteIdstringдаquoteId из POST /sbp/quotes
externalIdstringнетВаш идентификатор заказа
curl -X POST 'https://b2b.lumowallet.io/sbp/orders' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Idempotency-Key: 2c9e7f1a-5b3d-4e8a-9c6f-0a1b2c3d4e5f' \
  -H 'Content-Type: application/json' \
  -d '{
    "quoteId": "b7a3c1e2-9d4f-4a8b-8e6c-1f2d3a4b5c6d",
    "externalId": "buy-5531"
  }'
{
  "id": "e4d2c1b0-7a6f-4e5d-8c3b-2a1f0e9d8c7b",
  "externalId": "buy-5531",
  "customerId": "cust-8841",
  "quoteId": "b7a3c1e2-9d4f-4a8b-8e6c-1f2d3a4b5c6d",
  "status": "awaiting_payment",
  "target": "card",
  "cardId": "3f1d2a4b-8c7e-4f61-9a0b-5d2e1c3b4a59",
  "amountRub": "10000.00",
  "amountUsdt": "117.42",
  "partnerFeeUsdt": "0.00000000",
  "rate": "85.164367",
  "cardRechargeId": null,
  "cardRechargeStatus": null,
  "qrUrl": "https://qr.nspk.ru/…",
  "qrPngBase64": "iVBORw0KGgoAAAANSUhEUgAA…",
  "payerPhoneMasked": "+7 999 ***-**-67",
  "failureReason": null,
  "expiresAt": "2026-10-24T11:15:10.000Z",
  "completedAt": null,
  "createdAt": "2026-10-24T10:15:10.402Z"
}

Как показать QR клиенту:

  • на телефоне отдайте qrUrl операционной системе: она предложит открыть приложение банка;
  • на компьютере покажите картинку из qrPngBase64, клиент отсканирует её телефоном.

payerPhoneMasked — номер, с которого банк ждёт оплату. Покажите его клиенту рядом с QR. QR живёт до expiresAt, около часа. У клиента может быть только один активный заказ.

Коды ответа и Idempotency-Key

КодЧто это значит
201Заказ создан, QR в ответе
200Повтор с тем же Idempotency-Key: тот же заказ. Картинки QR в ответе нет, берите её из GET /sbp/orders/{id}
202status: creating, банк ещё не подтвердил выпуск QR

202 creating: не создавайте новый заказ. QR мог выпуститься, мы уточним это сами. Перечитайте заказ через несколько секунд (GET /sbp/orders/{id}): он станет awaiting_payment или failed. Повтор с тем же ключом безопасен. С новым ключом по той же котировке получите 409 SBP_QUOTE_USED.

Тот же ключ с другим quoteId — 409 IDEMPOTENCY_KEY_REUSED.

Статусы заказа

statusЧто происходит
creatingВыпускаем QR у банка
awaiting_paymentQR выпущен, ждём оплату
completedОплачено, USDT зачислены
expiredQR истёк без оплаты
cancelledЗаказ отменён
failedНе удалось выпустить QR или провести оплату, причина в failureReason

Каждая смена статуса приходит вебхуком sbp.order.status_changed. Если оплата всё-таки придёт после expired или failed, заказ станет completed и USDT будут зачислены.

Зачисление на карту

При target: card после оплаты мы сначала зачисляем USDT на баланс компании, а затем пополняем карту ровно на amountUsdt. Ваша наценка partnerFeeUsdt на карту не идёт и остаётся на балансе. Ход пополнения виден в cardRechargeStatus, а cardRechargeId связывает заказ с событиями card.recharge.* (см. Webhooks по картам).

cardRechargeStatusЧто это значит
nullПополнение ещё не запущено. При сбое связи мы повторим его сами
pending, succeeded, in_progress_manual, refunded, failedСтатус пополнения на момент запуска, как в Пополнении карты. Дальше следите за card.recharge.*

Если пополнение отклонено (cardRechargeStatus: failed), купленные USDT остаются на балансе компании. Пополнить карту можно обычным POST /cards/recharge.

Заказы клиента

GET /sbp/orders/{id}
GET /sbp/orders?customerId=cust-8841&status=awaiting_payment,completed&limit=50&offset=0

Пока заказ в статусе awaiting_payment, ответ GET /sbp/orders/{id} содержит qrUrl, qrPngBase64 и payerPhoneMasked. Список возвращает { "items": [...], "total": 12 }, новые заказы идут первыми, картинки QR в нём нет.

Отмена

POST /sbp/orders/{id}/cancel

Отменяет неоплаченный заказ. Отменяйте брошенные заказы сразу: каждый неоплаченный QR приближает блокировку клиента в банке. Если заказ уже оплачен или завершён, ответ — 409 SBP_ORDER_NOT_CANCELABLE.

Ошибки

errorCodeHTTPЧто произошло
SBP_BUY_NOT_ENABLED403Покупка не подключена вашей компании
SBP_CUSTOMER_NOT_FOUND404Клиент не зарегистрирован, сначала POST /sbp/customers
SBP_CUSTOMER_INVALID400Банку нужен российский мобильный номер плательщика: обновите phone через POST /sbp/customers
SBP_CUSTOMER_NOT_VERIFIED412Клиент не верифицирован, статус в details
SBP_CARD_REQUIRED400Покупки компании идут только на карту, нужен cardId
SBP_AMOUNT_OUT_OF_RANGE400Сумма вне границ GET /sbp/rate, меньше минимального пополнения карты или наценка не оставляет клиенту ничего
QUOTE_NOT_FOUND / QUOTE_EXPIRED404 / 400Котировки нет или она истекла, запросите новую
SBP_QUOTE_USED409По котировке уже создан заказ
IDEMPOTENCY_KEY_REQUIRED400Нет заголовка Idempotency-Key
IDEMPOTENCY_KEY_REUSED409Ключ уже использован для другой котировки
SBP_ACTIVE_ORDER_EXISTS409У клиента уже есть неоплаченный заказ
SBP_LIMIT_EXCEEDED429Превышен лимит банка на клиента, причина в details.providerCode
SBP_ORDER_NOT_FOUND404Заказа нет
SBP_ORDER_NOT_CANCELABLE409Заказ уже не отменить
SBP_PROVIDER_REJECTED422Банк отклонил запрос, код в details.providerCode
SBP_PROVIDER_UNAVAILABLE / SBP_BUY_UNAVAILABLE503Сервис временно недоступен, повторите позже
SBP_KYC_UNAVAILABLE503Верификация временно недоступна

Отказы банка

У ошибок от банка в details есть providerCode, а иногда ещё и providerDetails:

{
  "errorCode": "SBP_LIMIT_EXCEEDED",
  "message": "…",
  "details": {
    "providerCode": "sbp_buy_fiat_daily_limit",
    "providerDetails": {
      "code": "ProviderDailyQrlimitReached",
      "limit": "100000",
      "remaining": "0",
      "resets_at": "2026-10-25T09:00:00Z"
    }
  }
}
  • providerDetails.code приходит, когда банк однозначно отказал этому клиенту. Покажите по нему понятный клиенту текст.
  • limit, remaining и resets_at приходят у лимитов в рублях: сколько клиенту доступно и когда лимит обновится.

Значения providerCode, которые стоит обработать отдельно:

providerCodeЧто сказать клиенту
sbp_buy_daily_qr_limitНа сегодня лимит покупок исчерпан, попробуйте завтра
sbp_buy_daily_paid_qr_limitЛимит оплаченных покупок на сегодня, попробуйте через час
sbp_buy_pending_qr_limitСлишком много неоплаченных QR. Ожидание не поможет, нужна поддержка
sbp_buy_fiat_daily_limit / sbp_buy_fiat_monthly_limitПревышен лимит суммы за сутки или за месяц, см. resets_at
sbp_buy_amount_unavailableБанк не может принять эту сумму, другая может пройти
sbp_buy_blockedПокупка для клиента недоступна, нужна поддержка
sbp_buy_bank_not_found / sbp_buy_bank_unsupportedБанк клиента не поддерживается