Перейти к содержимому
Контракты

API future-продукта

Предложение REST/OpenAPI для будущего backend. URL и JSON — примеры формата. Права проверяет сервер, повторные операции не создают дублей.
Аутентификация
Bearer-токен; SMS-код одноразовый; лимиты попыток.
Права
RBAC + проверка объекта; отказ без скрытой деградации.
Ошибки
Стабильный код, безопасное сообщение, correlation ID.

Соглашения

Версия
Префикс /api/v1; ломающие изменения — новая версия.
Деньги
Целые минимальные единицы; никаких float.
Идемпотентность
Idempotency-Key для POST, создающих деньги, бронь или команду.
Повтор
Повтор с тем же ключом возвращает прежний результат.
Ошибки
HTTP-код + machine-readable code + безопасное сообщение.
Корреляция
correlationId в ответе и логах, без персональных данных.
Пагинация
Курсоры и стабильная сортировка.
Кэш
Не является источником истины для календаря и денег.

Поток входа

Телефон, код и версия согласия

Условный код подтверждения — 123456.

На узком экране прокручивается по горизонтали
Отрисовываем схему…
Настоящие OTP и токены не должны попадать в логи или клиентское хранилище без защиты.

Операции

Вход и профильКаталог и календарьБрони и платежиКошелёкЗарядкаЧеки и поддержкаАдминистрирование
POST/api/v1/auth/request-code

Запрос SMS-кода

Начать демонстрационный или реальный вход и применить ограничение частоты.

Запрос
{
  "phone": "+79000000000",
  "consentVersion": "demo-consent-v1"
}
Ответ
{
  "requestId": "otp_req_demo_01",
  "expiresInSeconds": 300,
  "attemptsLeft": 3,
  "demo": true,
  "message": "Демо-код 123456."
}
Доступ: Публичный, строгий rate limitИдемпотентность: Повтор в пределах окна не отправляет новый кодОшибки:RATE_LIMITEDVALIDATION_ERRORSMS_UNAVAILABLE
POST/api/v1/auth/verify-code

Проверка кода

Проверить одноразовый код и создать сессию после согласия.

Запрос
{
  "requestId": "otp_req_demo_01",
  "code": "123456",
  "consentVersion": "demo-consent-v1"
}
Ответ
{
  "accessToken": "demo-token-not-secret",
  "user": { "id": "USR-DEMO-01", "name": "Алексей Д." },
  "consent": { "version": "demo-consent-v1", "acceptedAt": "2026-10-24T09:00:00Z" }
}
Доступ: Публичный, rate limit по запросу и номеруИдемпотентность: Код одноразовый; успешная проверка возвращает ту же сессиюОшибки:INVALID_CODERATE_LIMITEDCONSENT_REQUIRED
GET/api/v1/catalog/stations

Список станций и фильтры

Вернуть станции и порты с фильтрами, статусом и возрастом данных.

Запрос
GET /api/v1/catalog/stations?connector=Type%202&powerMin=11&available=true
Ответ
{
  "items": [
    {
      "id": "ST-01",
      "name": "Северный бульвар, 12",
      "isOpen": true,
      "ports": [
        { "id": "PORT-0101", "connector": "Type 2", "powerKw": 11, "status": "available", "lastSeenAt": "2026-10-24T08:58:00Z" }
      ]
    }
  ],
  "meta": { "updatedAt": "2026-10-24T08:58:00Z", "demo": true }
}
Доступ: Публичный или гостевой токенИдемпотентность: Безопасный GET, кэш не является источником истиныОшибки:STATUS_STALERETRY_LATER
GET/api/v1/catalog/ports/{portId}/schedule

Расписание порта

Вернуть доступные интервалы с учётом броней, буфера и технических окон.

Запрос
GET /api/v1/catalog/ports/PORT-0101/schedule?date=2026-10-24&durationMinutes=60
Ответ
{
  "portId": "PORT-0101",
  "slots": [
    { "startAt": "2026-10-24T09:30:00Z", "endAt": "2026-10-24T10:30:00Z", "available": true },
    { "startAt": "2026-10-24T12:00:00Z", "available": false, "reason": "Пересечение с RSV-9001 и буфером 15 минут" }
  ]
}
Доступ: ПубличныйИдемпотентность: Безопасный GETОшибки:NOT_FOUNDSTATUS_STALE
POST/api/v1/holds

Создание hold

Атомарно удержать интервал до подтверждения оплаты.

Запрос
POST /api/v1/holds
Idempotency-Key: hold-demo-018

{
  "portId": "PORT-0101",
  "startAt": "2026-10-24T14:00:00Z",
  "endAt": "2026-10-24T15:00:00Z",
  "kind": "planned"
}
Ответ
{
  "id": "HOLD-2001",
  "status": "active",
  "price": 99,
  "expiresAt": "2026-10-24T09:03:00Z",
  "demo": true
}
Доступ: Bearer-токен водителяИдемпотентность: Обязательный Idempotency-KeyОшибки:SLOT_CONFLICTPORT_UNAVAILABLESTATUS_STALEVALIDATION_ERROR
POST/api/v1/holds/{holdId}/confirm

