Перейти к содержанию

Аутентификация

В каждом запросе к /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 всё же попадается — у инвойса, подписки на вебхук, статического кошелька, — но лишь справочно: там всегда будет ваш собственный идентификатор.

Посмотреть, какому мерчанту принадлежит ключ:

curl https://api.pay.nullswap.com/api/v1/merchant \
  -H "x-api-key: sk_live_..."
{
  "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"
}

Чего ключ не может

Администрирование мерчанта в это API не входит. Участники, API-ключи, белые списки IP и подписки на вебхуки заводятся и меняются вручную в кабинете. Подписки на вебхуки API читать умеет — вместе с секретом, которым вы проверяете подписи, — но создать, изменить или удалить их через 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/merchant/withdrawals
POST /api/v1/aml/check-address

Маршрутов PUT, PATCH и DELETE нет.

Держите ключ на сервере

Ключ работает на предъявителя в самом прямом смысле: кто им владеет, тот может создавать инвойсы, выводить ваши деньги и читать всю историю ваших платежей.

Никогда не передавайте ключ в браузер или мобильное приложение

Обращайтесь к API только со своего бэкенда. Если ключ утёк, перевыпустите его в кабинете: старый перестанет работать уже со следующего запроса, потому что ключи нигде не кешируются.

Белый список IP

Ключ можно дополнительно привязать к списку адресов, с которых разрешены запросы (Настройки → Белый список IP).

  • Пустой белый список фильтрацию не включает — ключ работает откуда угодно.
  • Как только в списке появится хотя бы одна запись, запросы со всех остальных адресов начнут получать 403 CallerIpNotAllowedError, даже если сам ключ полностью корректен.

Сверяемся мы с реальным адресом, с которого установлено TCP-соединение. Заголовок X-Forwarded-For подделывается и потому не учитывается. Если ваш трафик выходит через NAT или egress-прокси, в белый список нужно добавлять адрес этого узла, а не адрес сервера приложения.

Ошибки

Статус message Причина
401 InvalidApiKeyError Заголовка нет, он повреждён, ключ неизвестен или отозван. Различить эти четыре случая по ответу нельзя — так задумано.
403 CallerIpNotAllowedError Ключ верный, но запрос пришёл с адреса, которого нет в его белом списке.
403 MerchantIsBlockedError Мерчант заблокирован.
403 PermissionDeniedError Ресурс, который вы запросили, принадлежит другому мерчанту.

Заблокированный мерчант не может и читать

MerchantIsBlockedError возникает на этапе аутентификации, ещё до того, как начнёт выполняться сам маршрут, — а значит, затрагивает вообще все эндпоинты, включая чтение. Если у вас 403 трактуется как «запись отклонена», эту трактовку надо расширить.

Стоит различать 403 PermissionDeniedError и 404. Инвойс, который существует, но принадлежит другому мерчанту, вернёт 403; идентификатор, которого нет вообще нигде, вернёт 404.