Sign in
Вступ/Сповіщення Webhook

Webhook-сповіщення

Отримуйте оновлення статусу платежів та виплат у режимі реального часу через webhooks, підписані HMAC.

Система 2328.io надсилає webhook на ваш url_callback щоразу, коли змінюється статус платежу. Це рекомендований спосіб отримувати сповіщення про успішні платежі.

Формат запиту

  • Метод: POST
  • Content-Type: application/json
  • Підпис: поле sign у тілі запиту

Payload

Тіло webhook повторює формат відповіді /v1/payment/info і додатково містить tx_explorer_url та поле sign для перевірки підпису.

Успішний платіж

JSON
{
  "uuid": "db17d490-15b6-47b9-9015-91d1d8b119f2",
  "order_id": "ORDER-12345",
  "amount": "180.00000000",
  "currency": "RUB",
  "url": "https://go.2328.io/db17d490-15b6-47b9-9015-91d1d8b119f2",
  "expires_at": "2026-05-09T16:56:58+03:00",
  "created_at": "2026-05-09T15:56:58+03:00",
  "payer_currency": "TON",
  "payer_amount": "0.95256917",
  "network": "TON",
  "address": "UQA0RevhkCQx-EltyNgPPeG8dqtnCz7ZslOzMdNQlLxVaNBb",
  "payment_status": "paid",
  "txid": "41c2a327323480af8e705d05deb09c238a41779928832abef4bb77c862357b11",
  "tx_explorer_url": "https://tonviewer.com/transaction/41c2a327323480af8e705d05deb09c238a41779928832abef4bb77c862357b11",
  "payment_amount": "0.95256917",
  "merchant_amount": "0.949711462490000000",
  "amount_usd": "2.41324380",
  "exchange_rate": "0.01340691",
  "sign": "6f8c15b6e53b506d5bfa38ed3fb3b50697af73434262153c02e412541372f04d"
}

Скасований / невдалий платіж

Коли платіж не перебуває у термінальному стані paid, поля txid, payment_amount та merchant_amount дорівнюють null:

JSON
{
  "uuid": "48edaf2d-2c49-4638-8f86-88636f661c1f",
  "order_id": "ORDER-12345",
  "amount": "2800.00000000",
  "currency": "RUB",
  "url": "https://go.2328.io/48edaf2d-2c49-4638-8f86-88636f661c1f",
  "expires_at": "2026-05-09T06:19:04+03:00",
  "created_at": "2026-05-09T05:19:04+03:00",
  "payer_currency": "ETH",
  "payer_amount": "0.01620968",
  "network": "ETH-ERC20",
  "address": "0x37c20d6d96d130Bc5B33D832e43b8e16aACe0c59",
  "payment_status": "cancel",
  "txid": null,
  "tx_explorer_url": null,
  "payment_amount": null,
  "merchant_amount": null,
  "amount_usd": "37.53934800",
  "exchange_rate": "0.01340691",
  "sign": "40ce68ad9691ad54e684329d75ab5adaf5b01409a2d18d3e0110b8c1be605342"
}

Опис полів

ПолеТипОпис
uuidstringUUID платежу
order_idstringВаш ID замовлення
amountdecimal (8 dp)Фіатна сума у currency
currencystringФіатна валюта, яку запитав мерчант
urlstringURL хостингового checkout
expires_atstring (ISO 8601)Коли спливає термін платіжної сесії
created_atstring (ISO 8601)Коли було створено платіжну сесію
payer_currencystringКриптовалюта, якою сплачує платник
payer_amountdecimal (8 dp)Очікувана сума криптовалюти
networkstringБлокчейн-мережа
addressstringДепозитна адреса
payment_statusstringОдне з: pending, check, paid, underpaid_check, underpaid, overpaid, cancel, aml_lock (див. References)
txidstring | nullХеш блокчейн-транзакції, присутній лише після підтвердженого платежу
tx_explorer_urlstring | nullПосилання на транзакцію в блокчейн-експлорері. null, якщо txid відсутній або переказ є внутрішнім P2P.
payment_amountdecimal | nullФактично сплачена сума, присутня лише після оплати
merchant_amountdecimal (18 dp) | nullСума, зарахована мерчанту після комісій
amount_usddecimal (8 dp)Сума у USD на момент створення
exchange_ratedecimalВикористаний курс обміну crypto / fiat
signstring (hex)Підпис HMAC-SHA256 payload

Перевірка підпису

