Sign in
Introductie/Webhook-meldingen

Webhook-meldingen

Ontvang realtime statusupdates van betalingen en uitbetalingen via HMAC-ondertekende webhooks.

Het 2328.io-systeem stuurt een webhook naar je url_callback zodra de status van een betaling verandert. Dit is de aanbevolen manier om op de hoogte te worden gebracht van succesvolle betalingen.

Verzoekformaat

  • Methode: POST
  • Content-Type: application/json
  • Handtekening: sign-veld in de body van het verzoek

Payload

De webhook-body volgt het formaat van de response van /v1/payment/info en voegt tx_explorer_url en het sign-veld voor handtekeningverificatie toe.

Succesvolle betaling

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

Geannuleerde / mislukte betaling

Wanneer de betaling niet in een terminale paid-status staat, zijn txid, payment_amount en 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"
}

Veldreferentie

VeldTypeBeschrijving
uuidstringPayment UUID
order_idstringJe order-ID
amountdecimal (8 dp)Fiatbedrag in currency
currencystringFiatvaluta die de merchant heeft aangevraagd
urlstringURL van de gehoste checkout
expires_atstring (ISO 8601)Wanneer de betalingssessie verloopt
created_atstring (ISO 8601)Wanneer de betalingssessie is aangemaakt
payer_currencystringCrypto waarin de betaler betaalt
payer_amountdecimal (8 dp)Verwacht cryptobedrag
networkstringBlockchain-netwerk
addressstringStortingsadres
payment_statusstringEen van: pending, check, paid, underpaid_check, underpaid, overpaid, cancel, aml_lock (zie References)
txidstring | nullHash van blockchain-transactie, alleen aanwezig na een bevestigde betaling
tx_explorer_urlstring | nullURL van de transactie in de blockchainverkenner. null als txid ontbreekt of de overdracht intern P2P is.
payment_amountdecimal | nullWerkelijk betaald bedrag, alleen aanwezig na betaling
merchant_amountdecimal (18 dp) | nullBedrag bijgeboekt aan merchant na fees
amount_usddecimal (8 dp)Bedrag in USD op moment van aanmaken
exchange_ratedecimalGebruikte crypto/fiat-wisselkoers
signstring (hex)HMAC-SHA256-handtekening van de payload

De handtekening verifiëren

Om een webhook-handtekening te verifiëren:

  1. Haal het sign-veld uit de payload
  2. Verwijder het sign-veld uit het object
  3. Encodeer de overige velden als JSON
  4. Encodeer de JSON in base64
  5. Bereken HMAC-SHA256 over de base64-string met je API_KEY
  6. Vergelijk de berekende handtekening met de waarde van sign met behulp van een constant-time vergelijking
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);

Verifieer altijd de handtekening voordat je geld aan een gebruiker bijboekt. Een niet-ondertekende of onjuist ondertekende webhook kan een vervalst verzoek zijn.

Uitbetalingswebhooks

Wanneer de status van een uitbetaling verandert, stuurt het systeem een POST-webhook naar de url_callback-URL die bij het aanmaken van de uitbetaling is meegegeven. Als url_callback niet is opgegeven, worden er geen webhooks voor die uitbetaling verstuurd.

Uitbetalingswebhooks moeten worden geverifieerd met je Payout API key — niet met de gewone API key. Het ondertekeningsalgoritme is identiek aan dat van betalingswebhooks (verwijder sign, encodeer als JSON, base64, HMAC-SHA256), alleen de key verschilt.

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

Veldreferentie

VeldTypeBeschrijving
uuidstringPayout UUID
order_idstringJe idempotentie-/referentie-ID, indien meegegeven
statusstringpending, completed, failed, cancelled (zie References)
currencystringUitbetalingsvaluta
networkstringBlockchain-netwerk
amountdecimalUitbetalingsbedrag (in currency)
merchant_amountdecimalBedrag dat van het merchantsaldo is afgeschreven
network_amountdecimalBedrag dat daadwerkelijk on-chain is verstuurd
amount_usddecimalUSD-waarde op het moment van uitbetaling
to_addressstringBlockchain-adres van de ontvanger
memostring | nullMemo / destination tag, indien gebruikt
txidstring | nullHash van blockchain-transactie, ingesteld bij completed
tx_explorer_urlstring | nullURL van de transactie in de blockchainverkenner. null als txid ontbreekt of de overdracht intern P2P is.
block_numberinteger | nullBlockhoogte van de on-chain transactie
error_typestring | nullReden bij status = failed (bijv. aml_risk, zie References)
created_atstring (ISO 8601)Wanneer de uitbetaling is aangemaakt
updated_atstring (ISO 8601)Wanneer de status voor het laatst is gewijzigd
from_currencystringBronsaldo waarvan de uitbetaling is gedebiteerd wanneer automatische conversie is gebruikt (bijv. USDT voor een uitbetaling in BTC)
debited_amountdecimalBedrag dat van het from_currency-saldo is afgeschreven
debited_currencystringValuta van de afschrijving
signstring (hex)HMAC-SHA256-handtekening van de payload, ondertekend met de Payout API key

