Sign in
Einführung/Webhook-Benachrichtigungen

Webhook-Benachrichtigungen

Erhalten Sie Statusaktualisierungen für Zahlungen und Auszahlungen in Echtzeit über HMAC-signierte Webhooks.

Das System von 2328.io sendet einen Webhook an Ihre url_callback, sobald sich der Status einer Zahlung ändert. Dies ist die empfohlene Methode, um über erfolgreiche Zahlungen benachrichtigt zu werden.

Anfrageformat

  • Methode: POST
  • Content-Type: application/json
  • Signatur: Feld sign im Anfrage-Body

Payload

Der Webhook-Body folgt dem Format der Antwort von /v1/payment/info und ergänzt tx_explorer_url sowie das sign-Feld zur Signaturprüfung.

Erfolgreiche Zahlung

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

Stornierte / fehlgeschlagene Zahlung

Wenn sich die Zahlung nicht im finalen Zustand paid befindet, sind txid, payment_amount und 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"
}

Feldreferenz

FeldTypBeschreibung
uuidstringZahlungs-UUID
order_idstringIhre Bestell-ID
amountdecimal (8 dp)Fiat-Betrag in currency
currencystringVom Händler angeforderte Fiat-Währung
urlstringURL des gehosteten Checkouts
expires_atstring (ISO 8601)Zeitpunkt, an dem die Zahlungssitzung abläuft
created_atstring (ISO 8601)Zeitpunkt, an dem die Zahlungssitzung erstellt wurde
payer_currencystringKrypto, in der der Zahler bezahlt
payer_amountdecimal (8 dp)Erwarteter Krypto-Betrag
networkstringBlockchain-Netzwerk
addressstringEinzahlungsadresse
payment_statusstringEiner von: pending, check, paid, underpaid_check, underpaid, overpaid, cancel, aml_lock (siehe References)
txidstring | nullHash der Blockchain-Transaktion, nur nach bestätigter Zahlung vorhanden
tx_explorer_urlstring | nullURL der Transaktion im Blockchain-Explorer. null, wenn keine txid vorhanden ist oder es sich um einen internen P2P-Transfer handelt.
payment_amountdecimal | nullTatsächlich gezahlter Betrag, nur nach Zahlung vorhanden
merchant_amountdecimal (18 dp) | nullDem Händler nach Gebühren gutgeschriebener Betrag
amount_usddecimal (8 dp)Betrag in USD zum Zeitpunkt der Erstellung
exchange_ratedecimalVerwendeter Krypto-/Fiat-Wechselkurs
signstring (hex)HMAC-SHA256-Signatur des payload

Signatur verifizieren

So verifizieren Sie eine Webhook-Signatur:

  1. Extrahieren Sie das Feld sign aus dem payload
  2. Entfernen Sie das Feld sign aus dem Objekt
  3. Codieren Sie die übrigen Felder als JSON
  4. Codieren Sie das JSON in base64
  5. Berechnen Sie HMAC-SHA256 vom Base64-String unter Verwendung Ihres API_KEY
  6. Vergleichen Sie die berechnete Signatur mit dem Wert sign mittels eines konstantzeitigen Vergleichs
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);

Verifizieren Sie immer die Signatur, bevor Sie einem Nutzer Mittel gutschreiben. Ein nicht oder falsch signierter Webhook könnte eine gefälschte Anfrage sein.

Auszahlungs-Webhooks

Wenn sich der status einer Auszahlung ändert, sendet das System einen POST-Webhook an die url_callback-URL, die bei der Erstellung der Auszahlung übergeben wurde. Wurde keine url_callback angegeben, werden für diese Auszahlung keine Webhooks gesendet.

Auszahlungs-Webhooks müssen mit Ihrem Payout-API-Schlüssel verifiziert werden — nicht mit dem regulären API-Schlüssel. Der Signieralgorithmus ist identisch zu dem für Zahlungs-Webhooks (Feld sign entfernen, JSON-codieren, base64, HMAC-SHA256), nur der Schlüssel ist ein anderer.

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