Щоб перевірити підпис webhook:

  1. Витягніть поле sign із payload
  2. Видаліть поле sign з об'єкта
  3. Закодуйте інші поля у JSON
  4. Закодуйте JSON у base64
  5. Обчисліть HMAC-SHA256 від base64-рядка, використовуючи свій API_KEY
  6. Порівняйте обчислений підпис із значенням sign, використовуючи порівняння за константний час
PHP
<?php
function verifyWebhookSign(array $data, string $apiKey): bool {
    $receivedSign = $data['sign'] ?? '';
    unset($data['sign']);

    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $base64 = base64_encode($json);
    $calculated = hash_hmac('sha256', $base64, $apiKey);

    return hash_equals($calculated, $receivedSign);
}

$apiKey = 'YOUR_API_KEY';
$payload = json_decode(file_get_contents('php://input'), true);

if (!verifyWebhookSign($payload, $apiKey)) {
    http_response_code(401);
    exit;
}

switch ($payload['payment_status']) {
    case 'paid':
    case 'overpaid':
        // Credit the order — check idempotency by order_id first
        break;
    case 'underpaid_check':
    case 'underpaid':
    case 'cancel':
        break;
}

http_response_code(200);

Завжди перевіряйте підпис перед зарахуванням будь-яких коштів користувачу. Непідписаний або неправильно підписаний webhook може бути підробленим запитом.

Webhooks виплат

Коли status виплати змінюється, система надсилає POST webhook на URL url_callback, переданий під час створення виплати. Якщо url_callback не було надано, для цієї виплати webhooks не надсилаються.

Webhooks виплат мають перевірятися за допомогою вашого Payout API key — а не звичайного API key. Алгоритм підпису ідентичний webhooks платежів (видалити sign, закодувати у JSON, base64, HMAC-SHA256), відрізняється лише ключ.

Payload

JSON
{
  "uuid": "019dff1f-0dbd-7277-8d45-271e7775388f",
  "order_id": "4dfdcc84402b1185b71cbe399321533e",
  "status": "completed",
  "currency": "TRX",
  "network": "TRX-TRC20",
  "amount": "3.00",
  "merchant_amount": "3.00",
  "network_amount": "3.00",
  "amount_usd": "1.04",
  "to_address": "THauRv5tcucQRohXg8NiyGTk16DX1XQG5x",
  "memo": null,
  "txid": "9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
  "tx_explorer_url": "https://tronscan.org/#/transaction/9242e533703704ef3eaba840f70b4a26333e72c943377ee375fea17badb53def",
  "block_number": null,
  "error_type": null,
  "created_at": "2026-05-07T00:08:38+03:00",
  "updated_at": "2026-05-07T00:08:54+03:00",
  "from_currency": "USDT",
  "debited_amount": "1.050735",
  "debited_currency": "USDT",
  "sign": "925ad7bf3d6841864101f7cc2c7e30652e70a06cdb04dbe07a0129480000ce4a"
}

Опис полів

ПолеТипОпис
uuidstringUUID виплати
order_idstringВаш ID ідемпотентності / посилання, якщо ви його надали
statusstringpending, completed, failed, cancelled (див. References)
currencystringВалюта виплати
networkstringБлокчейн-мережа
amountdecimalСума виплати (у currency)
merchant_amountdecimalСума, списана з мерчант-балансу
network_amountdecimalСума, фактично надіслана у блокчейн
amount_usddecimalВартість у USD на момент виплати
to_addressstringБлокчейн-адреса отримувача
memostring | nullMemo / destination tag, якщо використовується
txidstring | nullХеш блокчейн-транзакції, встановлюється для completed
tx_explorer_urlstring | nullПосилання на транзакцію в блокчейн-експлорері. null, якщо txid відсутній або переказ є внутрішнім P2P.
block_numberinteger | nullВисота блока on-chain транзакції
error_typestring | nullПричина, коли status = failed (наприклад, aml_risk, див. References)
created_atstring (ISO 8601)Коли було створено виплату
updated_atstring (ISO 8601)Коли востаннє змінювався статус
from_currencystringВихідний баланс, з якого було списано виплату при використанні автоконвертації (наприклад, USDT для виплати у BTC)
debited_amountdecimalСума, списана з балансу from_currency
debited_currencystringВалюта списання
signstring (hex)Підпис HMAC-SHA256 payload, підписаний за допомогою Payout API key

Найкращі практики

  • Ідемпотентність — завжди перевіряйте, чи вже було оброблено платіж (за order_id або uuid). Webhooks можуть надходити кілька разів.
  • Швидка відповідь — повертайте HTTP 200 якомога швидше. Виносьте важку роботу у фонову чергу.
  • Повторні спроби — якщо система не отримує HTTP 200, webhook повторно надсилається через 2 хвилини. Максимум 5 спроб повторного надсилання.
  • Асинхронна обробка — обробляйте події webhook асинхронно, щоб не блокувати відповідь.
  • Безпека — ЗАВЖДИ перевіряйте підпис sign перед тим, як довіряти payload.

