Перейти к содержимому
Trade · Автодонат

API для разработчиков

Подключите Millida Trade к своему сайту: приём платежей как через платёжную систему и автоматическая выдача товаров без MC-плагина.

Обновлено 2 сентября 2026 г.

Merchant API позволяет встроить Millida Trade в собственный сайт: принимать оплату (Millida работает как платёжная система) и выдавать товары на сервере без установки плагина — ваш бэкенд сам забирает очередь команд.

База API: https://api.millida.net/v2/merchant. Все суммы — в копейках (1 ₽ = 100 копеек).

Получение API-ключа

Ключ выпускается в личном кабинете магазина: раздел «Интеграции → API-ключи». При создании вы один раз получаете секрет вида mtk_live_… — сохраните его, повторно он не показывается.

Ключ можно ограничить правами (scopes):

  • payments — создание и управление счетами;
  • deliveries — забор и подтверждение очереди выдачи;
  • read — чтение счетов, серверов и товаров.

Пустой набор прав = полный доступ. Ключ можно отозвать или перевыпустить в любой момент.

Аутентификация

Передавайте ключ в заголовке Authorization: Bearer или X-Api-Key:

Authorization: Bearer mtk_live_xxxxxxxxxxxxxxxx

Ключ привязан к одному магазину. Лимит запросов — 240/мин на ключ (создание счёта — 120/мин).

Millida как платёжная система

Сценарий: у вас свой сайт-витрина, оплату проводит Millida. Создаёте счёт, перенаправляете покупателя на paymentUrl, после оплаты получаете вебхук.

Создать счёт на произвольную сумму

POST /v2/merchant/invoices
Content-Type: application/json

{
  "externalId": "order-1042",
  "amountKopecks": 19900,
  "description": "VIP на 30 дней",
  "playerNickname": "Steve",
  "customerEmail": "player@example.com"
}

Ответ:

{
  "id": "clx…",
  "externalId": "order-1042",
  "status": "pending",
  "amountKopecks": 19900,
  "subtotalKopecks": 19900,
  "discountKopecks": 0,
  "promoCode": null,
  "commissionKopecks": 995,
  "netKopecks": 18905,
  "paymentUrl": "https://millida.trade/checkout/clx…",
  "paidAt": null,
  "createdAt": "2026-06-16T10:00:00.000Z"
}

Перенаправьте покупателя на paymentUrl — там он выберет СБП или карту. externalId — ваш идентификатор заказа; повторный запрос с тем же externalId вернёт тот же счёт (защита от дублей).

Что означают суммы:

ПолеСмысл
subtotalKopecksСумма до скидки
discountKopecksСкидка по промокоду
amountKopecksИтог к оплате (subtotal − discount)
commissionKopecksКомиссия платформы
netKopecksВам к зачислению (amount − commission)

Возврат покупателя на ваш сайт (redirect URL)

Передайте successUrl и failureUrl при создании счёта — после оплаты покупатель автоматически вернётся на вашу страницу (кнопка «Вернуться на сайт магазина» + авто-редирект через 6 секунд).

POST /v2/merchant/invoices
{
  "externalId": "order-1044",
  "amountKopecks": 19900,
  "playerNickname": "Steve",
  "successUrl": "https://ваш-сайт/pay/done",
  "failureUrl": "https://ваш-сайт/pay/error"
}

К вашему адресу мы добавим параметры, сохранив ваши собственные:

https://ваш-сайт/pay/done?order=clx…&external_id=order-1044&status=paid
ПараметрЗначение
orderID счёта в Millida
external_idваш externalId (если передавали)
statuspaid или failed

Требования к адресу: абсолютный http/https на публичный домен. Относительные пути, javascript:, data:, логин-пароль в URL и локальные адреса (localhost, 127.0.0.1, *.local) отклоняются с ошибкой 400.

Параметры редиректа — только для UI. Не считайте заказ оплаченным по status=paid из URL: покупатель может открыть такую ссылку сам. Факт оплаты берите из вебхука invoice.paid или GET /v2/merchant/invoices/:id.

Оба адреса возвращаются в ответах по счёту (successUrl, failureUrl) — удобно проверить, что счёт создан с нужным возвратом. Если адреса не переданы, покупатель после оплаты остаётся на странице Millida с кнопкой «В магазин».

