Shaman Payments

Документация API

Приём платежей по СБП: вы создаёте счёт, отправляете покупателя на страницу оплаты, получаете уведомление о зачислении. Три запроса и один обработчик.

Порядок подключения

  1. 1. Напишите в поддержку — вам заведут кабинет, затем создайте кассу.
  2. 2. Выпустите тестовый ключ — он доступен сразу, до проверки компании. Отдельно выпустите ключ вебхуков на кассе: при создании подписки секрет показывается один раз, и потерять его нельзя — заведите его в коде сайта сразу.
  3. 3. Создайте счёт и проведите оплату в тестовом режиме.
  4. 4. Напишите обработчик уведомления и проверьте подпись секретом вебхуков — так вы узнаёте об оплате, а не по собственному опросу статуса.
  5. 5. После модерации переключите кассу в боевой режим и выпустите боевой ключ. На проде используйте именно его и адрес вашей боевой кассы: тестовый ключ создаёт только тестовые счета, на которые никто не может прислать настоящие деньги.

Базовый адрес

https://api.shamanpayments.com

Спецификация OpenAPI

Машиночитаемое описание всего API — методы, схемы, коды ошибок и формат уведомлений с проверкой подписи. Годится и генератору клиентов, и языковой модели: отдайте файл целиком и попросите написать интеграцию под ваш стек.

Открыть openapi.json 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"
  }'

Параметры

ПолеТипОписание
amountint Сумма заказа в копейках. Обязательное.
order_idstring Ваш номер заказа. Обязательное. Повтор вернёт уже созданный счёт.
descriptionstring Видно покупателю на странице оплаты.
success_urlstring Куда вернуть после оплаты. По умолчанию — из настроек кассы.
fail_urlstring Куда вернуть при отказе или истечении.
ttl_minutesint Запрошенный срок счёта: от 5 минут до 5 суток (5–7200). Для маршрутов с фиксированным сроком платёжной системы значение проверяется, но не меняет окно оплаты. См. сроки в ответе ниже.
payer_emailstring Контакт плательщика. Хранится зашифрованным.
metadataobject Ваши данные — вернём их в вебхуке без изменений.

Ответ 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Провайдер недоступен — повторите позже

Остались вопросы?

Напишите — поможем с интеграцией.

Написать в поддержку