Feldreferenz

FeldTypBeschreibung
uuidstringAuszahlungs-UUID
order_idstringIhre Idempotenz- / Referenz-ID, sofern angegeben
statusstringpending, completed, failed, cancelled (siehe References)
currencystringAuszahlungswährung
networkstringBlockchain-Netzwerk
amountdecimalAuszahlungsbetrag (in currency)
merchant_amountdecimalVom Händlerguthaben belasteter Betrag
network_amountdecimalTatsächlich on-chain gesendeter Betrag
amount_usddecimalUSD-Wert zum Zeitpunkt der Auszahlung
to_addressstringBlockchain-Adresse des Empfängers
memostring | nullMemo / Destination-Tag, falls verwendet
txidstring | nullHash der Blockchain-Transaktion, gesetzt bei completed
tx_explorer_urlstring | nullURL der Transaktion im Blockchain-Explorer. null, wenn keine txid vorhanden ist oder es sich um einen internen P2P-Transfer handelt.
block_numberinteger | nullBlockhöhe der on-chain-Transaktion
error_typestring | nullFehlerursache, wenn status = failed (z. B. aml_risk, siehe References)
created_atstring (ISO 8601)Zeitpunkt, an dem die Auszahlung erstellt wurde
updated_atstring (ISO 8601)Zeitpunkt der letzten Statusänderung
from_currencystringQuell-Guthaben, von dem die Auszahlung bei automatischer Umrechnung abgebucht wurde (z. B. USDT für eine BTC-Auszahlung)
debited_amountdecimalVom from_currency-Guthaben abgebuchter Betrag
debited_currencystringWährung der Abbuchung
signstring (hex)HMAC-SHA256-Signatur des payload, signiert mit dem Payout-API-Schlüssel

Best Practices

  • Idempotenz — Prüfen Sie stets, ob die Zahlung bereits verarbeitet wurde (anhand von order_id oder uuid). Webhooks können mehrfach eintreffen.
  • Schnelle Antwort — Antworten Sie so schnell wie möglich mit HTTP 200. Lagern Sie aufwändige Arbeit in eine Hintergrund-Queue aus.
  • Wiederholungen — Erhält das System keine HTTP-200-Antwort, wird der Webhook nach 2 Minuten erneut gesendet. Maximal 5 Wiederholungsversuche.
  • Asynchrone Verarbeitung — Verarbeiten Sie Webhook-Ereignisse asynchron, um die Antwort nicht zu blockieren.
  • Sicherheit — Verifizieren Sie IMMER die sign-Signatur, bevor Sie dem payload vertrauen.

Webhooks können in beliebiger Reihenfolge eintreffen. Gehen Sie nicht davon aus, dass der erste empfangene Webhook der finale Zustand ist — rufen Sie bei Unsicherheit immer erneut über /v1/payment/info (oder /v1/payout/status/{uuid}) ab.

Liefer- und Verarbeitungsvertrag

Verwenden Sie die folgende Reihenfolge in Ihrem webhook-Endpunkt:

  1. Lesen Sie den Anforderungstext, ohne Geheimnisse oder die vollständige Signatur zu protokollieren.
  2. Ermitteln Sie, ob es sich um ein Zahlungs-/statisches Wallet-Ereignis oder ein Auszahlungsevent handelt, um den richtigen API-Schlüssel auszuwählen.
  3. Entfernen Sie sign, reproduzieren Sie die dokumentierten JSON-Bytes, berechnen Sie HMAC-SHA256 und vergleichen Sie im konstanten Zeitintervall.
  4. Validieren Sie die erforderlichen Kennungen, Dezimalstrings und Statuswerte.
  5. Fügen Sie atomar einen inbox/idempotency-Datensatz ein. Wenn er bereits existiert, geben Sie HTTP 200 zurück, ohne Nebeneffekte zu wiederholen.
  6. Bestätigen Sie die Mutation der Bestellung/der Buchführung und stellen Sie nicht-kritische E-Mails, Analysen oder Benachrichtigungen in die Warteschlange.
  7. Geben Sie HTTP 200 schnell zurück.

