Обзор API
MARV предоставляет V2 как основную клиентскую API; V1 поддерживается для обратной совместимости. ACP - административный профиль; EXT - внешние вебхуки мерчантов.
Авторизация (V2)
V2 использует session-based авторизацию через JWT.
1. Login — клиент отправляет POST /v2/login с платформенными данными в JSON body:
{
"api_type": 30,
"api_uid": "12345",
"api_token": "platform-signed-token",
"platform_data": "vk_user_id=12345&sign=abc...",
"client_version": "1.5.0",
"language": "ru"
}Поле platform_data — raw JSON с платформ-специфичными данными для верификации. Содержимое зависит от api_type:
| Платформа | platform_data | Описание |
|---|---|---|
| VK | "vk_user_id=123&sign=abc..." | URL-encoded launch params (HMAC-SHA256) |
| OK | "session_key" | Ключ сессии для MD5-верификации |
| DR | "session_key" | Ключ сессии для MD5-верификации |
| MS Start | "base64-payload" | Base64-encoded JSON с RSA подписью |
| GamePush | "base64-query" | Base64-encoded query params |
| Другие | — | Не требуется (можно не передавать) |
Сервер проверяет платформенный токен, создаёт/получает пользователя и возвращает JWT вместе с профилем:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1709136000,
"user": { "id": 1, "api_uid": "12345", "data": { ... } }
}Объект user содержит полный профиль (с data), что избавляет от отдельного запроса к /v2/users/me при старте сессии.
2. Запросы — все остальные V2 эндпоинты требуют JWT в заголовке:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...Платформенные заголовки (X-Auth-Token, X-Auth-Type, X-Auth-Uid) для V2 не нужны. Без валидного Bearer-токена запрос будет отклонён с 401.
3. Single session — каждый вызов /v2/login выдаёт новый токен с уникальным jti (JWT ID) и сохраняет его на сервере. Предыдущий токен того же пользователя автоматически инвалидируется: запросы со старым jti получат 401. Один пользователь = одна активная сессия.
4. Истечение — токен действует 24ч (настраивается через app.security.jwt.ttl). При истечении сервер вернёт 401; клиент должен повторить login.
JWT подписан HMAC-SHA256 ключом app.security.jwt.secret. Этот ключ можно использовать в других сервисах для верификации токена. Payload содержит: uid (user_id), atp (api_type), aui (api_uid), jti (session id), exp, iat.
Авторизация (V1)
V1 использует per-request платформенную авторизацию через заголовки:
| Заголовок | Обязателен | Описание |
|---|---|---|
X-Auth-Token | да | Токен авторизации: по умолчанию MD5(secret_apiType_apiUID) |
X-Auth-Type | да | Тип платформы (целое), выбирает конфигурацию платформы |
X-Auth-Uid | да | UID пользователя на платформе |
X-Session-Token | нет | Сессионный токен (только при single_mode.enabled) |
X-Client-Version | нет | Версия клиента; валидируется согласно конфигу |
X-Client-Language-Code | нет | Код языка клиента; сохраняется в профиле пользователя |
X-Session | нет | Сессия платформы (используется некоторыми платформами для верификации) |
X-Access-Token | нет | Access-токен платформы |
X-Inviter-Uid | нет | UID пригласившего пользователя (для реферальной системы) |
Single session mode (deprecated): при app.session.single_mode.enabled: true и немобильной платформе, при первом вызове /v1/users/fetch без X-Session-Token сервер генерирует токен, сохраняет его в БД и возвращает в заголовке X-Set-Session-Token. Клиент должен передавать этот токен в последующих запросах через X-Session-Token. При blocking: true запросы с устаревшим или отсутствующим токеном отклоняются (код 8 или 11).
Request ID и страна клиента
- Клиент может (но не обязан) передавать
X-Request-Id. Если заголовок не задан, MARV сгенерирует UUID самостоятельно. - Каждый ответ содержит
X-Request-Id— его можно использовать для сопоставления с логами (trace_idдобавляется вzerologконтекст). - При работе через Cloudflare заголовок
CF-IPCountryпроксируется в ответ какX-Client-Country-Code, поэтому страна клиента доступна даже без отдельного API. - В ответ также добавляется
X-Server-Id: это идентификатор узла (app.server.idиз конфига либо автогенерированное значение), что упрощает отладку и мониторинг лидерства.
Верификация IAP
ApiType/ApiUidвсегда берутся из аутентифицированного пользователя; значения в теле запроса игнорируются.- Поле
user_idопционально (передаётся внутриstore_payload). Если его нет, MARV подставляет ApiUid текущего пользователя. Указывайтеuser_idявно только на платформах, где IAP использует другой идентификатор (например, Discord). - V2 передаёт store-specific данные через
store_payload(JSON object). V1 использует flat-поля для обратной совместимости. - Необязательное поле
merchant_profileпозволяет выбрать профиль мерчанта (по умолчаниюdefault).
Пример V2 запроса POST /v2/transactions/iap/verify:
{
"product_id": 10,
"price": 9.99,
"currency": "USD",
"store_payload": {
"transaction_id": "receipt-123",
"purchase_token": "purchase-token-abc",
"package_name": "com.example.app"
}
}Ответ включает success, order_id, product_sku, transaction_id (внутренний ID) и duplicate (true если транзакция уже существовала).
Поля store_payload по платформам
Все поля ниже передаются внутри store_payload. Основные поля запроса (product_id, price, currency) обязательны для всех платформ.
| Платформа | package_name | product_sku | purchase_token | receipt | transaction_id | user_id |
|---|---|---|---|---|---|---|
| Google Play | ✅ | ✅ | ✅ | — | — | — |
| App Store (iOS) | — | — | — | ✅ | ✅ | — |
| RuStore | ✅ | ✅ | ✅ | — | — | — |
| Amazon | — | — | — | ✅ | — | ✅ |
| Galaxy Store | — | — | ✅ | — | — | — |
| Bazaar | ✅ | ✅ | ✅ | — | — | — |
| Myket | ✅ | ✅ | ✅ | — | — | — |
| Discord | — | — | — | — | ✅ | опц. |
| — | — | — | ✅ | — | — | |
| Jest | — | — | ✅ | — | опц. | — |
| Yandex | — | — | — | — | — | — |
✅ — обязательное; опц. — опциональное; — — не используется.
Yandex
IAP-верификация на платформе Yandex не поддерживается. Запрос вернёт ошибку.
Примеры store_payload по платформам
Google Play / RuStore / Bazaar / Myket:
{
"package_name": "com.example.game",
"product_sku": "gem_pack_100",
"purchase_token": "opaque-purchase-token"
}App Store (iOS):
{
"receipt": "MIIT...base64-encoded-receipt-data",
"transaction_id": "1000000123456789"
}Amazon:
{
"receipt": "receipt-id-from-amazon",
"user_id": "amazon-user-id"
}Galaxy Store:
{
"purchase_token": "payment-id-token"
}Discord:
{
"transaction_id": "entitlement-id",
"user_id": "discord-user-id"
}Facebook:
{
"receipt": "signature.base64payload"
}Jest:
{
"purchase_token": "jwt-signed-token",
"transaction_id": "optional-purchase-id"
}Форматы ответов
- Успех (V2):
{ "time": <unix>, "data": <payload> } - Ошибка (V2/ACP):
{ "time": <unix>, "error": { "code": "...", "http_status": N, "message": "...", "details": { ... }}}
Подробно про ошибки и соответствия доменных ошибок см. в V2 Errors.
Конфигурация и таймауты
- HTTP‑таймауты и бакеты метрик настраиваются в config/main.md, секциях
app.http.timeouts.*иobservability.metrics.http_buckets_ms.
EXT (мерчанты)
- Общие принципы и требования описаны в EXT Guide. Для каждого мерчанта используется собственная авторизация/подпись.
ACP: инициализация и мониторинг
POST /acp/me— основная точка входа ACP. Возвращает текущего ACP-пользователя, привязанные аккаунты, информацию о сервере (версия, аптайм, ОС), список включённых модулей, сконфигурированных платформ и активных маршрутов. Заменяет устаревший/acp/acp-users/me.POST /acp/system/overview— сводная аналитика для дашборда: статистика пользователей (total, today, week, month, DAU, MAU), количество онлайн, сессий за сегодня, состояние кластера (leader), память, Redis и статусы задач.POST /acp/system/metrics— Prometheus-совместимые метрики (HTTP, DB, cache, controllers, tasks). Описание полей и настройки — в Observability.
Реклама (ACP → Ads)
/acp/ads/listвозвращает все объявления иruntime‑снимок контроллерных метрик (количество вызовов, ошибки, p95) по префиксу/acp/ads/*. Эндпоинт read-only, отдельная роль не требуется./acp/ads/create|update|delete|toggleтребуют рольAccessAds(1024). После модификаций ответы всегда содержатcommit: ["ads"]и актуальный списокads, чтобы UI мог обновить состояние без повторного запроса.- DTO и поля объявлений описаны в openapi.yaml (
AcpAd,AcpCreateAdDTO, ...). Поляtitle/description/data— произвольные JSON‑структуры,weightзадаёт приоритет показа.
Alert Center (ACP)
/acp/alerts/list— фильтрует критические уведомления (type, severity, status, tags, диапазон дат). Требует рольAccessAlerts(2048)./acp/alerts/detail— возвращает карточку уведомления и историю доставок (Slack/Telegram webhooks)./acp/alerts/ack— подтверждает обработку, сохраняет комментарий (виден всем операторам).- Конфигурация каналов и шаблонов описана в config/main.md; подробный контракт — в openapi.yaml.
Реклама (V1 → Ads)
POST /v1/ads/fetchвозвращает доступные объявления (только включённые и отфильтрованные по весу). Дополнительно формируетсяvideo_urlпоdomain.stream.subdomainиз конфигурации.POST /v1/ads/impressionиPOST /v1/ads/clickинкрементируют счётчики просмотров/кликов поsid. Эти вызовы не возвращают payload.- Все V1 Ads маршруты используют стандартную платформенную авторизацию (
X-Auth-*). См. структурыV1Ad,V1AdImpressionDTO,V1AdClickDTOв openapi.yaml.
Реклама (V2 → Ads)
POST /v2/adsвозвращает массив объявлений (V2Ad) с теми же полями, что и V1 (включая вычисленныйvideo_url).POST /v2/ads/impressionиPOST /v2/ads/clickпринимают DTO (V2TrackImpression,V2TrackClick) и увеличивают счётчики поsid.- Все вызовы требуют V2 авторизацию (
Authorization: Bearer JWT). Ответы следуют формату{ "time": ..., "data": ... }. Схемы перечислены в openapi.yaml.
Заказы (Orders)
Модуль order реализует маппинг «SKU клиента → транзакция сервера» для IAP-покупок. Клиент создаёт заказ до инициации платежа на платформе, затем резолвит его при получении подтверждения.
Таблица orders
| Поле | Тип | Описание |
|---|---|---|
id | serial PK | — |
api_type | integer | Тип платформы |
api_uid | text | UID пользователя на платформе |
sku | text | Идентификатор продукта |
transaction_id | text, nullable | ID транзакции (заполняется при resolve) |
status | text | pending / completed |
created_at | timestamp | Время создания |
updated_at | timestamp | Время последнего обновления |
V1 API
POST /v1/orders/create— создаёт pending-заказ. Body:{ "sku": "gem_pack_100" }. Возвращает{ "order_id": 42, "sku": "gem_pack_100", "status": "pending" }.POST /v1/orders/resolve— привязываетtransaction_idк существующему заказу. Body:{ "sku": "gem_pack_100", "transaction_id": "txn_abc123" }. Идемпотентен: если заказ с такимsku+transaction_idуже есть — вернёт его.
V2 API
POST /v2/orders/create— аналог V1, V2 формат ответа ({ "time": ..., "data": ... }).POST /v2/orders/resolve— аналог V1, V2 формат ответа.
Cron-задача order_cleanup
Удаляет неразрешённые (pending) заказы старше domain.orders.ttl_days (по умолчанию 7 дней). Настройка:
infrastructure:
scheduler:
tasks:
- name: Order Cleanup
tag: order_cleanup
type: order_cleanup
expression: "0 3 * * *" # каждый день в 3:00
enabled: trueКонфигурация
| Ключ | По умолчанию | Описание |
|---|---|---|
domain.orders.ttl_days | 7 | Сколько дней хранить pending-заказы |
A/B тесты: Platform-Aware Distribution
При включённой опции platform_aware (per-test boolean в таблице experiments), распределение пользователей по экспериментальных группам учитывает api_type. Это гарантирует, что пропорции групп соблюдаются внутри каждой платформы, а не по всей таблице.
В MARV2 (legacy) используется конфиг-флаг experiments.platform_aware_distribution, в MARV (DDD) — колонка platform_aware на каждом тесте.
Rival Query: TABLESAMPLE
Запрос соперников (/v1/rivals/fetch) использует 3-фазный подход для оптимизации:
- TABLESAMPLE SYSTEM_ROWS — блочная выборка O(1), самый быстрый путь
- TABLESAMPLE BERNOULLI(1%) — fallback при разреженных данных
- ORDER BY RANDOM() — last resort для маленьких таблиц
Это заменяет прежний ORDER BY RANDOM(), который на таблицах с миллионами записей вызывал полный SeqScan + сортировку.
Требование: расширение
tsm_system_rowsдолжно быть установлено:CREATE EXTENSION IF NOT EXISTS tsm_system_rows;(миграция включена).
QueryBuilder: Raw SQL
QueryBuilder поддерживает raw SQL через методы Raw() и Scan():
recs := []orm.RivalRecord{}
err := db.Query(ctx).
Raw("SELECT * FROM rivals TABLESAMPLE SYSTEM_ROWS(?) WHERE api_type IN (?) LIMIT ?", 200, apiTypes, limit).
Scan(&recs).ErrorЭти методы доступны через все обёртки: GORM-реализация, metrics-декоратор, transaction.
