Документация API
Приём платежей по СБП: вы создаёте счёт, отправляете покупателя на страницу оплаты, получаете уведомление о зачислении. Три запроса и один обработчик.
Порядок подключения
- 1. Напишите в поддержку — вам заведут кабинет, затем создайте кассу.
- 2. Выпустите тестовый ключ — он доступен сразу, до проверки компании. Отдельно выпустите ключ вебхуков на кассе: при создании подписки секрет показывается один раз, и потерять его нельзя — заведите его в коде сайта сразу.
- 3. Создайте счёт и проведите оплату в тестовом режиме.
- 4. Напишите обработчик уведомления и проверьте подпись секретом вебхуков — так вы узнаёте об оплате, а не по собственному опросу статуса.
- 5. После модерации переключите кассу в боевой режим и выпустите боевой ключ. На проде используйте именно его и адрес вашей боевой кассы: тестовый ключ создаёт только тестовые счета, на которые никто не может прислать настоящие деньги.
Базовый адрес
https://api.shamanpayments.com
Спецификация OpenAPI
Машиночитаемое описание всего API — методы, схемы, коды ошибок и формат уведомлений с проверкой подписи. Годится и генератору клиентов, и языковой модели: отдайте файл целиком и попросите написать интеграцию под ваш стек.
https://api.shamanpayments.com/openapi.json
Авторизация
Каждый запрос — с заголовком X-API-Key. Ключ у каждой кассы свой.
X-API-Key: sp_live_ВАШ_КЛЮЧ
ВАШ_КЛЮЧ — не настоящий токен, а место под ваш: ключ выпускается в кабинете
и выглядит как sp_live_… (бой) или sp_test_… (тест).
Где взять ключ
В кабинете: Кассы → нужная касса → Ключи API → Выпустить. Секрет показывается один раз — сохраните его сразу. Потеряли: выпустите новый, старый отзовите. Восстановить нельзя, у нас хранится только хеш.
Режим ключа важнее режима кассы
Ключ выпускается под конкретный режим: sp_test_… создаёт только тестовые
счета, sp_live_… — только боевые. Ваша интеграция не начнёт внезапно
принимать настоящие деньги из-за переключения тумблера в кабинете.
Создание счёта
POST /v1/invoices
Суммы — в копейках
Целые числа невозможно испортить округлением. 10000 — это 100 ₽.
В примере — sp_test_…: так удобнее пробовать. На проде подставьте свой sp_live_… и проверьте, что касса в боевом режиме.
curl -X POST https://api.shamanpayments.com/v1/invoices \
-H "X-API-Key: sp_test_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"order_id": "order-10241",
"description": "Заказ №10241",
"success_url": "https://shop.ru/success",
"fail_url": "https://shop.ru/fail"
}'
Параметры
| Поле | Тип | Описание |
|---|---|---|
| amount | int | Сумма заказа в копейках. Обязательное. |
| order_id | string | Ваш номер заказа. Обязательное. Повтор вернёт уже созданный счёт. |
| description | string | Видно покупателю на странице оплаты. |
| success_url | string | Куда вернуть после оплаты. По умолчанию — из настроек кассы. |
| fail_url | string | Куда вернуть при отказе или истечении. |
| ttl_minutes | int | Запрошенный срок счёта: от 5 минут до 5 суток (5–7200). Для маршрутов с фиксированным сроком платёжной системы значение проверяется, но не меняет окно оплаты. См. сроки в ответе ниже. |
| payer_email | string | Контакт плательщика. Хранится зашифрованным. |
| metadata | object | Ваши данные — вернём их в вебхуке без изменений. |
Ответ 201 Created
{
"id": "inv_3F8KQ2M1N7XBZ0RTVCWD9E",
"order_id": "order-10241",
"status": "pending",
"mode": "test",
"amount": 150000,
"charged_amount": 150000,
"fee": 18000,
"net": 132000,
"currency": "RUB",
"checkout_url": "https://pay.shamanpayments.com/i/xxxxxxxx",
"qr_text": null,
"expires_at": "2026-08-18T15:30:00+00:00",
"open_until": "2026-08-18T15:30:00+00:00",
"payment_expires_at": "2026-08-18T15:30:00+00:00",
"ttl_source": "merchant",
"paid_at": null
}
Срок начала и срок оплаты
expires_at — текущий эффективный дедлайн в ISO 8601.
У отложенных маршрутов он уточняется при создании внешнего платежа:
не фиксируйте навсегда значение из ответа на создание счёта,
обновляйте его из GET /v1/invoices/{invoice_id}.
open_until — крайний срок начала отложенной оплаты из серверной
настройки кассы; для старых счетов может быть null.
payment_expires_at — сохранённый срок уже открытого платежа,
до его создания — null.
ttl_source: merchant — срок запроса или настройки кассы;
provider — фиксированная политика выбранного платёжного маршрута.
Для маршрута с фиксированным окном 600 секунд до открытия действует
open_until из серверной настройки кассы (по умолчанию 60 минут).
После начала успешного внешнего запроса на создание доступно 600 секунд:
для СБП — при открытии чекаута, для карты — при отправке формы.
Задержка ответа, обновление страницы, 3-D Secure и возврат к способу оплаты
не продлевают окно. Если получен более ранний срок провайдера, действует он.
Это политика нашего чекаута, а не гарантия доступности реквизитов со стороны платёжной системы.
По истечении фиксированного окна новая платёжка автоматически не создаётся.
Повтор допускается только по действию покупателя после подтверждённого отказа
без создания платежа и паузы 60 секунд. При неизвестном результате запроса
повтор блокируется до подтверждения или проверки поддержкой.
Для остальных провайдеров прежняя логика ttl_minutes сохраняется.
Повторный запрос безопасен
Если ответ не дошёл и вы повторили запрос с тем же order_id, вернётся
тот же счёт и код 200 вместо 201.
Новый счёт не создаётся, сроки существующего счёта и платежа не продлеваются.
Отправьте покупателя на checkout_url — там уже есть QR, таймер
и возврат в ваш магазин.
Хотите рисовать QR сами
Поле qr_text в ответе на создание почти всегда
null: код выпускает банковская система, и занимает это
несколько секунд. Мы запрашиваем его сразу, поэтому запросите счёт
ещё раз через GET /v1/invoices/{id} — там код уже будет.
Пока его нет, показывайте покупателю ожидание, а не ошибку.
Лимиты
| Минимальная сумма | 10,00 ₽ | Настраивается на кассе |
| Максимальная сумма | 50 000,00 ₽ | Предел одного платежа по СБП |
| Срок жизни счёта | 5 суток | По умолчанию 60 минут |
| Валюта | RUB | Других пока нет |
Сумма сверх лимита — это 422 с понятным текстом, а не
отказ на стороне банка: счёт в таком случае не создаётся вовсе.
Статус счёта
GET /v1/invoices/{invoice_id}
GET /v1/orders/{order_id}
POST /v1/invoices/{invoice_id}/cancel
Второй метод пригодится, когда ответ на создание потерялся: вместо повторного создания спросите, что стало с заказом.
Статусы
| pending | Ожидает оплаты |
| paid | Оплачен, деньги получены |
| expired | Истёк срок |
| canceled | Отменён |
Вебхуки
Адрес обработчика задаётся на кассе. Там же при создании подписки один раз
показывается секрет — им подписываются уведомления. Мы отправляем
POST с JSON и ждём любой ответ 2xx.
Не получили — повторим по расписанию
0, 1, 5, 30, 120, 360 минут.
{
"id": "evt_3F8KQ2M1N7XBZ0RTVCWD9E",
"event": "payment.succeeded",
"created_at": "2026-08-18T15:04:11+00:00",
"mode": "live",
"data": {
"invoice_id": "inv_3F8KQ2M1N7XBZ0RTVCWD9E",
"order_id": "order-10241",
"status": "paid",
"amount": 150000,
"charged_amount": 150000,
"fee": 18000,
"net": 132000,
"currency": "RUB",
"paid_at": "2026-08-18T15:04:10+00:00",
"metadata": null
}
}
Проверка подписи
Проверяйте её обязательно: без этого кто угодно, узнав ваш адрес, сможет прислать поддельное «оплачено».
X-ShamanPay-Signature: sha256=<hex>
X-ShamanPay-Timestamp: 1787060000
X-ShamanPay-Event: payment.succeeded
X-ShamanPay-Delivery: dlv_...
signature = HMAC_SHA256(secret, "{timestamp}.{сырое тело запроса}")
Секрет подписи — не API-ключ
В формуле выше secret — это секрет конкретной подписки на вебхуки,
выданный при её создании на кассе. Ключ X-API-Key, которым вы
создаёте счета, здесь не подойдёт: подпись не сойдётся, и все уведомления будут
отброшены. У части провайдеров это одно и то же значение — у нас нет, чтобы
утёкший ключ приёма уведомлений не давал доступа к созданию платежей.
Подписывается сырое тело
Считайте подпись до разбора JSON. Если сначала распарсить и собрать обратно, порядок ключей и пробелы изменятся — подпись не сойдётся.
import hmac, hashlib
def verify(secret: str, signature: str, timestamp: str, raw_body: bytes) -> bool:
payload = f"{timestamp}.".encode() + raw_body
expected = "sha256=" + hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
События
| payment.succeeded | Платёж прошёл, деньги получены |
| payment.failed | Платёж не прошёл |
| invoice.expired | Счёт истёк |
| invoice.canceled | Счёт отменён |
Отвечайте быстро и дедуплицируйте
Долгая обработка приводит к повторным доставкам. Ответьте
200 сразу, а работу выполняйте фоном. Одно и то же
событие может прийти дважды — сверяйтесь по полю id.
Апелляции
Покупатель перевёл деньги, а счёт не закрылся — например, заплатил под конец окна оплаты или чек ещё проверяется. Приложите чек, выписку или видео перевода: мы передадим апелляцию шлюзу и закроем счёт, когда оплату признают. Подать можно в любом статусе счёта, кроме оплаченного. То же самое доступно в кабинете — кнопка «Подать апелляцию» в карточке операции.
POST /v1/invoices/{invoice_id}/appeals
GET /v1/invoices/{invoice_id}/appeals
GET /v1/appeals/{appeal_id}
Подача
Запрос — multipart/form-data. Повтор того же файла по той же попытке
вернёт уже поданную апелляцию с кодом 200 вместо
201: ретрай при таймауте не создаёт вторую.
curl -X POST https://api.shamanpayments.com/v1/invoices/inv_3F8KQ2M1N7XBZ0RTVCWD9E/appeals \ -H "X-API-Key: sp_live_ВАШ_КЛЮЧ" \ -F "file=@receipt.pdf;type=application/pdf" \ -F "amount=150000" \ -F "comment=Списано в 20:58 со Сбера"
Поля
| file | Чек (PDF, JPG, PNG, HEIC) или видео перевода (MP4, MOV), до 25 МБ. Обязательно. |
| amount | Сколько перевёл покупатель, в копейках. Обязательно. |
| payment_id | Попытка оплаты. Не указана — последняя, по которой выдавались реквизиты. |
| comment | Что известно о переводе, до 1000 символов. |
Ответ
{
"id": "apl_3F8KQ2M1N7XBZ0RTVCWD9E",
"invoice_id": "inv_3F8KQ2M1N7XBZ0RTVCWD9E",
"order_id": "order-10241",
"payment_id": "pay_3F8KQ2M1N7XBZ0RTVCWD9E",
"status": "sent",
"status_label": "На рассмотрении",
"channel": "auto",
"amount": 150000,
"comment": "Списано в 20:58 со Сбера",
"file_name": "receipt.pdf",
"error": null,
"resolution_note": null,
"created_at": "2026-10-21T15:04:11+00:00",
"resolved_at": null
}
Статусы апелляции
| sent | На рассмотрении у шлюза |
| approved | Оплата признана — счёт оплачен |
| rejected | Шлюз оплату не нашёл |
| failed | Апелляцию не приняли — причина в error, можно подать снова |
channel: auto — передана шлюзу
автоматически, обычно отвечает за минуты; support — через его
поддержку, ответ — в течение часа днём, дольше по старым заявкам. Отдельного вебхука
по апелляции нет: когда оплату признают, придёт обычный payment.succeeded
по счёту. Статус апелляции можно спросить в любой момент.
Тестовый режим
Тестовый ключ доступен сразу после подключения, до проверки компании. Счета создаются как настоящие, на странице оплаты есть кнопка «Оплатить (тест)» — она проводит полный цикл вместе с вебхуком.
Тестовые платежи не попадают в баланс
Это не настоящие деньги: в реестре они помечены, в оборот и остаток не входят.
Ошибки
{"status": "failed", "error_message": "Минимальная сумма платежа — 10,00 ₽"}
| Код | Что означает |
|---|---|
| 401 | Ключ не передан, неверен или отозван |
| 403 | Касса на паузе, компания не прошла модерацию или адрес не разрешён |
| 404 | Счёт не найден |
| 422 | Данные не прошли проверку — повторять бессмысленно |
| 502 | Провайдер недоступен — повторите позже |
Остались вопросы?
Напишите — поможем с интеграцией.
Написать в поддержку