Rufen Sie keine langsamen Dienste von Drittanbietern auf, während Sie die idempotency-Transaktion halten. Ein timeout nach der Bestätigung, aber vor der Rückgabe, kann ein retry verursachen; das Duplikat muss den bestätigten inbox-Schlüssel beachten und ein No-Op werden.

EreignisPrimäre IdentitätNotizen
Zahlungssitzunguuid + Status-/VersionsnachweisDasselbe invoice kann mehrere legitime Statusänderungen auslösen.
Teilzahlung-Aufladunginvoice uuid + txidMehr als eine Überweisung kann zum gleichen unterbezahlten invoice gehören.
Statisches Wallet-EinzahlungNetzwerk + txidorder_id wird von jeder Einzahlung auf dieses Wallet wiederverwendet.
AuszahlungAuszahlung uuid + StatusErstelle niemals eine zweite Auszahlung aus webhook retry Logik.

Wenn Ihr Schema keine Ereignis-ID hat, speichern Sie den verifizierten payload-Hash als zusätzliches Audit-Nachweis, ersetzen Sie jedoch nicht die oben genannten Geschäftsidentitäten durch einen Zeitstempel.

Ordnung und reconciliation

Die Zustellung erfolgt mindestens einmal und Statusnachrichten können rasant sein. Implementiere monotone Geschäftsregeln statt "letzte Anfrage gewinnt":

  • Verschiebe niemals eine erfüllte Bestellung zurück auf check, nur weil ein älteres Ereignis zu spät ankam;
  • underpaid_check zusätzliche TXIDs zu erhalten, ohne frühere Credits zu wiederholen;
  • paid und overpaid als erfolgreiche settlement-Staaten zu behandeln, während ihre unterschiedlichen Mengen erhalten bleiben;
  • Behalte underpaid als ein endgültiges Teilzahlungsergebnis, es sei denn, die autoritative API meldet später einen anderen Zustand;
  • Routen Sie aml_lock zur Überprüfung und lassen Sie nicht zu, dass ein generischer retry-Worker es ausfüllt;
  • Abfrage von Zahlungs- oder Auszahlungsinformationen immer dann, wenn der Übergang unmöglich ist, der Kontext fehlt oder finanziell unklar ist.

Führe geplante reconciliation aus, selbst wenn webhook Lieferung gesund erscheint. Vergleichen Sie Ihren lokalen Terminalzustand und den gutgeschriebenen Betrag mit /v1/payment/info, /v1/static-wallet/transactions oder /v1/payout/status/{uuid} und warnen Sie auf Unterschiede, anstatt die Ledger-Geschichte stillschweigend zu überschreiben.

Webhook Endpunktsicherheit

  • HTTPS erfordern und den Rückruf öffentlich zugänglich halten; Private/Loopback-Rückrufziele werden während der Zahlungserstellung abgelehnt.
  • Setzen Sie ein kleines Request-Body-Limit und einen JSON-Inhaltstyp ein.
  • Rate-Limit vor teuren Arbeiten, aber genug Headroom für echte Bursts und retries lassen.
  • Autorisieren Sie niemals einen webhook ausschließlich durch die Quell-IP. Netzwerk-Lowlists sind Verteidigung in Tiefe; Die HMAC-Verifizierung ist verpflichtend.
  • Schwärbe sign, API-Schlüssel, Adressen, wenn es die Richtlinie ergibt, und persönliche Metadaten aus Anwendungsprotokollen.
  • Halte sowohl aktuelle als auch explizit geplante Ersatztasten während eines kontrollierten Rotationsfensters verfügbar; Rate niemals, welcher Schlüssel ein Ereignis signiert hat.
  • Geben Sie einen generischen Fehlerkörper bei ungültigen Signaturen zurück, damit der Endpunkt nicht zu einem Schlüssel- oder Kontoorakel wird.