Best practices

  • Idempotentie — Controleer altijd of de betaling al is verwerkt (op order_id of uuid). Webhooks kunnen meerdere keren binnenkomen.
  • Snelle response — Geef zo snel mogelijk HTTP 200 terug. Verplaats zwaar werk naar een achtergrondqueue.
  • Retries — Als het systeem geen HTTP 200 ontvangt, wordt de webhook na 2 minuten opnieuw verstuurd. Maximaal 5 retry-pogingen.
  • Asynchrone verwerking — Verwerk webhook-events asynchroon om te voorkomen dat de response wordt geblokkeerd.
  • Beveiliging — Verifieer ALTIJD de sign-handtekening voordat je de payload vertrouwt.

Webhooks kunnen in een willekeurige volgorde binnenkomen. Ga er niet vanuit dat de eerste webhook die je ontvangt de eindtoestand is — haal indien zekerheid nodig is altijd opnieuw de status op via /v1/payment/info (of /v1/payout/status/{uuid}).

Leverings- en verwerkingscontract

Gebruik de volgende volgorde binnen uw webhook-endpoint:

  1. Lees de request body zonder geheimen of de volledige handtekening te loggen.
  2. Identificeer of het een betaling/static-wallet gebeurtenis is of een uitbetalingsgebeurtenis, zodat u de juiste API-sleutel selecteert.
  3. Verwijder sign, reproduceer de gedocumenteerde JSON-bytes, bereken HMAC-SHA256 en vergelijk in constante tijd.
  4. Valideer de vereiste identificatoren, decimale strings en statuswaarden.
  5. Voeg atomisch een inbox/idempotentie-record in. Als deze al bestaat, retourneer HTTP 200 zonder bijwerkingen te herhalen.
  6. Voer de order-/grootboekmutatie uit en plaats niet-kritieke e-mails, analyses of meldingen in de wachtrij.
  7. Geef snel HTTP 200 terug.

Roep geen trage externe diensten aan terwijl de idempotentie-transactie wordt vastgehouden. Een time-out na commitment maar vóór terugkeer kan een herhaling veroorzaken; de duplicaat moet de vastgelegde inbox-sleutel observeren en een no-op worden.

GebeurtenisPrimaire identiteitNotities
Betaalsessieuuid + status-/versiebewijsDezelfde factuur kan meerdere legitieme statuswijzigingen doorvoeren.
Deels-betaling opwaarderingfactuur uuid + txidMeer dan één overboeking kan bij dezelfde onderbetaalde factuur horen.
Statische-portemonnee stortingnetwerk + txidorder_id wordt hergebruikt door elke storting naar die portemonnee.
Uitbetalinguitbetaling uuid + statusMaak nooit een tweede uitbetaling aan vanuit webhook-herhaal logica.

Als je schema geen gebeurtenis-id heeft, sla de geverifieerde payload-hash op als aanvullend auditbewijs, maar vervang de bovenstaande bedrijfsidentiteiten niet door een tijdstempel.

Bestelling en afstemming

De levering is minstens één keer en statusberichten kunnen elkaar inhalen. Implementeer monotoon zakelijke regels in plaats van 'laatste verzoek wint':

  • verplaats een vervulde bestelling nooit terug naar check omdat een ouder evenement te laat is aangekomen;
  • sta underpaid_check toe om aanvullende txids te ontvangen zonder eerdere kredieten te herhalen;
  • behandel paid en overpaid als succesvolle afwikkelingstoestanden, terwijl je hun verschillende bedragen behoudt;
  • houd underpaid als een definitief gedeeltelijk betalingsresultaat tenzij de gezaghebbende API later een andere staat meldt;
  • route aml_lock naar beoordeling en laat een generieke herhalingswerker dit niet vervullen;
  • query betalings-/uitbetalingsinformatie telkens wanneer de overgang onmogelijk is, context ontbreekt, of financieel ambigu is.

Voer geplande reconciliatie uit, zelfs wanneer de levering van webhooks gezond lijkt. Vergelijk je lokale terminalstatus en gecrediteerde bedrag met /v1/payment/info, /v1/static-wallet/transactions of /v1/payout/status/{uuid} en waarschuw bij verschillen in plaats van stilzwijgend de grootboekgeschiedenis te overschrijven.

Beveiliging van webhook-endpoint

  • Vereis HTTPS en houd de callback openbaar bereikbaar; privé-/loopback-callbackdoelen worden afgewezen tijdens het maken van betalingen.
  • Handhaaf een kleine limiet voor request-body en JSON-inhoudstype.
  • Beperk het aantal aanvragen voordat er kostbaar werk wordt uitgevoerd, maar laat genoeg speelruimte voor legitieme pieken en herhaalde pogingen.
  • Autoriseer nooit een webhook alleen op basis van het bron-IP. Netwerk-toegestane lijsten zijn een verdediging in diepte; HMAC-verificatie is verplicht.
  • Redigeer sign, API-sleutels, adressen wanneer dit vereist is door het beleid, en persoonlijke metadata uit applicatielogs.
  • Houd zowel de huidige als expliciet geplande vervangende sleutels beschikbaar tijdens een gecontroleerd rotatievenster; raad nooit welke sleutel een gebeurtenis heeft ondertekend.
  • Geef een algemene foutmelding terug bij ongeldige handtekeningen zodat het eindpunt geen sleutel- of accountoracle wordt.