Documentatie / Integratie

Webhooks

Coinetic meldt wijzigingen in een factuur met een POST-request aan uw server. Een webhook is een signaal ‘bekijk de factuur’, geen bewijs van betaling.

Instellen

  • Dashboard → ‘Sleutels en webhooks’ → ‘Toevoegen’. De URL is alleen https:// naar een publiek adres op poort 443 (in de sandbox ook http://localhost).
  • Het secret van het endpoint wordt één keer getoond. Maximaal 5 actieve endpoints: elk event gaat naar allemaal.

Request

HTTP
POST /webhooks/coinetic HTTP/1.1
Content-Type: application/json
User-Agent: cryptopay-webhooks/1
X-Webhook-Id: evt_3f9c…
X-Webhook-Timestamp: 1760000000
X-Webhook-Signature: v1=5d1e…

{"id":"evt_3f9c…","type":"invoice.paid","created_at":"2026-10-12T15:04:05Z","livemode":true,"data":{"invoice_id":"inv_x7k2…","order_id":"ORDER-1001","status":"paid","paid_amount":"49990000"}}

X-Webhook-Id blijft gelijk bij retries. Tijdens de rotatie van een secret staan er in X-Webhook-Signature twee handtekeningen, gescheiden door een komma. De technische namen (cryptopay-webhooks/1) zijn historisch; u hoeft ze aan uw kant niet aan te passen.

De handtekening verifiëren

text
expected = "v1=" + hex(HMAC-SHA256(webhook_secret, X-Webhook-Timestamp + "." + raw_body))
  1. Neem de ruwe body — de bytes vóór het parsen van de JSON.
  2. Controleer dat X-Webhook-Timestamp niet meer dan 5 minuten afwijkt van de huidige tijd.
  3. Splits X-Webhook-Signature op de komma. De handtekening is geldig als een willekeurig deel overeenkomt met de verwachte waarde (vergelijken in constante tijd).
  4. Geen overeenkomst — antwoord met 401 en doe verder niets.

Referentie: secret whsec-0123456789abcdef0123456789abcdef, timestamp 1760000000, body {"id":"evt_1","type":"invoice.paid"} → v1=aceeb56215ba09e7a2923d48f89afb8331f422f2d0a8cf60f76fef69fe06a6a4.

Verwerking

text
handtekening verifiëren → dedupliceren op X-Webhook-Id → GET /v1/invoices/{invoice_id}
→ order bijwerken op basis van de status uit de API (precies één keer leveren) → antwoorden met 2xx
  • Deduplicatie. Hetzelfde event kan meerdere keren binnenkomen. Sla verwerkte X-Webhook-Id's op; zet de vlag ‘geleverd’ in dezelfde transactie als de levering zelf.
  • Antwoord snel — binnen 10 seconden. Zet langdurig werk in een queue.
  • Responscodes: 2xx — geaccepteerd; 410 Gone — deze levering niet opnieuw proberen; elke andere code (ook 3xx — redirects worden niet gevolgd) of een timeout — retry.
  • Negeer onbekende velden en eventtypes — de lijst wordt nog uitgebreid.

Kant-en-klare handlers in vier talen vindt u in de snelstart.

Retries

10 s → 30 s → 2 min → 10 min → 30 min → 1 u → 3 u ×7 → 1 u, met een kleine spreiding. De laatste poging volgt na ongeveer een etmaal. Een mislukte levering kunt u opnieuw versturen: dashboard → ‘Webhooks’ → ‘Opnieuw verzenden’.

Volgorde

Events van één factuur worden in volgorde aan het endpoint geleverd: het volgende gaat pas weg als de vorige levering niet meer op een retry wacht. Of die levering nu is geslaagd of niet — daarna gaan de volgende events. Baseer u daarom op de status uit GET /v1/invoices/{id}, niet op de volgorde van de events. Events van verschillende facturen zijn onafhankelijk van elkaar.

Eventtypes

EventWanneer
invoice.detectedDe betaling is zichtbaar in het netwerk
invoice.confirmingDe betaling staat in een blok, de bevestigingen lopen
invoice.paidBetaald en bevestigd — u kunt leveren (na een extra controle). data.late: true — de stablecoin kwam na de vervaldatum binnen en is automatisch verrekend
invoice.overpaidBetaald met een overschot
invoice.underpaidMinder bevestigd dan nodig
invoice.awaiting_paymentDe factuur wacht weer op betaling (bijvoorbeeld na een reorg van de enige betaling)
invoice.expiredDe looptijd is verstreken zonder betaling
invoice.late_paymentBetaling in een volatiele asset na de vervaldatum
invoice.manual_reviewDe betaling wordt gecontroleerd op AML
invoice.payment_heldDe betaling is door AML vastgehouden (quarantaine)
invoice.reorgedHet blok met de betaling is teruggedraaid, de status is herberekend (bijvoorbeeld paid → confirming)
invoice.payment_after_closeEen betaling op een al gesloten factuur: die betaalt de order niet, maar kan worden terugbetaald
invoice.cancelledDe factuur is zonder betaling gesloten door het uitvoeren van een terugbetaling
refund.sentDe terugbetaling is uitgevoerd; in data.refund staan het bedrag, het adres en de tx_hash

Facturerings-events (billing.*) gaan over de facturen voor de platformcommissie: uitgegeven, betaald, achterstallig, opschorting en het opheffen daarvan.

Digitale producten en invoice.reorgedEen reorg na paid is zeldzaam, maar komt voor. Levert u het product direct, abonneer u dan op dit event en geef zulke gevallen door aan uw support.

Rotatie van het secret

Dashboard → ‘Secret wijzigen’: Coinetic ondertekent vanaf dan met beide secrets.

  1. Zet het nieuwe secret op uw server (accepteer beide).
  2. Klik op ‘Rotatie voltooien’ — het oude wordt verwijderd.
  3. Verwijder het oude secret van uw server.