Подтверждение hold

Подтвердить демонстрационную оплату и создать бронь.

Запрос
POST /api/v1/holds/HOLD-2001/confirm
Idempotency-Key: confirm-demo-018

{
  "paymentMethod": "sbp",
  "demoOutcome": "success"
}
Ответ
{
  "reservation": { "id": "RSV-2001", "status": "confirmed", "price": 99 },
  "payment": { "id": "PAY-4001", "status": "confirmed" },
  "walletEntry": { "id": "WAL-1001", "amount": -99 },
  "demo": true
}
Доступ: Bearer-токен водителяИдемпотентность: Обязательный Idempotency-Key; повтор возвращает ту же броньОшибки:HOLD_EXPIREDPAYMENT_FAILEDINSUFFICIENT_BALANCESLOT_CONFLICT
POST/api/v1/webhooks/payments

Webhook платёжного провайдера

Обработать подписанное событие оплаты идемпотентно.

Запрос
POST /api/v1/webhooks/payments
X-Provider-Signature: demo-signature

{
  "eventId": "evt_demo_401",
  "paymentId": "PAY-4001",
  "amount": 99,
  "status": "confirmed"
}
Ответ
{
  "received": true,
  "deduplicated": false,
  "paymentId": "PAY-4001",
  "walletEntryId": "WAL-1001"
}
Доступ: Подпись провайдераИдемпотентность: Внешний event ID + ключ идемпотентностиОшибки:VALIDATION_ERRORPAYMENT_PENDINGRECONCILIATION_REQUIRED
GET/api/v1/wallet

Баланс и резервы

Вернуть баланс, доступную сумму, резерв и последние проводки.

Запрос
GET /api/v1/wallet
Ответ
{
  "balance": 371,
  "reserved": 50,
  "available": 321,
  "currency": "EcoCoin",
  "entries": [
    { "id": "WAL-0006", "amount": -61, "type": "energy", "relatedId": "SESS-2001" }
  ],
  "demo": true
}
Доступ: Bearer-токен водителяИдемпотентность: Безопасный GETОшибки:AUTH_REQUIREDRECONCILIATION_REQUIRED
POST/api/v1/sessions/start

Запуск сессии

Проверить условия и отправить команду Start в CSMS.

Запрос
POST /api/v1/sessions/start
Idempotency-Key: start-demo-001

{
  "portId": "PORT-0101",
  "vehicleId": "VEH-01",
  "requestedKwh": 10,
  "reservationId": "RSV-1001"
}
Ответ
{
  "sessionId": "SESS-3001",
  "status": "START_REQUESTED",
  "commandId": "CMD-9001",
  "checks": {
    "connector": "ok",
    "calendar": "ok",
    "telemetry": "ok",
    "balance": "ok"
  },
  "demo": true
}
Доступ: Bearer-токен водителяИдемпотентность: Обязательный Idempotency-Key; повтор возвращает существующую командуОшибки:INVALID_CONNECTORSTATUS_STALEINSUFFICIENT_BALANCESESSION_ALREADY_ACTIVEPORT_UNAVAILABLE
GET/api/v1/sessions/{id}

Состояние и телеметрия сессии

Восстановить серверное состояние после фона, перезапуска или потери сети.

Запрос
GET /api/v1/sessions/SESS-3001
Ответ
{
  "id": "SESS-3001",
  "status": "ACTIVE",
  "energyKwh": 3.4,
  "powerKw": 10.8,
  "requestedKwh": 10,
  "lastMeterAt": "2026-10-24T09:36:00Z",
  "stale": false,
  "tariffVersion": "TAR-1",
  "demo": true
}
Доступ: Bearer-токен водителя или оператораИдемпотентность: Безопасный GETОшибки:NOT_FOUNDSTATUS_STALERETRY_LATER
POST/api/v1/sessions/{id}/stop

Остановка сессии

Отправить Stop и дождаться подтверждения станции до финального статуса.

Запрос
POST /api/v1/sessions/SESS-3001/stop
Ответ
{
  "sessionId": "SESS-3001",
  "status": "STOP_REQUESTED",
  "commandId": "CMD-9002",
  "message": "Ожидаем подтверждение прекращения подачи энергии"
}
Доступ: Bearer-токен водителя или оператораИдемпотентность: Повтор возвращает ту же команду остановкиОшибки:COMMAND_PENDINGSTATION_UNREACHABLERECONCILIATION_REQUIRED
GET/api/v1/sessions/{id}/summary

Итог сессии

Вернуть фактический объём, разбивку суммы, возврат резерва и чек.

