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

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

Этот документ описывает, как сервер мерчанта аутентифицируется в API ключом API, и связанные с ключом лимиты запросов, ротацию и сетевые правила. Прочитайте его до первого запроса.

Ключ API

Каждый запрос к маршрутам /v1 передаёт ключ API мерчанта в заголовке X-Api-Key. Единственное исключение — GET /v1/openapi.json, машиночитаемое описание API: ему ключ не нужен.

curl -s https://api.stons.io/v1/invoices/6f1d3c1e-2f7a-4f0e-9a51-0c8b3e2d7a44 -H "X-Api-Key: $API_KEY"

Ключ принадлежит мерчанту — в кабинете он называется проектом. Первый ключ владелец проекта получает при подаче заявки на проект в кабинете, следующие выпускает в разделе проекта «Ключи API» кнопкой «Выпустить новый ключ». Ключ имеет вид sk_<16 шестнадцатеричных символов>_<43 символа base64url>: первая часть — публичный идентификатор ключа, который кабинет показывает в списке ключей, вторая — секрет.

Сервис хранит не ключ, а хэш argon2id его секрета, и сравнивает хэши за постоянное время. Отсюда два следствия:

  • утерянный ключ нельзя восстановить, только выпустить новый;
  • ключ показывается один раз, в момент выпуска, — сохраните его в менеджер секретов.

Правила обращения с ключом:

  • Ключ предназначен только для запросов сервер-сервер. Он не должен попадать в браузер, мобильное приложение, репозиторий или логи.
  • Ключ видит только данные своего мерчанта: инвойса другого мерчанта для него не существует (404).
  • Ключ работает с момента выпуска. Пока проект не одобрен после проверки, POST /v1/invoices и POST /v1/addresses отвечают 403 merchant_not_approved, а чтение инвойсов работает.
  • Успешную проверку ключа сервис помнит до 60 секунд. Поэтому отозванный ключ и ключ отключённого мерчанта отклоняются не позже чем через 60 секунд после изменения.

Ошибки

Ключ проверяется раньше тела запроса, поэтому запрос с неверным ключом получает 401, даже если и тело неверное.

HTTP error.code Когда
401 unauthorized Заголовка нет, ключ неверного формата, неизвестен, отозван или секрет не совпадает. Ответ не уточняет, что именно
403 merchant_disabled Ключ верный, но мерчант отключён — оператором или владельцем в кабинете
429 rate_limited Превышен лимит запросов — см. лимиты запросов

Пример: запрос без ключа.

curl -s https://api.stons.io/v1/invoices/6f1d3c1e-2f7a-4f0e-9a51-0c8b3e2d7a44

401:

{"error":{"code":"unauthorized","message":"Missing or invalid X-Api-Key header"}}

Лимиты запросов

Запросы к маршрутам /v1 ограничены дважды, в окне 60 секунд: для IP-адреса клиента и для ключа API. Значения лимитов задаёт оператор установки.

  • Лимит по IP проверяется первым, до ключа: в него засчитываются и запросы без ключа или с неверным ключом.
  • Лимит по ключу проверяется после ключа: он считает запросы одного ключа с любых адресов.
  • GET /v1/openapi.json и несуществующие пути не ограничены.

Ответы ограниченных маршрутов содержат заголовки:

Заголовок Смысл
x-ratelimit-limit Сколько запросов разрешено в окне. Запрос, прошедший оба лимита, сообщает лимит по ключу; запрос, отклонённый лимитом, — этот лимит
x-ratelimit-remaining Сколько запросов осталось в окне
x-ratelimit-reset Через сколько секунд окно начнётся заново
retry-after Только в ответе 429: через сколько секунд повторить

Условные чтения

Каждое простое чтение отвечает заголовком ETag — метку именно этого ответа дают GET /v1/assets, GET /v1/invoices, GET /v1/invoices/{id} и GET /v1/deposits/{tx_hash}. Пришлите метку обратно в If-None-Match, и ответ, который не изменился, придёт как 304 Not Modified без тела. Это нужно продукту, который опрашивает инвойс, пока покупатель платит, или проходит ленту изменений между вебхуками.

