Кабинет магазина и API v1

Пользователи магазина

Администратор создаёт магазин и пользователя кабинета. Вход /merchant/login. Роли: manager управляет настройками и сотрудниками, finance создаёт счета и расходные заявки, viewer просматривает данные. Идентификатор магазина берётся из сессии или API-ключа, а не из пользовательского поля запроса. Пароль можно сменить из любого личного кабинета; другие сессии отзываются. TOTP реализован для администратора; для кабинета магазина TOTP ещё не добавлен.

На главной кабинета: баланс по направлениям, последние счета, локальные заявки, API-ключи, ссылка на оплату, регулярные счета, webhook, сотрудники и анкета. Ключи выдаются один раз, в БД хранится хеш. Ротация создаёт новый и отзывает старый в одной транзакции. Утраченный ключ не раскрывается повторно.

Общие правила запросов

Базовый путь /api/v1. Передавайте X-API-Key. Создающие запросы требуют Idempotency-Key длиной не более 120 символов. Для каждого нового бизнес-действия создавайте новый случайный ключ. Повтор прежнего ключа с тем же содержимым возвращает прежний результат; другое содержимое вызывает 409. Сохранение результата и денежное действие выполняются в одной транзакции.

Денежные значения отправляются строками, например "125.50", а атомарные суммы — "125500000". JSON float не допускается для сумм. Не смешивайте минимальные единицы разных сетей. Коды и технические API-поля английские, пользовательский интерфейс русский.

Метод Путь Полномочие Назначение
GET /health публичный Состояние процесса и доступность БД; не доказательство готовности сетей
GET /routes routes:read Только локально включённые тестовые направления
POST /invoices invoices:create Создать счёт
GET /invoices/{id} invoices:read Свой счёт и попытки оплаты
GET /balances balances:read Учёт, резервы, удержания, доступный остаток
GET /transactions transactions:read До 200 последних собственных поступлений
GET /payouts payouts:read До 200 последних собственных расходных заявок
POST /payouts payouts:create Локальная заявка с резервированием, не сетевой вывод
POST /refunds refunds:create Локальный возврат по своему счёту
POST /payment-links links:manage Постоянная ссылка фиксированной цены
POST /recurring-plans recurring:manage План выставления новых счетов без автосписания

В списках пока нет cursor-pagination; не стройте на лимите 200 полноценный экспорт всей истории. CSV выгружается из кабинета до 10000 счетов и экранирует формулы электронных таблиц.

Пример создания счёта

{"order_id":"order-1001","amount":"125.50","currency":"USD","description":"Локальный заказ"}

POST /api/v1/invoices, Content-Type: application/json, X-API-Key: ваш_ключ, Idempotency-Key: уникальный_ключ. Ответ содержит data.id, order_id, status, amount, currency, expires_at, payment_url. Сохраняйте идентификатор счёта у заказа. Не признавайте покупку оплаченной по возвращению пользователя на сайт или по нажатию кнопки.

Для выплаты используйте payment_route_id, amount_atomic, fee_limit_atomic, destination_address: TEST_..., reason. Для возврата дополнительно invoice_id — публичный идентификатор счёта. Request_id в API формируется из Idempotency-Key. Сумма зарезервирована, но ещё не списана. Лимит комиссии — упрощённая модель симулятора в том же активе; это не расчёт реального газа.

Для ссылки: title, amount, currency. Для регулярных счетов: customer_reference (внутренний код без лишних персональных данных), amount, currency, interval_days от 1 до 365. Обработчик создаёт отдельный invoice на период. Это не разрешение списывать средства из внешнего кошелька.

Уведомления

Разрешён публичный HTTPS URL без логина/пароля, query и fragment. Loopback, частные и служебные адреса запрещены даже при локальном осмотре: не отключайте защиту ради теста. В среде сборки внешняя доставка не выполнялась.

Тело содержит event_id, event_type, created_at, data. Заголовки: X-CryptoGate-Event, X-CryptoGate-Timestamp, X-CryptoGate-Signature. Подпись — v1= плюс hex HMAC-SHA256 от точных байтов timestamp + "." + raw_body, ключ — сохранённый webhook secret. Сначала проверяйте формат и допустимое расхождение времени, затем constant-time сравнение подписи, только после этого разбирайте JSON. Не сериализуйте JSON заново для проверки подписи.

Принимайте событие в собственную таблицу с уникальным event_id и изменение заказа в одной транзакции. Повтор считается доставленным, но не должен повторно выдавать товар. Необработанные состояния не игнорируйте молча. Возвращайте 2xx после надёжного сохранения. Доставка допускает повторы; после восьми неудач состояние dead, а не бесконечная петля.

В релизе добавлены примеры клиента Python/PHP и проверяющего HMAC кода. Это SDK-примеры, не плагины WooCommerce/других CMS. Конкретные CMS-интеграции ещё не реализованы.