Документация API
Приём платежей по СБП: один POST создаёт платёж и возвращает ссылку на оплату. Дальше — статусы, возвраты, вебхуки и коды ошибок. Здесь описано только то, что работает: ручек, которых нет, в этом тексте нет тоже.
Как получить ключ
Зарегистрируйте компанию, дождитесь одобрения заявки и создайте ключ в кабинете, раздел «Разработчикам». Тестовый ключ работает сразу и проводит платежи по всему пути, не двигая деньги.
ПодключитьсяПервый запрос
Адрес https://avans.pro, тело и ответы — JSON, ключ в заголовке Authorization. Примеры ниже можно выполнять как есть, подставив свой ключ.
Начало
Адрес, авторизация и режимы
Все запросы идут по HTTPS на https://avans.pro, тело и ответы — JSON. Ключ передаётся заголовком.
curl https://avans.pro/api/v1/balance \ -H "Authorization: Bearer sk_live_..."
Ключ определяет и магазин, и режим. sk_test_… работает в тестовом контуре: платежи проходят весь путь и меняют статусы, но провайдер не задействован и деньги не двигаются. Ключи мерчант выпускает сам в кабинете, раздел «Разработчикам» → «Ключи»; секрет показывается один раз.
Секретный ключ — это доступ к деньгам магазина. Он рассчитан на серверную часть; в браузер, мобильное приложение и репозиторий его класть нельзя.
Ограничение — 300 запросов в минуту на ключ. При превышении приходит 429 rate_limited. Каждый вызов виден в кабинете, на вкладке «Логи».
Создать платёж
POST /api/v1/payments
| Поле | Тип | Описание |
|---|---|---|
| amountобяз. | integer | Сумма в копейках. 1 500,50 ₽ — это 150050. |
| methodобяз. | string | Способ оплаты: sbp, crypto и прочие подключённые. |
| currency | string | RUB, USD или EUR. По умолчанию RUB. |
| description | string | Назначение платежа, до 200 символов. Видно плательщику. |
| reference | string | Ваш номер заказа, до 64 символов. Возвращается как есть. |
| customer.name | string | Имя покупателя. |
| customer.email | string | Почта: по ней покупатель опознаётся при повторных платежах. |
| customer.city | string | Город, необязательно. |
curl -X POST https://avans.pro/api/v1/payments \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1024" \
-d '{
"amount": 150050,
"method": "sbp",
"description": "Заказ 1024",
"reference": "1024",
"customer": { "name": "Иван", "email": "[email protected]" }
}'Ответ — 201:
{
"id": "pay_6ihv8zfh0d33k6",
"object": "payment",
"amount": 150050,
"currency": "RUB",
"fee": 4351,
"net": 145699,
"status": "pending",
"raw_status": "pending",
"method": "sbp",
"method_label": "СБП",
"description": "Заказ 1024",
"reference": "1024",
"created": 1786542063,
"captured": null,
"confirmation_url": "https://avans.pro/sbp/pay_6ihv8zfh0d33k6",
"test": false
}confirmation_url — страница оплаты, куда нужно отправить покупателя. Платёж в этот момент ещё не оплачен: об оплате сообщит вебхук charge.succeeded.
fee и net — комиссия и сумма к зачислению, обе в копейках. Считаются по вашему тарифу в момент создания.
Повторы без двойных списаний
Заголовок Idempotency-Key
Если связь оборвалась и вы не знаете, дошёл ли запрос, повторите его с тем же Idempotency-Key — вернётся исходный платёж, второй не создастся.
Ключ выбираете вы: подойдёт номер заказа. Он действует в пределах вашего магазина и режима, поэтому один и тот же номер в тесте и в бою не столкнутся.
Заголовок необязателен, но при создании платежей его стоит слать всегда: без него повтор запроса — это второй платёж и второе списание с покупателя.
Один платёж
GET /api/v1/payments/{id}
Самый частый запрос: «что с моим заказом». Принимает и наш идентификатор (pay_…), и ваш номер заказа из поля reference — хранить наш ради этого не обязательно. В ответе, кроме платежа, приходят его возвраты и поле matched_by: по чему нашли.
curl "https://avans.pro/api/v1/payments/pay_rhjdhf8kbw65ka" -H "Authorization: Bearer sk_live_..." curl "https://avans.pro/api/v1/payments/order-2841" -H "Authorization: Bearer sk_live_..."
Список платежей
GET /api/v1/payments
| Поле | Тип | Описание |
|---|---|---|
| limit | integer | Сколько вернуть: от 1 до 100, по умолчанию 25. |
| starting_after | string | Идентификатор последнего платежа с прошлой страницы. |
| status | string | Отбор по статусу: succeeded, pending, failed, refunded, disputed. Принимает и внутренние значения из raw_status. |
| reference | string | Ваш номер заказа. Совпадение точное. |
| created_after | integer | Только платежи позже этого времени. Секунды эпохи — как поле created в ответах. |
| created_before | integer | Только платежи раньше этого времени. |
curl "https://avans.pro/api/v1/payments?limit=50" \ -H "Authorization: Bearer sk_live_..."
Постранично — курсором, а не смещением: пока вы листаете, приходят новые платежи, и нумерация страниц смещалась бы под руками.
Возврат
POST /api/v1/refunds
| Поле | Тип | Описание |
|---|---|---|
| paymentобяз. | string | Идентификатор платежа (pay_…). |
| amount | integer | Сумма в копейках. Не указана — возврат полный. Частичный сейчас не поддерживается. |
| reason | string | Причина, до 200 символов. Попадёт в вашу отчётность. |
| Idempotency-Key | заголовок | Повтор с тем же ключом вернёт уже созданный возврат, а не сделает второй. |
curl -X POST https://avans.pro/api/v1/refunds \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "payment": "pay_6ihv8zfh0d33k6", "amount": 50000 }'Возврат только полный: подключённый провайдер не умеет возвращать часть суммы. Укажете amount меньше суммы платежа — придёт отказ, а не молчаливый возврат всего.
Возврат выполняется не мгновенно. Ответ означает, что заявка принята; деньги вернутся позже, и тогда платёж перейдёт в статус refunded. Отследить можно по статусу платежа или по вебхуку.
Список возвратов
GET /api/v1/refunds
| Поле | Тип | Описание |
|---|---|---|
| limit | integer | От 1 до 100, по умолчанию 25. |
| starting_after | string | Идентификатор последнего возврата с прошлой страницы. |
| payment | string | Только возвраты по этому платежу. |
curl "https://avans.pro/api/v1/refunds?payment=pay_rhjdhf8kbw65ka" -H "Authorization: Bearer sk_live_..."
История событий
GET /api/v1/events
То, что мы вам присылали. Нужен, если приёмник вебхуков полежал: вместо опроса платежей по одному заберите пропущенное отсюда. Тело события — ровно то же, что уходило на эндпоинт, разбор писать заново не придётся.
| Поле | Тип | Описание |
|---|---|---|
| limit | integer | От 1 до 100, по умолчанию 25. |
| type | string | Только события этого типа, например charge.succeeded. |
| since | integer | Время в секундах эпохи — как поле created в ответах. Вернутся события позже него. |
curl "https://avans.pro/api/v1/events?since=1755200000&type=charge.succeeded" -H "Authorization: Bearer sk_live_..."
Рядом с событием приходит поле delivery: статус доставки, число попыток и код ответа вашего сервера — видно, дошло ли оно и почему нет.
Баланс
GET /api/v1/balance
{
"object": "balance",
"livemode": true,
"available": [{ "currency": "RUB", "amount": 1187550 }],
"pending": [{ "currency": "RUB", "amount": 250000 }],
"reserved": [{ "currency": "RUB", "amount": 0 }]
}Всё в копейках. available — доступно к выплате, pending — принято, но ещё не разблокировано, reserved — удержано под споры.
Вебхуки
Уведомления о событиях
Адрес задаётся на вкладке «Вебхуки». Запрос приходит методом POST с телом события и тремя заголовками:
| Поле | Тип | Описание |
|---|---|---|
| Avans-Signature | string | Подпись тела. Формат: t=<время>,v1=<подпись>. |
| Avans-Event | string | Тип события. |
| Avans-Delivery | string | Идентификатор доставки: по нему отличают повтор. |
События:
| Поле | Тип | Описание |
|---|---|---|
| charge.succeeded | событие | Платёж оплачен. Именно по нему отгружайте заказ. |
| charge.failed | событие | Платёж не прошёл, отменён или просрочен — заказ можно закрывать. |
| charge.refunded | событие | Оформлен возврат. |
| charge.disputed | событие | Открыт спор: сумма заморожена, нужны документы. |
| charge.chargeback | событие | Банк вернул деньги плательщику окончательно, сумма списана. |
| payout.paid | событие | Выплата отправлена. |
| payout.failed | событие | Выплата не прошла. |
| balance.updated | событие | Баланс изменился. |
Подпись считается по строке <время>.<тело как есть> алгоритмом HMAC-SHA256 на секрете эндпоинта:
const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const expected = crypto
.createHmac("sha256", endpointSecret)
.update(`${t}.${rawBody}`)
.digest("hex");
// Сравнение постоянного времени: обычное сравнение строк
// позволяет подбирать подпись побайтно по времени ответа.
const valid = crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(v1)
);Подпись считается по СЫРОМУ телу запроса. Если разобрать JSON и собрать обратно, порядок полей и пробелы изменятся, и подпись не сойдётся — читайте тело до разбора.
Отвечайте 200 как можно быстрее, а работу выполняйте после. Любой другой ответ или таймаут считается неудачей, и доставка повторяется с возрастающими паузами. Повтор приходит с тем же Avans-Delivery — по нему отличайте повтор от нового события.
Ошибки
Формат и коды
{
"error": {
"code": "invalid_request",
"message": "Укажите идентификатор платежа"
}
}| Поле | Тип | Описание |
|---|---|---|
| invalid_request | 400 | Неверные или недостающие поля. |
| unauthorized | 401 | Ключ не передан, неверен или отозван. |
| forbidden | 403 | Действие недоступно этому ключу. |
| account_under_review | 403 | Магазин на проверке: приём временно остановлен. |
| not_found | 404 | Объект не найден. |
| conflict | 409 | Действие противоречит текущему состоянию. |
| rate_limited | 429 | Превышены 300 запросов в минуту. В заголовке Retry-After — через сколько секунд повторять. |
| provider_unavailable | 503 | Провайдер недоступен. Повторите позже. |
| internal_error | 500 | Ошибка на нашей стороне. |
Сообщение в message написано для человека и может меняться. Ветвите логику по code.
Остались вопросы по интеграции
Напишите на [email protected] — отвечает человек, который писал это API, а не автоответчик.