Аутентификация
Вы выпустите ключ доступа и подпишете им запрос с вашего сервера — минут десять, если кабинет уже открыт, и дольше, если ключ придётся просить у владельца проекта.
Перед началом
Ключи лежат в кабинете: Настройки проекта → Подключение приложения → Для разработчика, секция «Ключи доступа». Там же, в поле «Адрес API», лежит основа всех адресов ниже: https://api.subster.ai.
- Выпускать, ротировать и отзывать ключи может только владелец или администратор проекта. Позвали участником — попросите у владельца роль администратора: «Проект и команда», «Передача разработчику».
- Раздел работает на платных тарифах. На бесплатном не выпустить ни ключ, ни секрет подписи.
Шаг 1. Выпустите ключ доступа
Нажмите «Создать ключ», дайте ему имя и выберите режим — боевой (sk_live_…) или тестовый (sk_test_…). Значение показывается один раз: не сохранили — выпускайте новый.
Тестовый ключ делает всё то же, что боевой, кроме двух вещей. Списать и вернуть деньги им нельзя, на такой запрос придёт отказ test_key_not_allowed. Событие, присланное нам с ним, проверочное и в рекламные сети не уходит. Отдельных тестовых данных у него нет — он работает с настоящими покупателями проекта, и отмена подписки тестовым ключом настоящая.
Это ключ Subster, а не Stripe
Начало у них общее, и секрет подписи у нас тоже начинается с whsec_, как у Stripe. Ключи Stripe владелец проекта вводит в другом разделе кабинета, и нашим запросам они не подойдут: берите ключ там, где сказано выше, — в панели Stripe его нет.
Шаг 2. Подпишите запрос ключом
curl "https://api.subster.ai/s2s/v1/entitlement?guid=<GUID>" \
-H "Authorization: Bearer sk_live_…"
<GUID> — идентификатор покупателя: его приложение получает при опознании и передаёт вашему серверу.
Тем же заголовком открываются все серверные запросы — адреса /s2s/v1/*. Номер проекта мы берём из самого ключа, передавать его отдельно не нужно. Держите ключ на сервере: в коде приложения ему не место.
Что может ключ
Каждому запросу нужно своё право ключа. Ключ выпускается со всеми правами, кроме трёх последних в таблице: их открывает владелец проекта переключателями, см. «Деньги из приложения». Переключатель действует сразу на все ключи проекта, в том числе уже выпущенные.
| Право | Запросы | Есть у нового ключа |
|---|---|---|
entitlement:read | GET /s2s/v1/entitlement | да |
users:write | POST /s2s/v1/users | да |
identity:read | GET /s2s/v1/identity/link-profile, POST /s2s/v1/identity/resolve-by-email, POST /public/identity/resolve-by-email с ключом | да |
identity:write | POST /s2s/v1/identity/link-profile | да |
user:read | GET /s2s/v1/user/properties | да |
user:write | POST /s2s/v1/user/properties | да |
events:write | POST /public/event с ключом | да |
subscription:manage | POST /s2s/v1/subscription/cancel, /pause, /resume, /portal | после переключателя |
billing:charge | POST /s2s/v1/subscription/charge, GET /s2s/v1/subscription/charge/status | после переключателя |
billing:refund | POST /s2s/v1/subscription/refund | после переключателя |
Выбрать права при выпуске кабинет не даёт: окно «Создать ключ» спрашивает только имя и режим.
Подпись секретом — для двух публичных адресов
POST /public/event и POST /public/identity/resolve-by-email принимают и ключ, и подпись тела. Начните с ключа — он короче. Подпись берите, если ключ на этом участке держать негде.
С ключом:
- в теле события поле
projectIdвсё равно нужно — без него придёт400; - идентификатор по почте спрашивайте у
POST /s2s/v1/identity/resolve-by-email: ответ там без обёртки, а отказы различимы — «Проверить право доступа», раздел «Если у сервера есть только почта».
Секрет выпускается кнопкой «Выпустить» в строке «Секрет подписи входящих запросов» на той же вкладке и тоже показывается один раз. Держите его на сервере, как ключ: кто знает секрет, подпишет запрос от имени проекта. Прав у подписи нет: таблица выше действует только для запросов с ключом, а секрет открывает оба адреса целиком.
- Возьмите текущее время в миллисекундах — это
timestamp. - Соберите строку
${timestamp}.${rawBody}: тело берётся байт в байт, как уходит в сеть, без повторной сборки JSON. - Посчитайте от неё
HMAC-SHA256секретом проекта, результат переведите в hex. - Отправьте два заголовка:
X-Signature: sha256=<hex>иX-Signature-Timestampс тем же временем.
const ts = String(Date.now());
const sig = crypto.createHmac("sha256", PROJECT_SECRET)
.update(`${ts}.${rawBody}`).digest("hex");
// headers: { "X-Signature": `sha256=${sig}`, "X-Signature-Timestamp": ts }
В подписанном теле должно быть поле projectId — по нему мы выбираем, чьим секретом проверять подпись. Значение лежит в поле «ID проекта» на той же вкладке «Для разработчика». Время сверяется с нашим: разошлись больше чем на пять минут — отказ.
Метка здесь в миллисекундах, а в подписи наших событий к вам — в секундах: код проверки с одной стороны на другую не переносится (Вебхуки).
Что приходит при отказе
Серверные запросы отказывают телом { "error": { "type", "code", "message" } }. Ветвитесь по code: текст message может измениться.
| Ответ | type | Что случилось |
|---|---|---|
401 | authentication_error | ключа нет, он испорчен, отозван или это старый ключ после ротации |
403 | permission_error | операция закрыта — причину называет code, таблица ниже |
400, 409, 422 | invalid_request_error | ошибка в самом запросе, в том числе параметр, которого запрос не знает, или конфликт — например, платёж уже возвращён |
429 | rate_limit_error | запросов больше потолка — таблица ниже |
404 | not_found | ресурса нет либо он принадлежит другому проекту — ответ один на оба случая |
5xx | api_error | сбой на нашей стороне, code тоже api_error — повторите позже |
Что значит code у отказа 403:
code | Причина | Что делать |
|---|---|---|
permission_error | у ключа нет права на эту операцию | сверить с таблицей прав выше |
charge_disabled, refund_disabled, subscription_manage_disabled | владелец проекта не включил переключатель списаний, возвратов или управления подпиской | попросить владельца включить — «Деньги из приложения» |
test_key_not_allowed | списание или возврат тестовым ключом | выпустить боевой ключ |
bridge_tier_expired | платный тариф проекта не оплачен или сменён на бесплатный, и льготный срок кончился | владельцу вернуть платный тариф; ключи перевыпускать не нужно |
bridge_temporarily_unavailable | сбой на нашей стороне | повторить позже, с тарифом всё в порядке |
project_frozen, project_readonly | деньги и управление подпиской остановлены на уровне организации или проекта | разбор в «Диагностике» |
Два публичных адреса выше устроены иначе: неизвестный ключ и несошедшаяся подпись дают там 404 без подробностей. 401 оттуда не приходит никогда.
Сколько запросов можно
Сверх потолка приходит 429. Счётчик у каждого адреса свой: сотня проверок права доступа не отнимает потолок у записи свойств.
Серверные запросы считаются по ключу. Все ваши серверы с одним ключом делят один счётчик, и смена IP-адреса его не обнуляет. Открытые адреса считаются по IP-адресу отправителя, даже если запрос несёт ключ. Исключение — приём событий: он считает события проекта.
| Адрес | Потолок |
|---|---|
каждый адрес /s2s/v1/* | 120 в минуту на ключ |
GET /public/entitlement | 60 в минуту с IP-адреса |
POST /public/event | 300 событий в минуту на проект; с одного IP-адреса — до 3000 любых запросов и до 60 не прошедших проверку ключа или подписи |
POST /public/identity/resolve-by-email | 60 в минуту с IP-адреса |
POST /public/handoff/app-callback | 60 в минуту с IP-адреса |
GET /public/handoff/resolve | 10 в минуту с IP-адреса |
POST /public/handoff/resolve-by-fingerprint | 10 в минуту с IP-адреса |
POST /public/handoff/email-recovery/request | 5 за 15 минут с IP-адреса; письмо на одну почту — не чаще раза в 20 минут |
POST /public/handoff/portal-entry/request | 5 за 15 минут с IP-адреса; письмо на одну почту — не чаще раза в 20 минут |
Ротация и отзыв
Для ключа доступа оба действия лежат в меню «⋯» в конце его строки. «Ротировать» выпускает новый ключ, а старый принимается ещё 72 часа — успеете выкатить сервер без простоя. Потолок частоты у нового ключа свой, со старым он не общий. «Отозвать» гасит ключ сразу и навсегда. У старого ключа с меткой «дожитие до …» в меню остаётся одно «Отозвать сейчас».
Секрет подписи меняется кнопкой «Ротировать» в его строке. Старый секрет принимается ещё 48 часов.
Дальше
- Проверить право доступа — первый запрос под этим ключом и разбор ответа по полям.
- Деньги из приложения — списание, возврат и управление подпиской: три права, которых у ключа нет по умолчанию.
- Диагностика — ключ верный, а сервер всё равно отвечает отказом.