Sign in
Введение/Аутентификация

Аутентификация и подпись запросов

Подписывайте запросы к API с помощью HMAC-SHA256, используя project UUID и API key.

Каждый запрос к API (кроме входящих webhook'ов) должен содержать project UUID и подпись запроса. Подпись подтверждает, что запрос пришёл именно от вас и никто не изменил его по пути.

API-ключи

В 2328.io используется два ключа с одинаковым алгоритмом подписи, но разной областью действия:

КлючИспользуется для
API keyПлатежи, статические кошельки, баланс, курсы валют, а также проверка подписи webhook'ов платежей и статических кошельков
Payout API keyВсе endpoint'ы /v1/payout/*, а также проверка подписи webhook'ов выплат

Оба ключа создаются в настройках проекта на 2328.io. В примерах ниже фигурирует обобщённое «API key» — подставляйте нужный ключ в зависимости от вызываемого endpoint'а.

Никогда не путайте эти два ключа: подпись запроса на выплату обычным API key (или платежа — payout-ключом) приведёт к ошибке подписи.

Обязательные заголовки

ЗаголовокТипОбязательныйОписание
Content-TypestringдаВсегда application/json
projectstringдаUUID вашего проекта
signstringдаПодпись запроса HMAC-SHA256, рассчитанная вашим API key
User-AgentstringдаИдентифицирует ваше приложение (например, MyShop/1.4 (+https://myshop.example)). Запросы без User-Agent могут быть заблокированы.

Как работает подпись

Подпись — это «отпечаток» тела запроса. Она формируется так:

  1. Сериализация тела в JSON (компактный — без лишних пробелов).
  2. Кодирование этого JSON в Base64. Этот шаг нормализует данные между языками — после превращения в обычный ASCII любой язык даст одни и те же байты для HMAC.
  3. Расчёт HMAC-SHA256 от строки Base64 с использованием API key, результат в нижнем регистре в hex.

Для GET и других запросов без тела подписывайте пустую строку вместо JSON.

Подпись пустой строки для конкретного API key всегда одинакова. Если у вас много GET-вызовов, её можно закэшировать.

Реализации

PHP
<?php
function apiSign(array $data, string $apiKey): string {
    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $base64 = base64_encode($json);
    return hash_hmac('sha256', $base64, $apiKey);
}

Запросы без тела (GET)

Для запросов без тела (например, GET /v1/payout/status/{uuid}) подписывайте пустую строку:

Shell
SIGN=$(printf '' | openssl dgst -sha256 -hmac "$API_KEY" -hex | awk '{print $NF}')

Полный пример запроса

Shell
curl -X POST https://api.2328.io/api/v1/payment \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
  -H "project: YOUR_PROJECT_UUID" \
  -H "sign: YOUR_HMAC_SIGNATURE" \
  -d '{"amount":"100.00","currency":"USD","order_id":"ORDER-123"}'

Никогда не раскрывайте API key в клиентском коде. Все подписи должны вычисляться на вашем бэкенде. Утечка API key даёт постороннему полный доступ к вашему мерчант-аккаунту.

Проверка подписи webhook'ов

Когда 2328.io отправляет вам webhook, тот же алгоритм применяется в обратную сторону:

  1. Извлеките поле sign из payload'а.
  2. Сериализуйте оставшиеся поля в JSON (компактный, без пробелов).
  3. Закодируйте результат в Base64.
  4. Рассчитайте HMAC-SHA256 соответствующим ключом.
  5. Сравните результат с полученным sign функцией постоянного времени (hash_equals, crypto.timingSafeEqual, hmac.compare_digest, subtle.ConstantTimeCompare, OpenSSL.fixed_length_secure_compare).

Ключ подписи зависит от источника webhook'а:

WebhookКлюч для проверки
Webhook'и платежей и статических кошельков (/v1/payment, /v1/static-wallet)API key
Webhook'и выплат (/v1/payout)Payout API key

Типовые ошибки проверки. Ваш JSON-энкодер должен выдавать ровно те же байты, что и отправитель — иначе Base64 будет отличаться и подпись не совпадёт.

  • Go: используйте json.NewEncoder с SetEscapeHTML(false). Стандартный json.Marshal экранирует <, >, & как < и ломает подпись.
  • Python: передавайте ensure_ascii=False в json.dumps. Без этого не-ASCII символы (кириллица, китайский и т. д.) экранируются в \uXXXX.
  • Компактный JSON: без пробелов между полями (separators=(",", ":") в Python).
  • Порядок полей (Go): обычный map[string]any рандомизирует ключи при перекодировании. Используйте json.RawMessage, упорядоченную структуру либо удаляйте sign из исходных байтов.

Если проверка всё равно не проходит, запустите apiSign на полезной нагрузке самостоятельно — он должен выдать ту же hex-строку, что и полученный sign.

Валидная подпись не защищает от повторов. Она лишь доказывает, что webhook пришёл от 2328.io — но не мешает злоумышленнику позднее переотправить перехваченный webhook. Всегда проверяйте идемпотентность по uuid (или по txid для статических кошельков) перед зачислением средств. Если подпись отсутствует или неверна — отвечайте HTTP 401.

Полные примеры кода есть на странице Webhook-уведомления. Обработка повторов и правила идемпотентности — в разделе Лучшие практики.