Счёт по каталогу магазина

Если товары заведены в магазине Millida, передайте позиции — сумма и выдача рассчитаются автоматически:

POST /v2/merchant/invoices
{
  "externalId": "order-1043",
  "playerNickname": "Steve",
  "items": [{ "productId": "prod_…", "quantity": 1 }]
}

Проверить статус

GET /v2/merchant/invoices/clx…

Статусы: pending, paid, delivered, failed, cancelled, refunded. Список — GET /v2/merchant/invoices?status=paid&limit=50.

Промокоды и реферальная программа

Типовой сценарий: у каждого ютубера/стримера свой промокод. Игрок вводит его при оплате, вы автоматически видите покупки по этому коду и начисляете блогеру процент.

1. Заведите промокоды

Промокоды создаются в личном кабинете магазина — раздел «Акции и промокоды» (см. статью «Промокоды и акции»). Там же задаются процент или фиксированная скидка, срок действия, лимит применений и ограничение на одного игрока.

2. Передайте код при создании счёта

Поле у игрока на вашем сайте, значение — в promoCode:

POST /v2/merchant/invoices
{
  "externalId": "order-1050",
  "amountKopecks": 19900,
  "playerNickname": "Steve",
  "customerEmail": "player@example.com",
  "promoCode": "YOUTUBER10"
}

Скидку считаем мы — по вашим же акциям. Присылать её размер не нужно и нельзя: так покупатель не сможет подделать цену через DevTools.

Ответ уже со скидкой:

{
  "id": "clx…",
  "status": "pending",
  "subtotalKopecks": 19900,
  "discountKopecks": 1990,
  "promoCode": "YOUTUBER10",
  "amountKopecks": 17910,
  "commissionKopecks": 896,
  "netKopecks": 17014,
  "paymentUrl": "https://millida.trade/checkout/clx…"
}

Регистр кода не важен — youtuber10 и YOUTUBER10 это один промокод.

Если код невалиден (не существует, истёк, исчерпан, не набрана минимальная сумма, уже использован этим игроком), придёт 400 с понятной причиной в поле message — покажите её покупателю и дайте оформить заказ без кода.

3. Получите факт оплаты

Промокод и суммы приходят в вебхуке invoice.paid — по нему и начисляйте процент блогеру (см. раздел «Вебхуки» ниже).

4. Сверка и отчёты

Все оплаты по конкретному коду одним запросом — удобно для партнёрской панели и ежемесячной сверки:

GET /v2/merchant/invoices?promoCode=YOUTUBER10&status=paid&limit=100

Ответ — список счетов с subtotalKopecks, discountKopecks, amountKopecks и paidAt по каждому. Постранично — через cursor из поля nextCursor.

Что важно знать

  • Счётчик применений тратится только при оплате. Созданный, но неоплаченный или отменённый счёт лимит промокода не расходует.
  • Скидка 100 % через API недоступна. Счёт на 0 ₽ невозможно оплатить, поэтому подарочные промокоды работают только на витрине Millida. Обычные (процент, фиксированная сумма) — работают.
  • Комиссия считается от суммы после скидки, то есть от того, что реально заплатил покупатель.
  • Для лимитов нужен идентификатор игрока. Если у промокода стоит ограничение «на одного игрока» или «только первая покупка», передавайте playerNickname и/или customerEmail — без них такой код не применится.
  • Скидка на конкретные товары работает только в режиме каталога (items). Для счёта на произвольную сумму (amountKopecks) такой промокод будет отклонён — позиций для проверки нет.

Медиа-партнёры: доли с поддоменов

Сценарий: сайт у вас свой, плагина нет, но выручку надо делить с медиа-партнёрами, за которыми закреплены поддомены магазина. Millida сама посчитает доли и покажет партнёру его продажи — нужно лишь сказать, откуда пришёл покупатель.

1. Заведите поддомены партнёров в кабинете магазина (вкладка «Поддомены») и назначьте партнёру долю. Указывайте точные адреса, которые партнёр реально рекламирует: привязка идёт по точному совпадению, вариант с опечаткой не засчитается.

2. Передайте адрес при создании счёта — в поле subdomainHost:

POST /v2/merchant/invoices
{
  "externalId": "order-1050",
  "amountKopecks": 19900,
  "playerNickname": "Steve",
  "subdomainHost": "ky.example.pro"
}

