Аутентификация¶
В каждом запросе к /api/v1 передавайте свой API-ключ мерчанта в заголовке x-api-key. Ни логина,
ни обмена токенами, ни сессии, которую надо поддерживать, здесь нет. Двум эндпоинтам вне этого
префикса — /docs и /health — ключ не нужен.
curl https://api.pay.nullswap.com/api/v1/invoices?limit=20 \
-H "x-api-key: sk_live_2f8b1c0d4e5a69738f2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e"
Ключ состоит из префикса sk_live_ и 64 шестнадцатеричных символов. Это не bearer-токен: заголовок
Authorization: Bearer ... просто игнорируется, и API ответит 401, как если бы ключ не передавали
вовсе.
Один ключ — один мерчант¶
Ключ однозначно определяет мерчанта. Отсюда следует правило, которое действует на всех остальных страницах:
Ни один маршрут в этом API не принимает id мерчанта
Ни в пути, ни в query-параметрах, ни в теле. Любая выборка заранее ограничена вашим мерчантом, и
всё, что вы создаёте, создаётся от его имени. Если вы ищете, куда подставить merchantId, —
такого поля нет.
В ответах merchantId всё же попадается — у инвойса или статического кошелька, —
но лишь справочно: там всегда будет ваш собственный идентификатор.
Посмотреть, какому мерчанту принадлежит ключ:
{
"id": "9f1d2c3b-4a5e-4f60-8b71-2c3d4e5f6a7b",
"name": "Acme Store",
"status": "ACTIVE",
"createdAt": "2026-01-01T12:00:00.000Z",
"updatedAt": "2026-01-01T12:00:00.000Z",
"feePercentage": "150"
}
feePercentage — комиссия мерчанта, которую мы берём с ваших платежей, в базисных пунктах ("150" —
это 1.50%). Её устанавливаем мы; маршрута, чтобы её изменить, нет. Она удерживается из каждого свипа
на ваш баланс — см. комиссию мерчанта.
Чего ключ не может¶
Администрирование мерчанта в это API не входит. Участники, API-ключи, белые списки IP, белый список адресов для вывода, правила автоконвертации и подписки на вебхуки заводятся и меняются вручную в кабинете. Маршрутов для вебхуков в API нет вовсе: через него нельзя ни создать, изменить или удалить подписку, ни прочитать её, её историю доставок или секрет, которым вы проверяете подписи, — его смотрят в кабинете.
Если точнее, данные меняют всего шесть маршрутов, и это весь их список:
| Метод | Путь |
|---|---|
POST |
/api/v1/invoices |
POST |
/api/v1/invoices/{invoiceId}/payments/{paymentId}/resolve |
POST |
/api/v1/invoices/{invoiceId}/payments/{paymentId}/refund |
POST |
/api/v1/static-wallets |
POST |
/api/v1/wallets/{walletId}/sweep |
POST |
/api/v1/merchant/withdrawals |
Маршрутов PUT, PATCH и DELETE нет.
Держите ключ на сервере¶
Ключ работает на предъявителя в самом прямом смысле: кто им владеет, тот может создавать инвойсы, выводить ваши деньги и читать всю историю ваших платежей.
Никогда не передавайте ключ в браузер или мобильное приложение
Обращайтесь к API только со своего бэкенда. Если ключ утёк, перевыпустите его в кабинете: старый перестанет работать уже со следующего запроса, потому что ключи нигде не кешируются.
Ключ у мерчанта один, он находится в кабинете в разделе Настройки → API-ключ. У нового мерчанта ключа нет: первый выпускает владелец кнопкой Выпустить ключ. Перевыпустить его может только владелец — старый ключ при этом сразу отзывается. Выпуск и перевыпуск требуют двухфакторного кода, если у владельца включена двухфакторная аутентификация.
Ключ показывается один раз
Полностью кабинет показывает ключ только сразу после выпуска или перевыпуска. Тогда же
скопируйте его в хранилище секретов вашего бэкенда: мы храним только последние четыре символа,
поэтому дальше любой участник видит ключ замаскированным (••••1d0e), и показать его снова не
может никто, включая нас. Если ключ потерян, перевыпустите его и обновите интеграции.
Белый список IP¶
Доступ к API можно дополнительно ограничить списком адресов, с которых разрешены запросы, — на карточке Доступ к API по IP в разделе Настройки → API-ключ. Список принадлежит мерчанту, а не ключу, поэтому переживает перевыпуск ключа. Менять его может только владелец. Настройки → Белые списки — это другой список: адреса для вывода.
- Пустой белый список фильтрацию не включает — ключ работает откуда угодно.
- Как только в списке появится хотя бы одна запись, запросы со всех остальных адресов начнут
получать
403 CALLER_IP_NOT_ALLOWED, даже если сам ключ полностью корректен. - Запись — это IPv4-адрес, в котором любой октет может быть
*(203.0.113.*), точный IPv6-адрес или одиночная*для любого адреса. Диапазоны CIDR не принимаются, а в IPv6 подстановки нельзя.
Заблокированный мерчант получает отказ MERCHANT_BLOCKED ещё до проверки его белого списка.
Сверяемся мы с реальным адресом, с которого установлено TCP-соединение. Заголовок X-Forwarded-For
подделывается и потому не учитывается. Если ваш трафик выходит через NAT или egress-прокси, в белый
список нужно добавлять адрес этого узла, а не адрес сервера приложения.
Ошибки¶
| Статус | message |
Причина |
|---|---|---|
401 |
INVALID_API_KEY |
Заголовка нет, он повреждён, ключ неизвестен или отозван. Различить эти четыре случая по ответу нельзя — так задумано. |
403 |
CALLER_IP_NOT_ALLOWED |
Ключ верный, но запрос пришёл с адреса, которого нет в его белом списке. |
403 |
MERCHANT_BLOCKED |
Мерчант заблокирован. |
403 |
PERMISSION_DENIED |
Ресурс, который вы запросили, принадлежит другому мерчанту. |
Заблокированный мерчант не может и читать
MERCHANT_BLOCKED возникает на этапе аутентификации, ещё до того, как начнёт выполняться
сам маршрут, — а значит, затрагивает вообще все эндпоинты, включая чтение. Если у вас 403
трактуется как «запись отклонена», эту трактовку надо расширить.
Стоит различать 403 PERMISSION_DENIED и 404. Инвойс, который существует, но принадлежит
другому мерчанту, вернёт 403, а не 404. Запрос идентификатора, которого нет вообще нигде, — это
404.