Кабинет магазина и 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-интеграции ещё не реализованы.