Что туда класть: поддомен, с которого пришёл покупатель. Обычно это либо хост партнёрской страницы вашего сайта, либо игровой адрес партнёра, сохранённый у покупателя в куке/ссылке.

3. Проверьте результат. Привязанный адрес возвращается в ответе и в вебхуке в поле subdomainHost (null — заказ ничей):

{
  "id": "clx…",
  "status": "pending",
  "amountKopecks": 19900,
  "subdomainHost": "ky.example.pro"
}

Дальше всё автоматически: при оплате доля партнёра начисляется по настройкам в кабинете, партнёр видит свои продажи и начисления у себя, вы — разбивку по поддоменам в статистике магазина.

Что важно знать

  • Если subdomainHost не передан или не совпал, мы пробуем определить партнёра по нику: если игрок недавно заходил на сервер через адрес партнёра, продажа засчитается ему. Не совпало ничего — заказ остаётся без партнёра, вся сумма ваша.
  • Доля берётся из настроек в кабинете, а не из запроса. Передать «сколько платить партнёру» через API нельзя — это защита от подмены на стороне сайта.
  • Партнёр фиксируется при создании счёта. Перевесить уже созданный (тем более оплаченный) счёт на другого партнёра повторным запросом нельзя.

Выдача без плагина

Сценарий: вы не ставите MC-плагин, а сами исполняете команды на сервере. Укажите команды при создании счёта — после оплаты они попадут в очередь, а ваш бэкенд их заберёт.

Команды в счёте

POST /v2/merchant/invoices
{
  "externalId": "order-1044",
  "amountKopecks": 9900,
  "playerNickname": "Steve",
  "delivery": {
    "serverId": "srv_…",
    "commands": ["lp user Steve parent add vip", "say Steve купил VIP"]
  }
}

serverId — идентификатор сервера магазина, см. «Чтение данных магазина».

Забрать очередь

Опрашивайте очередь (например, раз в 3–5 секунд). Выданные команды атомарно резервируются за вами:

GET /v2/merchant/deliveries?limit=20
{
  "deliveries": [
    {
      "id": "del_…",
      "orderId": "clx…",
      "externalId": "order-1044",
      "serverId": "srv_…",
      "playerNickname": "Steve",
      "commands": ["lp user Steve parent add vip", "say Steve купил VIP"],
      "allowOffline": false
    }
  ]
}

Подтвердить выполнение

После исполнения команд на сервере подтвердите доставку:

POST /v2/merchant/deliveries/del_…/ack
{ "success": true }

При ошибке отправьте { "success": false, "message": "player offline" } — доставка вернётся в очередь и придёт снова (до 20 попыток).

Можно приложить журнал выполнения — тогда владелец магазина увидит в истории оплат, что именно ушло на сервер и что сервер ответил:

POST /v2/merchant/deliveries/del_…/ack
{
  "success": true,
  "results": [
    { "command": "p give Steve 50", "response": "Игроку Steve было выдано 50 CP", "success": true }
  ]
}

Ограничения: до 100 команд, command до 512 символов, response до 1024. Поле необязательное — без него всё работает как раньше.

Чтение данных магазина

Сценарий: вы собираете свою витрину и вам нужны сами серверы, товары, цены и описания. Оба метода требуют права read.

Серверы магазина

GET /v2/merchant/servers

Ответ — массив без обёртки:

[
  {
    "id": "s6x79pqfo0swfkbb6simswvp",
    "name": "Анархия",
    "online": true,
    "currentOnline": 42,
    "maxPlayers": 100
  }
]

online — был ли пинг от сервера за последние 60 секунд.

id — это и есть serverId: его вы передаёте в блоке выдачи при создании счёта и по нему раскладываете товары по серверам у себя на сайте.

Тот же идентификатор виден в кабинете: «Серверы» → карточка сервера → блок «ID сервера» с кнопкой «Скопировать ID».

Товары магазина

GET /v2/merchant/products

Ответ — массив без обёртки:

