Fiatside

Developers

Webhooks

Ein Webhook teilt Ihnen mit, dass eine Bestellung ihren Status geändert hat. Er ersetzt nicht das Lesen der Bestellung: Er ist eine Benachrichtigung, keine Quelle der Wahrheit.

Veröffentlichter Vertrag, noch nicht offen

Webhooks werden heute nicht ausgegeben. Ihre Form, Signatur und Wiederholungsrichtlinie sind festgelegt und hier veröffentlicht, damit Ihr Empfänger im Voraus geschrieben und getestet werden kann.

01

Ereignisse

Jedes Ereignis trägt denselben Umschlag: eine id, einen Typ, einen Zeitstempel und ein Datenobjekt. Abonnieren Sie die Typen, die Sie verarbeiten, und ignorieren Sie den Rest stillschweigend – neue Typen werden hinzugefügt, und ein Empfänger, der bei einem unbekannten Typ fehlschlägt, zerstört sich selbst.

Ereignisse
TypWas es bedeutet
order.createdDie Bestellung wird erstellt und der Kurs gesperrt. Die Einzahlungsadresse wird zugewiesen.
order.deposit_detectedEine eingehende Transaktion wird im Netzwerk gesehen, vor der Bestätigung. Verwenden Sie dieses Ereignis, um den Benutzer zu beruhigen, niemals um etwas auszuliefern.
order.confirmingDer Bestätigungszähler schreitet voran. Wird bei jedem Schritt ausgegeben, nicht bei jedem Block.
order.deposit_confirmedDie vom Netzwerk geforderte Bestätigungsanzahl ist erreicht.
order.underpaidDer erhaltene Betrag liegt unter dem erwarteten Betrag außerhalb der Toleranz. Die Bestellung ist nicht verloren: Sie wartet auf eine Wahl zwischen Aufstockung, Fortsetzung mit dem erhaltenen Betrag oder einer Rückerstattung.
order.overpaidDer erhaltene Betrag übersteigt den erwarteten Betrag. Der ursprüngliche Betrag bleibt zum gesperrten Kurs; der Überschuss wird separat behandelt.
order.payout_sentDie Überweisung hat den Zahlungsweg verlassen. Achtung: Gesendet ist nicht empfangen – die verbleibende Verzögerung hängt von der Bank des Begünstigten ab.
order.completedDie Abrechnung ist durch den Zahlungsweg bestätigt. Endzustand.
order.payout_failedDer Zahlungsweg hat die Überweisung abgelehnt oder zurückgegeben, mit seinem Grund. Bei einer Bankrückgabe wird kein automatischer erneuter Versuch unternommen: Die Ursache liegt fast immer im Detail.
order.refundedGelder wurden zurückgegeben, nur an die Ursprungsadresse, abzüglich Netzwerkgebühren.
02

Die Nutzlast

Ein konstanter Umschlag, unabhängig vom Typ.

Beispieljson
{
  "id": "evt_01J9ZQF3K8N2M4X7",
  "type": "order.payout_sent",
  "created": 1788356981,
  "data": {
    "reference": "K7Q4-M2XB",
    "state": "PAYOUT_SENT",
    "railId": "sepa_instant",
    "netMinor": 91228,
    "currency": "EUR",
    "sentAt": "2026-09-02T12:21:44Z"
  }
}
03

Signatur

Jede Zustellung trägt einen Signatur-Header: einen Zeitstempel und ein HMAC-SHA256, berechnet über diesen Zeitstempel, einen Punkt und den ROHEN Anforderungstext.

Fiatside-Signaturehttp
Fiatside-Signature: t=<timestamp unix>,v1=<hex hmac-sha256>
Verifizierung, Node.jstypescript
import { createHmac, timingSafeEqual } from 'node:crypto'

// IMPORTANT : le corps doit etre le corps BRUT, avant tout parsing JSON.
// Re-serialiser l'objet change l'ordre des cles et invalide la signature.
export function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=') as [string, string]))
  const timestamp = Number(parts['t'])
  const signature = parts['v1']
  if (!timestamp || !signature) return false

  // Rejeu : une signature valide capturee hier ne doit pas etre rejouable aujourd'hui.
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false

  const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
  const a = Buffer.from(expected, 'hex')
  const b = Buffer.from(signature, 'hex')
  // Comparaison a temps constant : un === laisse fuiter la signature octet par octet.
  return a.length === b.length && timingSafeEqual(a, b)
}
  • Signieren Sie den rohen Text, vor jeglichem Parsen. Das erneute Serialisieren des JSON-Objekts ändert die Schlüsselreihenfolge und Leerzeichen: Die Signatur stimmt nicht mehr überein, und Sie werden lange suchen.
  • Vergleichen Sie in konstanter Zeit. Ein ===-Vergleich stoppt beim ersten unterschiedlichen Byte, wodurch ein Angreifer die erwartete Signatur Byte für Byte messen kann.
  • Lehnen Sie außerhalb des 300-Sekunden-Toleranzfensters ab. Ohne diese Prüfung bleibt eine gültige, gestern erfasste Anfrage heute wiederverwendbar.
  • Antworten Sie 200, bevor Sie verarbeiten. Legen Sie das Ereignis in Ihre Warteschlange und behandeln Sie es danach: Lange Verarbeitung führt zu einem Timeout und damit zu einem erneuten Versuch, obwohl Sie das Ereignis empfangen haben.
04

Wiederholungen und Reihenfolge

Jeder Nicht-2xx-Status oder keine Antwort löst einen erneuten Versuch gemäß dem folgenden Zeitplan aus.

Wiederholungen und Reihenfolge
VersuchVerzögerung
1sofort
230 s
32 min
410 min
51 h
66 h
  • Die Ankunftsreihenfolge ist nicht garantiert. Ein Auszahlungsereignis kann vor dem Bestätigungsereignis eintreffen, das ihm logisch vorausgeht: Vertrauen Sie dem Bestellstatus, nicht der Reihenfolge des Empfangs.
  • Dasselbe Ereignis kann zweimal eintreffen. Speichern Sie die Ereignis-ID und ignorieren Sie eine bereits verarbeitete ID: Idempotenz auf Ihrer Empfängerseite liegt in Ihrer Verantwortung und ist leicht umzusetzen.
  • Vertrauen Sie niemals den Nutzdatenbeträgen, um auf Ihrer Seite etwas gutzuschreiben. Lesen Sie die Bestellung erneut über die API: Die Nutzdaten sagen, dass etwas passiert ist, die API sagt genau, was.
  • Nach dem letzten Versuch wird das Ereignis verworfen und bleibt in Ihrem Ereignisprotokoll sichtbar. Sie können es manuell erneut abspielen.