Оформление
Как это устроено
Четыре вещи, которые объясняют поведение API. Разобравшись с ними, остальное читается по таблицам.
Ключ называет компанию
Ключ принадлежит компании, а не человеку. Приложение не увольняется и не уходит в отпуск, поэтому привязывать доступ к сотруднику незачем: смена ответственного у вас не должна рвать работающую интеграцию.
Из ключа мы берём компанию, и по ней идут все выборки. Это и есть то, что не даёт увидеть чужие машины: не проверка где-то в коде, а условие в самом запросе к базе.
Чужой объект отдаётся как несуществующий — 404, а не 403. Это сделано нарочно: ответ «запрещено» на чужом идентификаторе подтверждал бы, что такой объект есть, и перебором можно было бы узнать размер чужой сети.
Права ключа
Два уровня, выбираются при выдаче и потом не меняются.
| Уровень | Что можно |
|---|---|
read | читать всё своё: постаматы, ячейки, состояние, команды |
write | то же плюс открывать ячейки |
Ключ на чтение, посланный открывать дверцу, получит 403 insufficient_scope.
Менять уровень у выданного ключа нельзя: превратить ключ на чтение в ключ с правом открывать дверцы правкой поля значило бы раздать это право незаметно для того, кто ключ выдавал. Нужен другой уровень — выдаётся новый ключ.
Если у вас несколько систем, берите разные ключи: отчётному модулю хватит read, а отозвать один ключ, не трогая остальные, можно только когда они разные.
Команды выполняются не сразу
Постаматы стоят на 4G за сотовым оператором: входящее соединение к ним невозможно, машина сама держит связь с сервером. Поэтому «открыть ячейку» — это не вызов, который возвращает результат, а команда, которая уходит на машину.
ваше приложение → POST …/open → 202 { command_id, status: "sent" }
↓
сервер → команда на постамат → замок сработал
↓
ваше приложение → GET /v1/commands/{id} → { status: "acked" }Отсюда три следствия для вашего кода:
202не означает «открыто». Показывать человеку «дверца открыта» по этому ответу нельзя.- Нужен второй запрос — статус команды.
ackedозначает, что замок сработал. - У команды есть срок. По умолчанию 60 секунд; не ответила машина — статус станет
expired, и команда не выполнится позже. Это защита: дверца, открывшаяся через час в пустом помещении, хуже невыполненной команды.
Постамат не на связи
Если машина молчит, сервер отвечает 409 terminal_offline сразу и команду не ставит в очередь. Поэтому перед открытием проверяйте online и показывайте состояние машины, а не кнопку, которая отдаст ошибку.
Ячейка называется номером
Единственное имя ячейки в API — номер на фасаде, тот, что видит человек у машины. Никаких внутренних адресов плат и каналов в API нет и не будет: это устройство конкретной машины, а оно у разных постаматов разное.
Два состояния ячейки, которые легко перепутать:
| Поле | Значение | Можно ли открыть через API |
|---|---|---|
locked | заблокирована оператором, временно не выдаётся | нет, 409 cell_locked |
service | служебная, никогда не выдаётся: внутри оборудование | нет, 409 cell_service |
Служебную ячейку открывает только наш оператор из админки — в ней вычислитель, блок питания и платы замков.
Частота запросов
| Что | Ограничение |
|---|---|
| запросы на чтение | 600 в минуту на ключ |
| открытие ячеек | 60 в минуту на постамат |
Открытие ограничено строже, и это не перестраховка: шестьдесят команд в минуту одной машине — уже не работа, а зациклившийся код у кого-то в приложении.
При превышении приходит 429 с заголовком Retry-After — в нём число секунд, через которое запрос примут.
Версия и совместимость
Внутри v1 не меняется: адрес метода, тип и смысл существующего поля, машинный код ошибки, формат ключа.
Может появиться в любой момент: новое поле в ответе, новый метод, новое значение в перечислении.
Не падайте на незнакомом поле
Это главное требование к вашему коду. Строгий разбор JSON, который ломается от нового поля в ответе, — причина, по которой чужие интеграции чаще всего перестают работать. Читайте то, что вам нужно, остальное пропускайте.