Запрос
GET /api/v1/sessions/SESS-3001/summary
Ответ
{
  "sessionId": "SESS-3001",
  "energyKwh": 3.4,
  "energyCost": 61,
  "bookingPrice": 49,
  "total": 110,
  "reservedAmount": 100,
  "releasedAmount": 39,
  "receipt": { "id": "RCPT-3004", "status": "ready", "demo": true },
  "status": "FINISHED"
}
Доступ: Bearer-токен водителяИдемпотентность: Безопасный GET; итог не пересчитывается после финализацииОшибки:NOT_FOUNDRECONCILIATION_REQUIRED
POST/api/v1/support/tickets

Создание обращения

Создать тикет с категорией и контекстом станции/брони/сессии.

Запрос
{
  "category": "charges",
  "message": "Чек не сформировался после зарядки.",
  "sessionId": "SESS-2005"
}
Ответ
{
  "id": "TKT-4003",
  "status": "new",
  "context": { "sessionId": "SESS-2005", "receiptId": "RCPT-3003" },
  "demo": true
}
Доступ: Bearer-токен водителяИдемпотентность: Idempotency-Key на формуОшибки:VALIDATION_ERRORNOT_FOUND
PATCH/api/v1/admin/ports/{id}

Статус порта

Технически заблокировать или вернуть порт с обязательной причиной.

Запрос
PATCH /api/v1/admin/ports/PORT-0101

{
  "status": "maintenance",
  "reason": "Проверка кабеля",
  "expectedVersion": 4
}
Ответ
{
  "portId": "PORT-0101",
  "status": "maintenance",
  "affectedReservations": 2,
  "incidentId": "INC-5003",
  "auditId": "AUD-6005"
}
Доступ: Роль operator/admin, проверка на сервереИдемпотентность: Повтор с тем же значением не создаёт вторую запись аудитаОшибки:FORBIDDENVALIDATION_ERRORNOT_FOUND
POST/api/v1/admin/tariffs

Будущая версия тарифа

Создать будущий тариф, не меняя активные и исторические снимки.

Запрос
{
  "energyPrice": 19.5,
  "shortBookingPrice": 55,
  "plannedBookingPrice": 109,
  "effectiveFrom": "2026-11-01T00:00:00Z",
  "reason": "Плановое изменение тарифа"
}
Ответ
{
  "tariffVersion": "TAR-3",
  "status": "future",
  "auditId": "AUD-6006",
  "activeSessionsUnchanged": true
}
Доступ: Роль adminИдемпотентность: Idempotency-Key на создание версииОшибки:FORBIDDENVALIDATION_ERROR

Коды ошибок

КодHTTPЗначениеДействие клиента
AUTH_REQUIRED401Действие требует входа.Открыть приложениенстрационный вход.
INVALID_CODE400Неверный или просроченный код.Запросить новый код или проверить ввод.
RATE_LIMITED429Слишком много попыток.Подождать и повторить позже.
CONSENT_REQUIRED403Не принята актуальная версия согласия.Показать согласие.
PORT_UNAVAILABLE409Порт занят, отключён или недоступен.Выбрать другой порт/интервал.
STATUS_STALE409Данные устарели для опасной операции.Обновить статус или обратиться в поддержку.
SLOT_CONFLICT409Интервал пересекается с бронью или буфером.Выбрать другое время.
HOLD_EXPIRED410Срок удержания истёк.Создать новое удержание.
PAYMENT_PENDING202Платёж ещё не подтверждён.Проверить статус позже.
PAYMENT_FAILED402Демонстрационный/реальный платёж отклонён.Повторить или выбрать другой способ.
INSUFFICIENT_BALANCE409Не хватает доступного баланса.Пополнить кошелёк.
STATION_UNREACHABLE503Станция или CSMS не отвечает.Повторить позже, не считать операцию успешной.
COMMAND_PENDING202Команда отправлена, результат неизвестен.Проверить статус, не отправлять новую команду.
SESSION_ALREADY_ACTIVE409У пользователя/порта уже есть сессия.Открыть активную сессию.
INVALID_CONNECTOR422Разъём несовместим с автомобилем.Выбрать совместимый порт.
RECONCILIATION_REQUIRED409Данные требуют сверки; автоматический итог не подставляется.Открыть инцидент/поддержку.
FORBIDDEN403Роль не имеет прав на действие.Сменить демонстрационную роль или обратиться к администратору.
VALIDATION_ERROR422Некорректные входные данные.Показать ошибку у поля.
NOT_FOUND404Объект не найден.Вернуться к списку.
RETRY_LATER503Временная недоступность.Повторить безопасно.
Неизвестный результат команды
Если Start/Stop завершился тайм-аутом, клиент не должен отправлять новую команду под новым ключом. Сначала проверяется состояние команды/сессии. Повтор без проверки может привести к двум командам или неверному статусу.