Webhooks можуть надходити не по порядку. Не припускайте, що перший отриманий webhook є фінальним станом — завжди робіть повторний запит через /v1/payment/info (або /v1/payout/status/{uuid}), якщо потрібна впевненість.

Договір на доставку та обробку

Використовуйте наступний порядок всередині вашого webhook-ендпойнта:

  1. Прочитайте тіло запиту, не зберігаючи секрети та повний підпис у логах.
  2. Визначте, чи це подія платежу/статичного гаманця або подія виплати, щоб вибрати правильний API-ключ.
  3. Видаліть sign, відтворіть документовані байти JSON, обчисліть HMAC-SHA256 і порівняйте у постійний час.
  4. Перевірте обов'язкові ідентифікатори, десяткові рядки та значення статусу.
  5. Атомарно вставте запис у папку входящих/idempotency. Якщо він вже існує, поверніть HTTP 200 без повторення побічних ефектів.
  6. Зафіксуйте мутацію замовлення/книги обліку та поставте в чергу некритичні електронні листи, аналітику або сповіщення.
  7. Поверніть HTTP 200 швидко.

Не викликайте повільні сторонні служби під час утримання транзакції ідемпотентності. Таймаут після коміту, але до повернення, може спричинити повторну спробу; дубль має враховувати зафіксований ключ вхідної пошти і стати операцією без змін (no-op).

ПодіяОсновна ідентичністьПримітки
Сесія оплатиuuid + докази статусу/версіїТа сама рахунок-фактура може випускати кілька законних змін статусу.
Часткове поповнення платежурахунок uuid + txidБільше ніж один переказ може належати до одного недостатньо сплаченого рахунку.
Депозит статичного гаманцямережа + txidorder_id використовується повторно для кожного депозиту в цей гаманець.
Виплатавиплата uuid + статусНіколи не створюйте другий платіж через повторну логіку вебхука.

Якщо у вашій схемі немає ідентифікатора події, збережіть перевірений хеш навантаження як додаткове аудиторське підтвердження, але не замінюйте вищевказані бізнес-ідентифікатори на мітку часу.

Сортування та звіряння

Доставка відбувається принаймні один раз, і повідомлення про статус можуть приходити в різному порядку. Реалізуйте монотонні бізнес-правила замість «перемагає останній запит»:

  • ніколи не переміщуйте виконане замовлення назад до check, якщо старіша подія надійшла пізно;
  • дозвольте underpaid_check отримувати додаткові txid без повторення попередніх зарахувань;
  • розглядайте paid та overpaid як успішні стани врегулювання, зберігаючи їх різні суми;
  • тримаєте underpaid як остаточний результат часткової оплати, якщо авторитетний API пізніше не повідомить інший стан;
  • направляйте aml_lock на перевірку і не дозволяйте загальному працівнику повторного виконання завершити його;
  • запитуйте інформацію про оплату/виплати щоразу, коли перехід неможливий, відсутній контекст або фінансово неоднозначний.

Виконуйте заплановану звірку навіть якщо доставка вебхука виглядає нормальною. Порівнюйте стан локального терміналу та нараховану суму з /v1/payment/info, /v1/static-wallet/transactions або /v1/payout/status/{uuid} та повідомляйте про відмінності замість тихого перезаписування історії книги обліку.

Безпека кінцевої точки вебхука

  • Вимагайте HTTPS та зберігайте callback публічно доступним; приватні/loopback цілі callback відхиляються під час створення платежу.
  • Забезпечте невеликий ліміт на тіло запиту та тип вмісту JSON.
  • Обмежуйте швидкість перед дорогими операціями, але залишайте достатньо запасу для легітимних пікових навантажень та повторних спроб.
  • Ніколи не авторизуйте вебхук лише за джерелом IP. Дозвільні списки мережі є захистом у глибину; перевірка HMAC є обов'язковою.
  • Редагуйте sign, ключі API, адреси, коли це вимагає політика, та особисті метадані з логів застосунку.
  • Тримайте як поточні, так і явно заплановані замінні ключі доступними під час контрольованого вікна ротації; ніколи не вгадуйте, який ключ підписав подію.
  • Повертайте загальне повідомлення про помилку при недійсних підписах, щоб кінцева точка не стала оракулом ключа або облікового запису.