Sign in
Wprowadzenie/Powiadomienia Webhook

Powiadomienia webhook

Otrzymuj w czasie rzeczywistym aktualizacje statusu płatności i wypłat poprzez webhooki podpisane HMAC.

System 2328.io wysyła webhook pod Twój url_callback przy każdej zmianie statusu płatności. To zalecany sposób otrzymywania powiadomień o udanych płatnościach.

Format żądania

  • Method: POST
  • Content-Type: application/json
  • Signature: pole sign w treści żądania

Payload

Treść webhooka jest identyczna z odpowiedzią /v1/payment/info, wzbogaconą o pole sign używane do weryfikacji podpisu.

Płatność udana

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",
  "payment_amount": "0.95256917",
  "merchant_amount": "0.949711462490000000",
  "amount_usd": "2.41324380",
  "exchange_rate": "0.01340691",
  "sign": "6f8c15b6e53b506d5bfa38ed3fb3b50697af73434262153c02e412541372f04d"
}

Płatność anulowana / nieudana

Gdy płatność nie jest w terminalnym stanie paid, pola txid, payment_amount oraz merchant_amount mają wartość 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,
  "payment_amount": null,
  "merchant_amount": null,
  "amount_usd": "37.53934800",
  "exchange_rate": "0.01340691",
  "sign": "40ce68ad9691ad54e684329d75ab5adaf5b01409a2d18d3e0110b8c1be605342"
}

Opis pól

PoleTypOpis
uuidstringUUID płatności
order_idstringTwój identyfikator zamówienia
amountdecimal (8 dp)Kwota fiat w currency
currencystringWaluta fiat zażądana przez sprzedawcę
urlstringURL hostowanego checkoutu
expires_atstring (ISO 8601)Moment wygaśnięcia sesji płatności
created_atstring (ISO 8601)Moment utworzenia sesji płatności
payer_currencystringKryptowaluta, w której płaci płacący
payer_amountdecimal (8 dp)Oczekiwana kwota w kryptowalucie
networkstringSieć blockchain
addressstringAdres depozytowy
payment_statusstringJedna z wartości: pending, check, paid, underpaid_check, underpaid, overpaid, cancel, aml_lock (zobacz References)
txidstring | nullHash transakcji blockchain, obecny tylko po potwierdzonej płatności
payment_amountdecimal | nullFaktycznie zapłacona kwota, obecna tylko po płatności
merchant_amountdecimal (18 dp) | nullKwota zaksięgowana sprzedawcy po opłatach
amount_usddecimal (8 dp)Kwota w USD w chwili utworzenia
exchange_ratedecimalUżyty kurs wymiany krypto / fiat
signstring (hex)Podpis HMAC-SHA256 payloadu

Weryfikacja podpisu

Aby zweryfikować podpis webhooka:

  1. Wyciągnij pole sign z payloadu
  2. Usuń pole sign z obiektu
  3. Zakoduj pozostałe pola jako JSON
  4. Zakoduj JSON w base64
  5. Oblicz HMAC-SHA256 z ciągu base64 przy użyciu swojego API_KEY
  6. Porównaj obliczony podpis z wartością sign, używając porównania w czasie stałym
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);

Zawsze weryfikuj podpis przed zaksięgowaniem jakichkolwiek środków użytkownikowi. Niepodpisany lub błędnie podpisany webhook może być spreparowanym żądaniem.

Webhooki wypłat

Gdy zmienia się status wypłaty, system wysyła webhook POST pod adres url_callback przekazany w momencie utworzenia wypłaty. Jeśli url_callback nie został podany, dla tej wypłaty nie są wysyłane żadne webhooki.

Webhooki wypłat muszą być weryfikowane Twoim Payout API key — nie zwykłym kluczem API. Algorytm podpisywania jest identyczny jak dla webhooków płatności (usuń sign, zakoduj jako JSON, base64, HMAC-SHA256), różni się wyłącznie kluczem.

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",
  "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"
}

Opis pól

PoleTypOpis
uuidstringUUID wypłaty
order_idstringTwój identyfikator idempotentności / referencyjny, jeśli go podano
statusstringpending, completed, failed, cancelled (zobacz References)
currencystringWaluta wypłaty
networkstringSieć blockchain
amountdecimalKwota wypłaty (w currency)
merchant_amountdecimalKwota pobrana z salda sprzedawcy
network_amountdecimalKwota faktycznie wysłana w sieci
amount_usddecimalWartość w USD w chwili wypłaty
to_addressstringAdres blockchain odbiorcy
memostring | nullMemo / tag docelowy, jeśli użyty
txidstring | nullHash transakcji blockchain, ustawiany przy completed
block_numberinteger | nullWysokość bloku transakcji on-chain
error_typestring | nullPowód, gdy status = failed (np. aml_risk, zobacz References)
created_atstring (ISO 8601)Moment utworzenia wypłaty
updated_atstring (ISO 8601)Moment ostatniej zmiany statusu
from_currencystringSaldo źródłowe, z którego pobrano wypłatę przy użyciu automatycznej konwersji (np. USDT dla wypłaty w BTC)
debited_amountdecimalKwota pobrana z salda from_currency
debited_currencystringWaluta obciążenia
signstring (hex)Podpis HMAC-SHA256 payloadu, podpisany Payout API key

Dobre praktyki

  • Idempotentność — zawsze sprawdzaj, czy płatność nie została już przetworzona (po order_id lub uuid). Webhooki mogą docierać wielokrotnie.
  • Szybka odpowiedź — odpowiadaj HTTP 200 możliwie najszybciej. Cięższe operacje przekaż do kolejki w tle.
  • Ponawianie prób — jeśli system nie otrzyma HTTP 200, webhook jest wysyłany ponownie po 2 minutach. Maksymalnie 5 prób ponawiania.
  • Przetwarzanie asynchroniczne — obsługuj zdarzenia webhook asynchronicznie, aby nie blokować odpowiedzi.
  • Bezpieczeństwo — ZAWSZE weryfikuj podpis sign przed zaufaniem payloadowi.

Webhooki mogą docierać w innej kolejności niż zdarzenia. Nie zakładaj, że pierwszy otrzymany webhook to stan końcowy — jeśli potrzebujesz pewności, zawsze ponownie pobieraj dane przez /v1/payment/info (lub /v1/payout/status/{uuid}).