GET /v1/invoices/6f1d3c1e-2f7a-4f0e-9a51-0c8b3e2d7a44
X-Api-Key: <ваш ключ>
If-None-Match: "LXEWQrcmsEQBYnyp-6wy9chTD7GQPMTbAiWHF5IaSIE"
  • 304 приходит с той же меткой и без тела: то, что у вас есть, актуально. 200 приходит с новой меткой — храните её рядом с ответом и присылайте со следующим запросом.
  • Метка считается по всему телу, поэтому меняется от любого изменения ответа: пришёл перевод, сменился статус, пересчиталась сумма, тот же инвойс запросили вместе с QR-кодом. Это не номер версии инвойса, и у двух разных запросов об одном инвойсе метки разные.
  • Метка непрозрачна: это строка в кавычках, которую надо сохранить и вернуть, читать из неё нечего. If-None-Match: * совпадает с любым ответом, который есть у сервиса.
  • Условный запрос — это запрос: он считается в лимитах выше и стоит сервису той же работы. Экономия на вашей стороне — тело по сети и его разбор.
  • Метку получает только 200. Ошибка метки не получает, и 304 её не заменяет: запрос, который ответил бы 404, отвечает 404.

Настройки проекта

Интеграция настраивает проект сама, своим ключом: указывает, куда присылать вебхуки, куда страница оплаты возвращает покупателя, и какие токены проект принимает. В кабинет заходить для этого не нужно (ADR-0067).

Секрет подписи вебхуков эти маршруты не возвращают. Он показывается один раз, при создании проекта; ключ API — не доступ к секрету.

GET /v1/project

{
  "project_id": "7f2c1b94-3d5a-4e86-9a2f-6c0b1d8e4a37",
  "name": "Shop",
  "address_mode": "per_invoice",
  "webhook_url": "https://shop.example/hooks",
  "return_url": "https://shop.example/thanks",
  "cancel_url": null,
  "review_status": "approved"
}
  • address_mode решает, что этот API принимает: POST /v1/addresses работает только в per_user, а счёт в per_user без external_user_id отклоняется — см. адреса.
  • review_status = pending или rejected означает, что счета и постоянные адреса отвечают 403 merchant_not_approved: проект ещё не одобрен.

PATCH /v1/project

Меняет одно или несколько полей; пустое тело и любое другое поле — 400:

PATCH /v1/project
X-Api-Key: <ваш ключ>
Content-Type: application/json

{ "webhook_url": "https://shop.example/hooks", "return_url": "https://shop.example/thanks" }
  • webhook_url можно задать и заменить, но не удалить: null — 400. События, уже поставленные в очередь, должны куда-то уходить.
  • return_url и cancel_url: адрес задаёт, null очищает, отсутствие поля оставляет как есть. Правила адресов — у страницы оплаты.
  • Режим адресов этим маршрутом не меняется: его разрешает оператор.
  • Ответ — объект проекта после изменения. Изменение попадает в журнал аудита с именем ключа, которым сделано.

GET /v1/project/assets

Какие активы проект принимает, с двумя переключателями — платформы и своим:

{
  "assets": [
    {
      "asset": "USDT_TRON",
      "network": "tron",
      "symbol": "USDT",
      "decimals": 6,
      "min_deposit": "1000000",
      "platform_enabled": true,
      "enabled": true,
      "accepting": true
    }
  ]
}

accepting — можно ли создать счёт в этом активе сейчас: платформа принимает его и проект его не выключил.

PUT /v1/project/assets

Выбирает принимаемые активы. Перечислять можно только те, что сервис принимает к оплате; от одного до пятидесяти, каждый не больше одного раза:

PUT /v1/project/assets
X-Api-Key: <ваш ключ>
Content-Type: application/json

{ "assets": [{ "asset": "USDT_TRON", "enabled": true }, { "asset": "TRX_TRON", "enabled": false }] }

Актив, которого сервис не принимает, — 422 asset_not_supported, и из запроса не записывается ничего. Ответ — весь список после изменения. Переключатель, который ничего не меняет, аудита не создаёт.

Ротация

У мерчанта может быть несколько действующих ключей одновременно, поэтому ключ заменяется без простоя:

  1. Владелец проекта выпускает новый ключ в разделе проекта «Ключи API» в кабинете.
  2. Продукт переключается на новый ключ.
  3. Владелец там же отзывает старый ключ. API отклоняет отозванный ключ не позже чем через 60 секунд после отзыва. Отозванный ключ не восстанавливается.

Сеть

API мерчантов отвечает по адресу https://api.stons.io/v1. В боевой установке перед ним стоит HTTPS-прокси:

  • из интернета доступны только пути, начинающиеся с /v1/; любой другой путь отвечает 404 с пустым телом;
  • запрос по http:// перенаправляется на https:// кодом 308. К моменту перенаправления заголовок с ключом уже ушёл по сети открытым текстом, поэтому в клиенте указывайте адрес с https://.

Маршруты /admin/v1, /cabinet/v1 и /checkout/v1 обслуживают админку сотрудников, приложение кабинета и сервер страницы оплаты STONS. Они не часть интеграции мерчанта и из интернета недоступны.