[
  {
    "id": "p8k…",
    "name": "VIP",
    "description": "Короткое описание — подзаголовок карточки",
    "longDescription": "Подробное описание для детальной карточки (markdown)",
    "iconUrl": "https://cdn.millida.net/…/vip.png",
    "priceKopecks": 6900,
    "oldPriceKopecks": 9900,
    "categoryId": "c3v…",
    "categoryTag": "RANK",
    "serverId": "s6x79pqfo0swfkbb6simswvp",
    "customQuantity": false,
    "variants": [
      { "id": "v1…", "label": "30 дней", "priceKopecks": 6900 }
    ]
  }
]

Что важно знать:

  • longDescription — подробное описание товара (то, что вы заполняете в «Товарах»); description — короткий подзаголовок карточки.
  • serverId = null означает, что товар доступен на всех серверах магазина, а не что сервер не задан.
  • oldPriceKopecks заполняется, только если у товара задана зачёркнутая старая цена.
  • Отдаются только видимые неархивные товары.
  • Ответ не кэшируется (Cache-Control: private, no-store) — изменения из кабинета видны следующим же запросом. Если правки доезжают до вашего сайта с задержкой, кэширует ваша сторона (ISR/CDN), а не Millida.

Вебхуки

Укажите webhookUrl при создании ключа — и получайте уведомления о событиях счёта. Вместе с URL выдаётся webhookSecret для проверки подписи.

События: invoice.paid, invoice.failed, invoice.delivered.

Вебхуки приходят только по счетам, созданным через API этим ключом. Покупки, сделанные напрямую на витрине Millida, в ваш вебхук не попадают — если промокоды нужны и там, смотрите статистику акций в личном кабинете магазина.

Повторы

Если ваш сервер ответил не 2xx или не ответил за 5 секунд, мы повторим доставку: через 1 минуту, 5 минут, 15 минут, час, 3 часа, 6 часов, 12 часов и сутки — всего 9 попыток за двое суток. Отвечайте 200 OK сразу, как приняли событие, а тяжёлую работу делайте после ответа: медленный ответ мы считаем неудачей и повторим.

Из-за повторов одно и то же событие может прийти несколько раз — обрабатывайте его идемпотентно по externalId, иначе выдадите товар дважды.

Если сайт лежал дольше двух суток или webhookUrl указывал не туда, событие можно отправить заново — напишите в поддержку с номером заказа. Проверить статус и сумму вы всегда можете сами: GET /v2/merchant/invoices/:id.

POST https://ваш-сайт/webhook
X-Millida-Event: invoice.paid
X-Millida-Signature: sha256=…

{
  "event": "invoice.paid",
  "data": {
    "id": "clx…",
    "externalId": "order-1050",
    "status": "PAID",
    "amountKopecks": 17910,
    "subtotalKopecks": 19900,
    "discountKopecks": 1990,
    "promoCode": "YOUTUBER10",
    "promotionId": "promo_…",
    "subdomainHost": "ky.example.pro",
    "commissionKopecks": 896,
    "netKopecks": 17014,
    "playerNickname": "Steve",
    "paidAt": "2026-07-21T10:01:00.000Z"
  }
}

amountKopecks — итоговая сумма платежа, promoCode — код, применённый к этому заказу (null, если промокода не было), subdomainHost — поддомен медиа-партнёра, к которому привязан заказ (null, если привязки нет). Этой пары достаточно, чтобы начислить блогеру процент: например, 10 % от amountKopecks при promoCode === "YOUTUBER10".

Пример обработчика:

app.post('/webhook', (req, res) => {
  const { event, data } = req.body;
  if (event === 'invoice.paid' && data.promoCode) {
    creditPartner(data.promoCode, Math.round(data.amountKopecks * 0.1));
  }
  res.sendStatus(200);
});

Проверка подписи

Подпись — HMAC-SHA256 от «сырого» тела запроса с вашим webhookSecret. Сравнивайте безопасно (constant-time):

import { createHmac, timingSafeEqual } from 'crypto';

function verify(rawBody, header, secret) {
  const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Безопасность

  • Секрет ключа храните на сервере, не на фронтенде. Скомпрометированный ключ — отзовите и перевыпустите в ЛК.
  • Сумму к зачислению всегда берите из вебхука/GET /invoices/:id, а не из данных, пришедших с клиента.
  • Всегда проверяйте подпись вебхука и идемпотентность по externalId (вебхук может прийти повторно).
  • Комиссия платформы удерживается автоматически; в ответе видны commissionKopecks и netKopecks (сумма к зачислению).