Документация API
Приём платежей по СБП: вы создаёте счёт, отправляете покупателя на страницу оплаты, получаете уведомление о зачислении. Три запроса и один обработчик.
Порядок подключения
- 1. Зарегистрируйте компанию и создайте кассу в кабинете.
- 2. Выпустите тестовый ключ — он доступен сразу, до проверки компании. Отдельно выпустите ключ вебхуков на кассе: при создании подписки секрет показывается один раз, и потерять его нельзя — заведите его в коде сайта сразу.
- 3. Создайте счёт и проведите оплату в тестовом режиме.
- 4. Напишите обработчик уведомления и проверьте подпись секретом вебхуков — так вы узнаёте об оплате, а не по собственному опросу статуса.
- 5. После модерации переключите кассу в боевой режим и выпустите боевой ключ. На проде используйте именно его и адрес вашей боевой кассы: тестовый ключ создаёт только тестовые счета, на которые никто не может прислать настоящие деньги.
Базовый адрес
https://api.well-pay.pro
Спецификация OpenAPI
Машиночитаемое описание всего API — методы, схемы, коды ошибок и формат уведомлений с проверкой подписи. Годится и генератору клиентов, и языковой модели: отдайте файл целиком и попросите написать интеграцию под ваш стек.
https://api.well-pay.pro/openapi.json
Авторизация
Каждый запрос — с заголовком X-API-Key. Ключ у каждой кассы свой.
X-API-Key: wp_live_ВАШ_КЛЮЧ
ВАШ_КЛЮЧ — не настоящий токен, а место под ваш: ключ выпускается в кабинете
и выглядит как wp_live_… (бой) или wp_test_… (тест).
Где взять ключ
В кабинете: Кассы → нужная касса → Ключи API → Выпустить. Секрет показывается один раз — сохраните его сразу. Потеряли: выпустите новый, старый отзовите. Восстановить нельзя, у нас хранится только хеш.
Режим ключа важнее режима кассы
Ключ выпускается под конкретный режим: wp_test_… создаёт только тестовые
счета, wp_live_… — только боевые. Ваша интеграция не начнёт внезапно
принимать настоящие деньги из-за переключения тумблера в кабинете.
Создание счёта
POST /v1/invoices
Суммы — в копейках
Целые числа невозможно испортить округлением. 10000 — это 100 ₽.
В примере — wp_test_…: так удобнее пробовать. На проде подставьте свой wp_live_… и проверьте, что касса в боевом режиме.
curl -X POST https://api.well-pay.pro/v1/invoices \
-H "X-API-Key: wp_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 суток. |
| 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.well-pay.pro/i/xxxxxxxx",
"qr_text": null,
"expires_at": "2026-08-18T15:30:00+00:00",
"paid_at": null
}
Повторный запрос безопасен
Если ответ не дошёл и вы повторили запрос с тем же 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-WellPay-Signature: sha256=<hex>
X-WellPay-Timestamp: 1787060000
X-WellPay-Event: payment.succeeded
X-WellPay-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.
Тестовый режим
Тестовый ключ доступен сразу после регистрации, до проверки компании. Счета создаются как настоящие, на странице оплаты есть кнопка «Оплатить (тест)» — она проводит полный цикл вместе с вебхуком.
Тестовые платежи не попадают в баланс
Это не настоящие деньги: в реестре они помечены, в оборот и остаток не входят.
Ошибки
{"status": "failed", "error_message": "Минимальная сумма платежа — 10,00 ₽"}
| Код | Что означает |
|---|---|
| 401 | Ключ не передан, неверен или отозван |
| 403 | Касса на паузе, компания не прошла модерацию или адрес не разрешён |
| 404 | Счёт не найден |
| 422 | Данные не прошли проверку — повторять бессмысленно |
| 502 | Провайдер недоступен — повторите позже |
Остались вопросы?
Напишите — поможем с интеграцией.
Написать в поддержку