Документація API
Усе, що потрібно, щоб приймати криптоплатежі через Moonvex: від першого ключа до виплат. Базова адреса API — https://api.moonvex.org, формат — JSON, кодування — UTF-8.
Як це працює
- Ваш сервер створює платіж: номер замовлення, сума, монета й мережа. У відповідь — адреса для оплати та
paymentUrl, готова сторінка оплати. - Клієнт платить зі свого гаманця: на сторінці оплати є QR-код, адреса, точна сума й офіційний контракт токена.
- Moonvex стежить за мережею: помічає переказ, чекає потрібної кількості підтверджень і звіряє транзакцію у двох незалежних вузлів.
- Ви отримуєте webhook
payment.confirmed— можна видавати товар. На баланс проєкту зараховується ваша сума: комісію Moonvex клієнт сплачує зверху. - Баланс виводиться виплатою на вашу адресу — з кабінету або через API.
Швидкий старт
- Зареєструйтеся в кабінеті, підтвердьте email і ввімкніть двофакторну автентифікацію.
- «Інтеграція» → «Створити ключ» → тип «Секретний». Ключ показується один раз — збережіть його на сервері у змінній
MOONVEX_SECRET_KEY. - Там само додайте webhook: адресу на вашому сервері (лише https). Збережіть секрет підпису в
MOONVEX_WEBHOOK_SECRET. - Створіть платіж і перенаправте клієнта на
paymentUrl:
curl https://api.moonvex.org/v1/payments \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" \
-H "Idempotency-Key: order-1001-attempt-1" \
-H "Content-Type: application/json" \
-d '{
"orderId": "order-1001",
"amount": "25",
"currency": "USDT",
"network": "TRON",
"customerId": "user-42",
"description": "Замовлення №1001",
"expiresInMinutes": 30,
"metadata": { "cart": "A17" }
}'- Прийміть webhook
payment.confirmed, перевірте підпис (розділ Webhooks) і позначте замовлення оплаченим. - Перед видачею дорогого товару можна повторно перевірити статус запитом
GET /v1/payments/:id.
SDK для Node.js
Офіційний пакет @moonvex/node: усі методи API, типи TypeScript, перевірка підпису webhooks, повторні спроби під час збоїв мережі з тим самим Idempotency-Key. Без залежностей, Node.js 18+.
npm install @moonvex/node
import { Moonvex, verifyWebhook } from '@moonvex/node';
const moonvex = new Moonvex({ apiKey: process.env.MOONVEX_SECRET_KEY });
// платіж
const payment = await moonvex.payments.create(
{ orderId: 'order-1001', amount: '25', currency: 'USDT', network: 'TRON' },
{ idempotencyKey: 'order-1001-attempt-1' },
);
res.redirect(payment.paymentUrl);
// webhook (express.raw: підпис обчислюється за сирим тілом)
app.post('/moonvex/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const event = verifyWebhook(req.body, req.get('Moonvex-Signature'), process.env.MOONVEX_WEBHOOK_SECRET);
if (event.type === 'payment.confirmed') markOrderPaid(event.data.orderId);
res.sendStatus(200);
});Методи: payments.create / get / quote / list / listAll / cancel, depositAddresses.create / list, payouts.create / get / quote / cancel, balances.list, rates.get, plans.upsert, subscriptions.create / get / cancel, webhooks.endpoints.*, webhooks.deliveries.*, verifyWebhook. Помилки — MoonvexError з полями status, code, detailsCode, requestId.
Ключі API
Кожен запит передає ключ у заголовку:
Authorization: Bearer sk_live_ruahuFSGZyeu4rI5_…
| Тип | Починається з | Для чого |
|---|---|---|
| Секретний | sk_live_ | Сервер: створення платежів, виплати, webhooks — усе відповідно до наданих прав. |
| Публічний | pk_live_ | Лише читання (права на запис йому надати не можна). Бачить усі платежі проєкту, тому його теж краще тримати на сервері. |
Права ключа
Під час створення ключа виберіть права (якщо нічого не вибрати — усі). Запит без потрібного права отримує 403 FORBIDDEN.
| Право | Що дозволяє |
|---|---|
payments:read | читати платежі й підписки |
payments:write | створювати та скасовувати платежі, плани й підписки |
payouts:read | читати виплати |
payouts:write | створювати та скасовувати виплати |
balances:read | читати баланси |
webhooks:manage | керувати адресами webhooks і журналом доставок |
wallets:write | видавати клієнтам постійні депозитні адреси |
- Дозволені IP. Можна обмежити ключ списком IP або підмереж — запити з інших адрес буде відхилено.
- Ключ показується один раз. Загубили — створіть новий і відкличте старий у кабінеті; відкликання діє одразу.
- Ніколи не розміщуйте секретний ключ у коді сайту, мобільного застосунку чи в репозиторії.
Ідемпотентність
Створення платежу, виплати й підписки потребує заголовка Idempotency-Key — унікального ключа операції (8–128 символів: літери, цифри, _ - : .). Він захищає від подвійних платежів і виплат під час повторення запиту після тайм-ауту.
- Повторний запит із тим самим ключем і тим самим тілом поверне ту саму відповідь і заголовок
Idempotent-Replayed: true— другий платіж не буде створено. - Той самий ключ з іншим тілом —
409з кодомIDEMPOTENCY_KEY_REUSED. - Ключі зберігаються 30 днів.
order-1001-attempt-1. Тоді повторний запит після збою мережі гарантовано не створить другий рахунок.Суми, монети та мережі
- Усі суми — рядки з десятковою крапкою:
"25","0.00125". Не передавайте числа з рухомою комою. - У відповідях суми надходять з усіма знаками монети: USDT у TRON — 6 знаків (
25.000000), BTC — 8. currency— у чому виражено суму: монета (USDT,BTC…) абоUSD. ДляUSDобов’язковийasset— монета оплати; курс фіксується під час створення рахунку й відображається вpricing.rate.- Ціна в одній криптовалюті з оплатою в іншій не підтримується.
- USDT і USDC — завжди цілі центи. Сума до оплати округлюється вгору до 0.01 (наприклад,
26.05, а не26.041667). BTC, ETH, BNB, SOL, TON і TRX — з повною точністю монети: 0.01 BTC — надто великий крок. - Комісія зверху.
amountу платежі — підсумок для клієнта, ваша ціна + комісія проєкту. Ваша ціна залишається вpricing.amount, ваш дохід у разі повної оплати — уmerchantAmount. Докладніше — Комісія та підсумкова сума.
Мережі, монети та підтвердження
| network | Мережа | Монети | Підтверджень до зарахування |
|---|---|---|---|
TRON | TRON (TRC-20) | TRX, USDT | 19 |
ETHEREUM | Ethereum (ERC-20) | ETH, USDT, USDC | 64 (фіналізація мережі) |
BSC | BNB Smart Chain (BEP-20) | BNB, USDT, USDC | 15 |
SOLANA | Solana (SPL) | SOL, USDT, USDC | 32 (finalized) |
TON | TON (Jetton) | TON, USDT | 1 (мастерчейн) |
BITCOIN | Bitcoin | BTC | 2 — до $1 000, 3 — до $10 000, 6 — понад цю суму або якщо сума в BTC |
Офіційні контракти токенів
Зараховуються лише токени з цими контрактами. Токен із такою самою назвою, але іншим контрактом — підробка, його не зараховують.
| Монета | Мережа | Контракт |
|---|---|---|
| USDT | TRON | TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t |
| USDT | ETHEREUM | 0xdAC17F958D2ee523a2206206994597C13D831ec7 |
| USDC | ETHEREUM | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 |
| USDT | BSC | 0x55d398326f99059fF775485246999027B3197955 |
| USDC | BSC | 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d |
| USDT | SOLANA | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| USDC | SOLANA | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | TON | EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs |
Платежі
Створити платіж
/v1/paymentsправо ключа: payments:write| Поле | Обов’язкове | Опис |
|---|---|---|
orderId | так | Номер замовлення у вашій системі (до 128 символів: літери, цифри, - _ . : / #). Один активний платіж на замовлення. |
amount | так | Сума рядком: "25", "49.90". |
currency | так | У чому сума: монета або USD. |
network | так | Мережа оплати: TRON, ETHEREUM, BSC, SOLANA, TON, BITCOIN. |
asset | для USD | Монета оплати, якщо сума в USD. |
customerId | ні | Ваш ідентифікатор клієнта — для пошуку та підписок. |
customerEmail | ні | Email клієнта. |
expiresInMinutes | ні | Строк оплати: 5–1440 хвилин, за замовчуванням 30. |
description | ні | До 500 символів. |
metadata | ні | Ваші дані: до 50 ключів, значення — рядок, число, true/false або null. Повертаються в платежі та webhooks. |
Відповідь 201 — об’єкт платежу:
{
"id": "pay_UsLiLbyGM7YUbdy6BsUL5a",
"object": "payment",
"status": "PENDING",
"mode": "INVOICE",
"orderId": "order-1001",
"customerId": "user-42",
"amount": "26.050000",
"merchantAmount": "25.008000",
"asset": "USDT",
"network": "TRON",
"contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"tokenStandard": "TRC-20",
"address": "TCnQo6fMf9LL8bjhiJMPq6DZ38GbAEoeDs",
"paymentUrl": "https://pay.moonvex.org/pay/pay_UsLiLbyGM7YUbdy6BsUL5a",
"pricing": { "currency": "USDT", "amount": "25", "rate": null },
"receivedAmount": "0.000000",
"fee": { "percent": "4", "expectedAmount": "1.042000", "amount": null },
"netAmount": null,
"confirmations": 0,
"requiredConfirmations": 19,
"txHashes": [],
"expiresAt": "2026-11-10T19:30:00.000Z",
"settledAt": null,
"createdAt": "2026-11-10T19:00:00.000Z",
"metadata": { "cart": "A17" }
}| Поле | Що означає |
|---|---|
id | Ідентифікатор платежу (pay_…). Збережіть його. |
address | Адреса для оплати. Окрема для кожного платежу. |
paymentUrl | Готова сторінка оплати для клієнта. |
amount | Разом до оплати для клієнта: ваша сума + комісія вашого проєкту. USDT і USDC — з точністю до центів (округлення вгору). |
merchantAmount | Скільки отримаєте ви, якщо клієнт сплатить повністю. |
receivedAmount | Скільки вже надійшло. |
contract / tokenStandard | Офіційний контракт токена та стандарт (null для нативної монети). |
fee | Комісія Moonvex за ставкою вашого проєкту (у кожного проєкту своя). Її сплачує клієнт зверху. expectedAmount — комісія в разі повної оплати, amount — фактична після оплати. |
netAmount | Скільки зараховано на ваш баланс після комісії. |
confirmations / requiredConfirmations | Підтвердження мережі: зараз / потрібно. |
txHashes | Хеші транзакцій клієнта. |
pricing | Початкова ціна: валюта, сума та курс (для USD). |
Комісія та підсумкова сума
Комісію Moonvex сплачує клієнт зверху. Ставка проєкту застосовується до підсумкової суми, яку сплачує клієнт (комісія = ставка × підсумок), тому підсумок = ціна / (1 − ставка), а не ціна × (1 + ставка): за 4 % клієнт сплачує 26.05 за ціну 25, а не 26.00. Ставка в кожного проєкту своя і фіксується в платежі під час створення.
Порядок розрахунку amount:
- Ціна в монеті. Для
currency: "USD"— за зафіксованим курсом, з округленням угору до найменшої одиниці монети. - Для USDT і USDC — угору до цілих центів.
- Ділимо на (1 − ставка) й округлюємо вгору до найменшої одиниці монети.
- Для USDT і USDC — знову вгору до цілих центів. Це і є
amount.
Під час оплати комісія = amount × ставка з округленням униз, вам — amount мінус комісія. Усі округлення йдуть на вашу користь, тому ви отримуєте не менше за ціну, зазвичай на частки цента більше. Приклад за 4 %: 25 / 0.96 = 26.041666… → 26.05; комісія 26.05 × 0.04 = 1.042; вам 25.008 замість 25 — зайві 0.008 через округлення до центів. Точна сума, яку отримаєте ви, — у merchantAmount.
Дізнатися підсумкову суму заздалегідь (наприклад, щоб показати її в кошику) — без створення платежу:
/v1/payments/quoteправо ключа: payments:read{
"object": "payment_quote",
"asset": "USDT",
"network": "TRON",
"merchantAmount": "25.008000",
"fee": { "percent": "4", "amount": "1.042000" },
"total": "26.050000",
"pricing": { "currency": "USDT", "amount": "25", "rate": null }
}Для ціни в USD курс у відповіді поточний; створений платіж фіксує свій курс.
Сума в USD з оплатою в BTC:
{
"orderId": "order-1002",
"amount": "49.90",
"currency": "USD",
"asset": "BTC",
"network": "BITCOIN"
}Отримати платіж
/v1/payments/:idправо ключа: payments:readСписок платежів
/v1/payments?status=&orderId=&limit=&cursor=право ключа: payments:readСпочатку нові. limit — 1–100 (за замовчуванням 20). Для наступної сторінки передайте nextCursor з відповіді.
curl "https://api.moonvex.org/v1/payments?status=CONFIRMED&limit=50" \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY"
# відповідь: { "data": [ ...платежі... ], "hasMore": true, "nextCursor": "eyJ..." }
# наступна сторінка: ?cursor=eyJ...&status=CONFIRMED&limit=50Скасувати платіж
/v1/payments/:id/cancelправо ключа: payments:writeЛише доки клієнт не почав платити (статуси CREATED і PENDING). Після скасування за тим самим orderId можна створити новий платіж — так само, як після закінчення строку.
Депозитні адреси
Для сервісів, де в клієнта є баланс або рахунок: маркетплейси та сервіси з передоплатою, SaaS і API з оплатою за використання, хостинг і хмари, рекламні кабінети, гаманці й обмінники, ігри та цифрові товари. Кожен ваш клієнт має власну постійну адресу. Клієнт поповнює її будь-коли й на будь-яку суму, без рахунку та строку. Кожне підтверджене надходження зараховується вам окремо, з комісією, і надходить webhook deposit.confirmed з customerId — за ним ви поповнюєте баланс клієнта у своїй системі.
/v1/deposit-addressesправо ключа: wallets:writecurl https://api.moonvex.org/v1/deposit-addresses \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{ "customerId": "user-42", "network": "TRON" }'
# 201 на перший запит, 200 — якщо в клієнта вже є адреса (та сама адреса)
{
"object": "deposit_address",
"customerId": "user-42",
"address": "TJ8nqPbE2s4yLxq6mJ7vYpB9cX3dQkWz1a",
"networks": ["TRON"],
"assets": [
{ "asset": "TRX", "network": "TRON", "contract": null },
{ "asset": "USDT", "network": "TRON", "contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" }
],
"createdAt": "2026-11-11T10:00:00.000Z"
}- Один клієнт — одна адреса в мережі. Повторний запит для того самого
customerIdповертає ту саму адресу (Idempotency-Key не потрібен). У різних клієнтів адреси різні. - Адреса на сімейство мереж. Адреса TRON приймає TRX і USDT; одна адреса EVM приймає монети Ethereum і BNB Smart Chain (у відповіді
networks— обидві мережі). Для Bitcoin, Solana і TON — окремі адреси. - Зараховуються лише монети з
assetsз офіційними контрактами. Підроблені токени та пил не зараховуються. - Підтвердження — як у звичайних платежів; для Bitcoin — 6 (сума поповнення заздалегідь невідома).
- Адреса постійна: показуйте її клієнтові в профілі, нею можна користуватися роками.
Як надходять поповнення
Кожне надходження — окремий об’єкт платежу з mode: "DEPOSIT" (без orderId і строку). Події: deposit.detected — переказ помічено в мережі (можна показати «поповнення в дорозі»), deposit.confirmed — підтверджено й зараховано. Сума — receivedAmount, після комісії — netAmount. Депозит не має заздалегідь відомої суми, тому тут комісія не додається зверху, а утримується з надходження за ставкою проєкту.
// webhook deposit.confirmed: event.data — об'єкт платежу з mode = "DEPOSIT"
{
"id": "01a1c8…",
"type": "deposit.confirmed",
"data": {
"id": "pay_9Qm…", "mode": "DEPOSIT", "status": "CONFIRMED",
"customerId": "user-42", "asset": "USDT", "network": "TRON",
"address": "TJ8nqPbE2s4yLxq6mJ7vYpB9cX3dQkWz1a",
"receivedAmount": "50.000000", "fee": { "percent": "4", "amount": "2.000000" }, "netAmount": "48.000000",
"txHashes": ["7f3a…"], "orderId": null, "...": "…"
}
}
// поповніть баланс клієнта у себе — рівно один раз на event.id:
if (event.type === 'deposit.confirmed') {
await creditCustomerOnce(event.id, event.data.customerId, event.data.asset, event.data.receivedAmount);
}/v1/deposit-addresses?customerId=право ключа: payments:read/v1/payments?customerId=user-42&mode=DEPOSITправо ключа: payments:readСписок адрес клієнта та історія його поповнень.
Статуси платежу
| Статус | Що сталося | Що робити |
|---|---|---|
CREATED | Рахунок створено, чекаємо на переказ. | Показати клієнтові сторінку оплати. |
PENDING | Чекаємо на переказ (з таким статусом платіж повертається одразу після створення). | — |
DETECTED | Переказ помічено в мережі. | Можна показати «оплату отримано, чекаємо на підтвердження». Товар не видавати. |
PARTIALLY_PAID | Надійшла частина суми. | Клієнт може доплатити на ту саму адресу. |
CONFIRMING | Транзакція в блоці, набираються підтвердження. | Чекати. |
CONFIRMED | Оплачено повністю, кошти зараховано. | Видати товар. |
OVERPAID | Надійшло більше за суму (понад допуск). Зараховано фактично отримане. | Видати товар; надлишок — на ваш розсуд. |
UNDERPAID | Вікно доплати (30 хвилин після строку) закрилося, надійшло менше. Зараховано фактично отримане. | Вирішити: видати частину, попросити доплатити окремим рахунком або повернути кошти. |
EXPIRED | Строк минув без оплати. | За потреби створити новий платіж за тим самим orderId. |
CANCELLED | Ви скасували платіж. | — |
FAILED | Транзакція в мережі не вдалася. | — |
REFUNDED | Платіж повернуто. | — |
- Допуск 0,5 %. Сума в межах ±0,5 % від рахунку вважається точною оплатою (гаманці іноді округлюють). Комісія завжди обчислюється від фактично отриманої суми, тому в разі недоплати в межах допуску ви отримаєте трохи менше за
merchantAmount; точна сума — уnetAmount. - Пізня оплата. Кошти, що надійшли після
EXPIRED, автоматично не зараховуються — їх розбирає підтримка Moonvex і зараховує вручну. Встановлюйте строк із запасом, якщо клієнти платять з бірж. - Не та мережа чи монета. Переказ не тією монетою або в іншій мережі на адресу платежу не зараховується автоматично й теж передається на ручний розгляд.
- Відкат блоків. Якщо транзакція зникла з мережі до фіналізації, платіж повернеться до очікування. Тому видавайте товар лише за
CONFIRMED/OVERPAID, а не заDETECTED.
Сторінка оплати
paymentUrl (https://pay.moonvex.org/pay/pay_…) — готова сторінка: сума, адреса, QR-код, офіційний контракт токена, таймер, кроки підтвердження. Статус оновлюється автоматично. Відкрийте її в новій вкладці або надішліть клієнтові посилання (у месенджері, електронною поштою). Вбудовувати її у фрейм на своєму сайті не можна — сторінка це забороняє з міркувань безпеки.
Якщо хочете власний дизайн оплати, є публічні дані без ключа (лише те, що бачить клієнт):
/v1/public/checkout/:id/v1/public/checkout/:id/eventsПерший повертає суму, монету, мережу, адресу, контракт, статус, підтвердження та строк; другий — потік оновлень статусу (Server-Sent Events). Ліміт — 60 запитів на хвилину з однієї IP-адреси.
GET /v1/payments/:id.Webhooks
Moonvex надсилає POST з JSON на вашу адресу під час кожної зміни платежу, виплати чи підписки. Адресу задають у кабінеті («Інтеграція» → «Webhooks») або через API; лише https.
POST /moonvex/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Moonvex-Webhooks/1.0
Moonvex-Event-Id: 01a1c34d-0e9f-721e-8579-db6f436c4a38
Moonvex-Delivery-Id: 01a1c34d-153c-7989-8fdf-517de0d81590
Moonvex-Signature: t=1794279267,v1=5f2b…c81e
{
"id": "01a1c34d-0e9f-721e-8579-db6f436c4a38",
"type": "payment.confirmed",
"apiVersion": "2026-11-01",
"createdAt": "2026-11-10T19:04:12.000Z",
"data": { "id": "pay_UsLiLbyGM7YUbdy6BsUL5a", "status": "CONFIRMED", "orderId": "order-1001", "...": "увесь об'єкт платежу" }
}id— ідентифікатор події. Під час повторної доставки він той самий: обробляйте кожну подію один раз.data— об’єкт повністю: дляpayment.*— платіж (як уGET /v1/payments/:id), дляpayout.*— виплата.Moonvex-Signature— підпис. Завжди перевіряйте його.
Перевірка підпису
Заголовок: t=<unix-час>,v1=<підпис>. Підпис — HMAC-SHA256 від рядка <t>.<сире тіло запиту> із секретом webhook, у hex. Перевіряйте за сирим тілом (до розбору JSON), порівнюйте за сталий час і відхиляйте події, старші за 5 хвилин. Після зміни секрету протягом 24 годин у заголовку два v1 — зі старим і новим секретом; підходить будь-який.
import crypto from 'node:crypto';
import express from 'express';
/** true, якщо підпис правильний і подія не старша за 5 хвилин */
function verifyMoonvex(rawBody, header, secret, toleranceSec = 300) {
const parts = (header || '').split(',').map((p) => p.split('='));
const t = Number((parts.find(([k]) => k === 't') || [])[1]);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac('sha256', secret).update(t + '.').update(rawBody).digest();
return parts
.filter(([k]) => k === 'v1')
.some(([, sig]) => {
const got = Buffer.from(sig, 'hex');
return got.length === expected.length && crypto.timingSafeEqual(got, expected);
});
}
const app = express();
// важливо: підпис обчислюється за «сирим» тілом, тому express.raw, а не express.json
app.post('/moonvex/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
if (!verifyMoonvex(req.body, req.get('Moonvex-Signature'), process.env.MOONVEX_WEBHOOK_SECRET)) {
return res.status(400).send('bad signature');
}
const event = JSON.parse(req.body.toString('utf8'));
if (await alreadyProcessed(event.id)) return res.sendStatus(200); // повторна доставка
if (event.type === 'payment.confirmed') await markOrderPaid(event.data.orderId, event.data);
await rememberProcessed(event.id);
res.sendStatus(200); // відповідайте 2xx протягом 10 секунд
});Відповідь і повторні спроби
- Відповідайте будь-яким кодом
2xxпротягом 10 секунд. Тривалу обробку виконуйте після відповіді (у черзі). - У разі помилки (5xx, 429, тайм-аут, мережа) повторні спроби: через 10 с, 1 хв, 5 хв, 30 хв, 2 год, 6 год, 12 год, 24 год і ще 24 год (±20 %) — близько трьох діб. Відповідь 4xx (крім 408 і 429) повторюється лише 3 рази.
- Якщо адреса довго не відповідає, доставки на неї ненадовго призупиняються, а потім відновлюються.
- Журнал доставок із кодами відповідей і кнопкою «Повторити» — у кабінеті, розділ «Інтеграція». Там само є кнопка «Надіслати тест» — подія
webhook.test.
Події
| Подія | Коли |
|---|---|
payment.created | Платіж створено. |
payment.detected | Переказ помічено в мережі (зокрема частковий). |
payment.confirming | Транзакція в блоці, набираються підтвердження. |
payment.confirmed | Оплачено повністю — видавайте товар. |
payment.overpaid | Сплачено більше за суму. |
payment.underpaid | Вікно доплати закрилося, сплачено менше. |
payment.expired | Строк оплати минув. |
payment.cancelled | Платіж скасовано. |
payment.failed | Транзакція не вдалася. |
deposit.detected | Поповнення постійної адреси помічено в мережі. |
deposit.confirmed | Поповнення підтверджено й зараховано — поповніть баланс клієнта. |
payout.created | Виплату прийнято. |
payout.processing | Виплату перевірено, вона готується до відправлення. |
payout.broadcasted | Транзакцію виплати відправлено в мережу. |
payout.confirmed | Виплату підтверджено мережею. |
payout.failed | Виплата не вдалася або її відхилено — суму повернуто на баланс. |
subscription.created | Підписку створено. |
subscription.renewed | Період підписки оплачено й продовжено. |
subscription.past_due | Період закінчився, але не оплачений: надішліть клієнтові pendingPayment.url. |
subscription.expired | Підписку не оплачено після пільгового періоду. |
subscription.cancelled | Підписку скасовано. |
За замовчуванням webhook отримує всі події; під час створення можна вибрати потрібні.
Виплати
Виплата надсилає кошти з балансу проєкту на будь-яку адресу в підтримуваній мережі.
/v1/payoutsправо ключа: payouts:writecurl https://api.moonvex.org/v1/payouts \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" \
-H "Idempotency-Key: withdraw-7781" \
-H "Content-Type: application/json" \
-d '{
"asset": "USDT",
"network": "TRON",
"address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"amount": "150",
"externalId": "withdraw-7781"
}'| Поле | Опис |
|---|---|
asset | Монета. |
network | Мережа. |
address | Адреса одержувача. Перевіряється на відповідність мережі; адресу контракту токена відхиляють. |
amount | З feeMode add (за замовчуванням) — скільки отримає одержувач; з deduct — скільки рівно спишеться з балансу. |
feeMode | add — комісія зверху суми; deduct — комісія із суми. Необовʼязково, за замовчуванням add. |
externalId | Ваш унікальний ідентифікатор виплати (повтор — 409 DUPLICATE_EXTERNAL_ID). |
metadata | Необов’язково, ваші дані. |
Відповідь 202 — виплату прийнято, вона виконується асинхронно; про перебіг надходять події payout.*.
{
"id": "po_8Fq2mXr0kD1sVb7nLtYw3c",
"object": "payout",
"externalId": "withdraw-7781",
"status": "CREATED",
"asset": "USDT",
"network": "TRON",
"address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
"amount": "150.000000",
"fee": { "percent": "2", "amount": "3.000000" },
"total": "153.000000",
"txHash": null,
"failureReason": null,
"createdAt": "2026-11-10T19:10:00.000Z",
"completedAt": null
}- Комісія за виплату = сума одержувачу × ставка проєкту, з округленням униз до найменшої одиниці монети (на вашу користь). З
feeMode: "add"вона утримується понад суму: з балансу списуєтьсяtotal=amount+fee.amount. ЗfeeMode: "deduct"списується рівно передана сума: одержувач отримує її / (1 + ставка) з округленням униз, комісія — залишок (може бути на 1 найменшу одиницю більшою, щоб списання дорівнювало вашій сумі). Поки виплата триває,totalу «резерві». - Недостатньо коштів на балансі —
409з кодомINSUFFICIENT_BALANCE. - Великі виплати перевіряються вручну (статус
PENDING_APPROVAL) — це може зайняти час. - Мережеву комісію сплачує Moonvex; одержувач отримує рівно
amount.
| Статус | Значення |
|---|---|
CREATED / RISK_CHECK | Прийнято, триває перевірка. |
PENDING_APPROVAL | Очікує на ручне схвалення. |
APPROVED / QUEUED / SIGNING | Готується до відправлення. |
BROADCASTED / CONFIRMING | Відправлено, чекаємо на підтвердження мережі. |
COMPLETED | Виконано. txHash — хеш транзакції. |
FAILED / REJECTED / CANCELLED | Не виконано; суму повернуто на баланс. Причина — у failureReason. |
Розрахунок виплати заздалегідь
Скільки отримає одержувач, яка буде комісія, скільки спишеться і чи вистачить балансу — нічого не створюється:
/v1/payouts/quote?asset=USDT&network=TRON&amount=100&feeMode=deductправо ключа: payouts:read{
"object": "payout_quote",
"asset": "USDT",
"network": "TRON",
"feeMode": "deduct",
"amount": "98.039215",
"fee": { "percent": "2", "amount": "1.960785" },
"total": "100.000000",
"available": "500.000000",
"sufficient": true
}Приклад за ставки 2 % і feeMode: "deduct": зі 100 USDT одержувач отримає 98.039215, комісія 1.960785, з балансу піде рівно 100.
/v1/payouts/:idправо ключа: payouts:read/v1/payouts/:id/cancelправо ключа: payouts:writeСкасувати можна до підписання (статуси до QUEUED включно).
Баланси та курси
/v1/wallets/balancesправо ключа: balances:readavailable — можна виводити; reserved — утримується під виплати, що тривають.
curl https://api.moonvex.org/v1/wallets/balances -H "Authorization: Bearer $MOONVEX_SECRET_KEY"
{
"data": [
{ "asset": "USDT", "network": "TRON", "available": "1240.500000", "reserved": "153.000000" },
{ "asset": "BTC", "network": "BITCOIN", "available": "0.01840000", "reserved": "0.00000000" }
]
}/v1/rates?assets=BTC,ETHКурси до USD (медіана кількох бірж) для BTC, ETH, BNB, TRX, SOL, TON, USDT, USDC — до 8 монет за запит. Потрібен будь-який чинний ключ. Оновлюються приблизно раз на 2 хвилини.
curl "https://api.moonvex.org/v1/rates?assets=BTC,ETH" -H "Authorization: Bearer $MOONVEX_SECRET_KEY"
{
"data": [
{ "asset": "BTC", "quote": "USD", "price": "73189.15", "sources": ["binance", "coinbase", "kraken"] },
{ "asset": "ETH", "quote": "USD", "price": "2026.00", "sources": ["binance", "coinbase", "kraken"] }
],
"ttlSeconds": 120
}Підписки
Регулярні платежі: кожен період — окремий рахунок, який клієнт оплачує за посиланням. Автоматично списати криптовалюту з гаманця клієнта неможливо, тому надсилайте клієнтові посилання pendingPayment.url (електронною поштою або в застосунку).
/v1/plansправо ключа: payments:write/v1/subscriptionsправо ключа: payments:write/v1/subscriptions/:idправо ключа: payments:read/v1/subscriptions/:id/cancelправо ключа: payments:write# 1. План (створюється або оновлюється за code)
curl -X PUT https://api.moonvex.org/v1/plans \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" -H "Content-Type: application/json" \
-d '{ "code": "pro-monthly", "name": "Pro, місяць", "priceAmount": "15", "currency": "USDT",
"asset": "USDT", "network": "TRON", "intervalDays": 30, "trialDays": 0, "graceDays": 3 }'
# 2. Підписка клієнта
curl https://api.moonvex.org/v1/subscriptions \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" -H "Idempotency-Key: sub-user-42-pro" \
-H "Content-Type: application/json" \
-d '{ "planCode": "pro-monthly", "customerId": "user-42" }'
# відповідь
{
"id": "sub_3Hk9…", "object": "subscription", "status": "PAST_DUE", "plan": "pro-monthly",
"customerId": "user-42", "currentPeriodEnd": null,
"pendingPayment": { "id": "pay_…", "url": "https://pay.moonvex.org/pay/pay_…" }
}| Статус | Значення |
|---|---|
TRIAL | Пробний період (trialDays), платити не потрібно. |
ACTIVE | Оплачено до currentPeriodEnd. Рахунок за наступний період виставляється заздалегідь. |
PAST_DUE | Період не оплачено; є pendingPayment для оплати. |
EXPIRED | Не оплачено протягом пільгового періоду (graceDays, за замовчуванням 3 дні). |
CANCELLED | Скасовано вами. |
Webhooks через API
/v1/webhooks/endpointsправо ключа: webhooks:manage/v1/webhooks/endpointsправо ключа: webhooks:manage/v1/webhooks/endpoints/:id/rotate-secretправо ключа: webhooks:manage/v1/webhooks/endpoints/:idправо ключа: webhooks:manage/v1/webhooks/deliveries?limit=50право ключа: webhooks:manage/v1/webhooks/deliveries/:id/replayправо ключа: webhooks:manage# створити адресу для сповіщень (секрет підпису повернеться один раз)
curl https://api.moonvex.org/v1/webhooks/endpoints \
-H "Authorization: Bearer $MOONVEX_SECRET_KEY" -H "Content-Type: application/json" \
-d '{ "url": "https://shop.example.com/moonvex/webhook", "events": ["payment.confirmed", "payment.expired"] }'
# { "id": "…", "url": "…", "events": [...], "secret": "whsec_…" }Секрет підпису повертається лише під час створення та зміни (rotate-secret). Після зміни старий секрет діє ще 24 години — встигніть оновити його у своїй системі.
Помилки
Помилка завжди надходить в одному форматі. requestId надішліть до служби підтримки — за ним можна знайти запит.
{
"error": {
"code": "CONFLICT",
"message": "Order order-1001 already has a payment in progress or is closed",
"requestId": "req_9e6c8eb939aa40b28d689df40f7d51a6",
"details": { "code": "DUPLICATE_ORDER" }
}
}| HTTP | code | Коли |
|---|---|---|
| 400 | VALIDATION_ERROR | Неправильні параметри. Подробиці — у message і details. |
| 401 | UNAUTHENTICATED | Немає ключа, ключ неправильний, відкликаний або прострочений. |
| 403 | FORBIDDEN | Ключ не має потрібного права, IP не дозволено, проєкт неактивний. |
| 404 | NOT_FOUND | Такого платежу/виплати у вашому проєкті немає. |
| 409 | CONFLICT | Конфлікт стану: див. details.code. |
| 429 | RATE_LIMITED | Забагато запитів — повторіть пізніше. |
| 500 | INTERNAL_ERROR | Помилка на нашому боці — повторіть із тим самим Idempotency-Key. |
| details.code | Що означає |
|---|---|
DUPLICATE_ORDER | За цим orderId уже є активний або оплачений платіж. |
IDEMPOTENCY_KEY_REUSED | Idempotency-Key уже використано з іншим тілом. |
ASSET_NETWORK_NOT_ENABLED | Ця монета в цій мережі не приймається. |
SYSTEM_PAUSED | Приймання платежів або виплати тимчасово призупинено. |
INSUFFICIENT_BALANCE | Недостатньо коштів на балансі для виплати з комісією. |
INVALID_ADDRESS | Адреса не підходить для мережі (або це адреса контракту). |
DUPLICATE_EXTERNAL_ID | Виплата з таким externalId уже існує. |
PAYMENT_NOT_FOUND / PAYOUT_NOT_FOUND | Такого об’єкта у вашому проєкті немає. |
Ліміти запитів
| Запити | Ліміт на ключ |
|---|---|
| Читання (GET) | 100 на секунду |
| Створення та скасування платежів, підписок | 20 на секунду |
| Виплати | 2 на секунду |
| Публічні дані сторінки оплати (без ключа) | 60 на хвилину з IP |
У разі перевищення — 429 RATE_LIMITED; повторіть через секунду. Не опитуйте статус платежів у циклі — використовуйте webhooks.
Машинозчитуваний опис API (OpenAPI 3): api.moonvex.org/v1/openapi.json.
Перед запуском
- Секретний ключ — лише на сервері; права — мінімально необхідні; за можливості — обмеження за IP.
- Кожен POST — з Idempotency-Key, побудованим на основі вашого ідентифікатора.
- Підпис webhook перевіряється за сирим тілом; події, старші за 5 хвилин, відхиляються.
- Кожна подія обробляється один раз (за
id); відповідь 2xx — швидко, обробка — у черзі. - Товар видається лише за
payment.confirmed/payment.overpaid(або за статусом з API), а не за поверненням клієнта на сайт. - Для
payment.underpaidє зрозумілий сценарій для клієнта. - Клієнтові показується
amountіз платежу (з комісією), а звірка із замовленням виконується заorderIdіpricing.amount, а не заamount. - Перевірено реальний платіж на невелику суму в кожній мережі, яку ви вмикаєте.