Аутентификация
В InfraIO Pay есть два API-уровня с разными моделями аутентификации. Выбирайте тот, который соответствует вызывающей стороне:
| Уровень | Префикс пути | Аудитория | Аутентификация |
|---|---|---|---|
| Мерчантский B2B | /b2b/v1/* | Ваш сервер | Подпись запросов HMAC-SHA256 |
| Панель управления | По сервисам: /auth/*, /payment/*, /merchant/*, /event/*, /user/*, … | Браузерные сессии для панели мерчанта | Bearer JWT |
Эта страница описывает B2B-уровень — тот, который вы вызываете со своего сервера с парой API-ключей. Если вы встраиваете панель InfraIO или строите внутренние инструменты, используйте уровень панели управления (отдельная документация, пока не опубликована).
Gateway маршрутизирует каждый уровень по ведущему префиксу, который он
срезает перед пересылкой: /b2b/v1/checkout-sessions/quick
достигает payment-service как /v1/checkout-sessions/quick, а
/payment/v1/orders панели достигает его как /v1/orders. Поэтому если
вы видите голые пути /v1/* где-то ещё, это внутренний backend-путь
после удаления публичного префикса — ваш клиент всегда отправляет форму
с префиксом. (Одно следствие для подписания: каноническая строка B2B
подписывает путь вместе с префиксом /b2b — см. ниже.)
Endpoint’ы
| Окружение | Base URL |
|---|---|
| Тестовое | https://api-dev.infraio.xyz |
| Боевое | https://api.infraio.xyz |
Шаблон URL тот же — окружение определяется по префиксу ключа
(pk_test_… или pk_live_…), а не по URL.
Пара ключей
Из панели мерчанта вы получаете два значения (Developers → API keys → + Add key):
- Publishable-ключ (
pk_test_…илиpk_live_…) — идентифицирует ваш аккаунт. Передаётся какX-Client-ID. Безопасно встраивать в ваш браузерный бандл (SDK уже это делает). - Secret-ключ (
sk_test_…илиsk_live_…) — ключ HMAC-подписи. Только серверный. Относитесь к нему как к паролю базы данных.
Если secret-ключ когда-либо попал в браузерный бандл, git-репозиторий, лог-строку или общий чат — немедленно отзовите его из панели. Окна перекрытия нет; отзыв мгновенный. Выпустите новый ключ и переразверните.
Подписание запроса
Каждый вызов /b2b/v1/* несёт три заголовка:
X-Client-ID: pk_live_70d0becbd7a53047b0dfe88a960d10ad
X-Timestamp: 1715990400
X-Signature: 9a8b7c6d… (hex HMAC-SHA256)Подпись вычисляется по канонической строке:
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODYMETHOD— HTTP-глагол в верхнем регистре (POST,GET, …).PATH— путь запроса включая префикс/b2b, без хоста и без query-строки (например,/b2b/v1/checkout-sessions/quick). Gateway проверяет подпись по сырому входящему пути до срезания/b2b, поэтому префикс должен присутствовать. Query-параметры не подписываются — дляGET …?cursor=…&limit=20подписывайте только путь, без части?….TIMESTAMP— unix-секунды как десятичная строка (например,"1715990400"), точно совпадающая сX-Timestamp.BODY— сырые байты тела запроса. Пустая строка дляGET/DELETE.
Подписывайте с помощью HMAC-SHA256 ключом secret, вывод hex:
Node / TS
import { createHmac } from "node:crypto";
function sign({ method, path, body, secret }: {
method: string; path: string; body: string; secret: string;
}) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const input = [method.toUpperCase(), path, timestamp, body].join("\n");
const signature = createHmac("sha256", secret).update(input).digest("hex");
return { timestamp, signature };
}Почему HMAC, а не Bearer?
API на чистых Bearer-токенах отправляет ваш единственный секрет по сети при каждом запросе. Любой, кто получит лог одного прокси с TLS-терминацией, получает ключи от вашего аккаунта. Подпись HMAC означает, что секрет никогда не путешествует — только его производная подпись, одноразовая (привязанная к этому конкретному запросу и к этой конкретной минуте).
Компромисс: подпись нужно вычислять для каждого вызова. Серверный SDK скрыл бы это; пока мы его не выпустим, хелпер выше занимает ~15 строк на каждом языке.
Допуск по времени
Связывающий допуск составляет ±5 минут (300 секунд), его
обеспечивает merchant-service при проверке подписи. Сам gateway чуть
менее строг (310с) как защита в глубину, но запрос, прошедший gateway и
не прошедший внутреннюю проверку, всё равно закончится ошибкой
401 invalid_signature — принимайте 300с за контракт. Из этого следуют
две вещи:
- Синхронизируйте часы сервера через NTP. Долгоживущий cron со смещёнными часами будет давать сбои время от времени.
- Не предвычисляйте и не ставьте подписи в очередь. Если запрос просидит в очереди повторов более 5 минут, его подпись истечёт.
Scope’ы ключей
Secret-ключи несут один или несколько из этих наборов scope’ов:
| Scope | Назначение |
|---|---|
read | Чтение списков и единичных заказов, сессий, возвратов |
write_order | Создание сессий checkout, заказов |
write_refund | Выпуск возвратов, генерация refund-request token’ов |
webhook_manage | Создание/обновление/удаление webhook-endpoint’ов |
По умолчанию панель выдаёт ключ с «полным доступом» (все четыре scope’а). Вы можете выпустить ключ с ограниченным scope’ом из Developers → API keys → + Add key, отметив только те scope’ы, которые нужны интеграции.
Применение scope’ов на данный момент рекомендательное, а не
принудительное. Scope’ы записываются на ключе и показываются вам в
панели, но middleware gateway пока не отклоняет вызовы вне scope’а —
любой валидный ключ sk_… сегодня ведёт себя как ключ с полным
доступом. Применение scope’ов на уровне endpoint’а запланировано на
следующий релиз. Не полагайтесь на scope’ы как на границу безопасности
пока что; считайте их метками и используйте ротацию / отзыв ключей,
чтобы реально ограничивать доступ в данный момент.
Где проверяется подпись
Проверка HMAC происходит один раз, на gateway. Gateway:
- Читает
X-Client-ID,X-Timestamp,X-Signature. - Находит мерчанта и секрет по
pk_…, проверяет временное окно, пересчитывает подпись, сравнивает за константное время. - При успехе срезает auth-заголовки, проставляет внутренние
заголовки (
X-B2B-Auth: 1,X-Merchant-ID,X-Merchant-Domain) и пересылает запрос в downstream-сервис (payment-service, merchant-service и т. д.). Окружение и разрешённые scope’ы сегодня НЕ инжектируются — downstream-код, которому нужно окружение, выводит его из тела запроса / per-merchant конфигурации, а не из заголовков. - При неудаче возвращает 401
INVALID_SIGNATURE, не трогая backend.
Downstream-сервисы не перепроверяют HMAC — они доверяют
инжектированным заголовкам gateway и работают с тем мерчантом, которого
определил gateway. Они не делают scope-gating по endpoint’у тоже:
как отмечено выше, scope ключа не инжектируется, поэтому любой
аутентифицированный sk_… достигает любого endpoint’а своего мерчанта
(применение scope’ов сейчас рекомендательное — см. callout в разделе
Scope’ы ключей.) Это важно в двух аспектах:
- Если вы держите собственный reverse-прокси перед InfraIO Pay, не
срезайте
X-B2B-Auth/X-Merchant-ID(и не подделывайте их — gateway отклоняет входящие запросы, несущие их на публичной кромке). - Пути публичной сети (
/b2b/v1/*) — единственный уровень, на котором выполняется HMAC-шаг. Внутренний gRPC между нашими сервисами использует mTLS — другую модель доверия, которая не принимаетX-Client-ID.
Что дальше
- Ошибки — форма ответа при 4xx/5xx.
- Безопасность → API-ключи — ротация, отзыв, что делать, если секрет утёк.
- Webhooks → Проверка подписи
— использует другую схему HMAC (заголовок
X-Signature: sha256=…, подписываетX-Timestamp + "." + raw_bodyплюс опциональныйX-Signature-Prevв течение 24-часового grace-окна ротации). Не путайте схемы — алгоритм хэширования общий, но подписываемые байты и семейство секретов (whsec_…противsk_…) разные.