Developers
Webhooki
Webhook informuje Cię o zmianie stanu zamówienia. Nie zastępuje odczytu zamówienia: to powiadomienie, a nie źródło prawdy.
Opublikowany kontrakt, jeszcze nie otwarty
Webhooki nie są dziś wysyłane. Ich struktura, podpis i polityka ponowień są ustalone i opublikowane tutaj, aby można było wcześniej napisać i przetestować odbiornik.
Zdarzenia
Każde zdarzenie ma tę samą kopertę: id, typ, znacznik czasu i obiekt danych. Subskrybuj typy, które obsługujesz, i po cichu ignoruj pozostałe — nowe typy będą dodawane, a odbiornik, który zawiedzie na nieznanym typie, sam się psuje.
| Typ | Co oznacza |
|---|---|
order.created | Zamówienie jest utworzone, a kurs zablokowany. Adres depozytowy jest przypisany. |
order.deposit_detected | Przychodząca transakcja jest widoczna w sieci przed potwierdzeniem. Użyj tego zdarzenia, aby uspokoić użytkownika, nigdy, aby cokolwiek dostarczyć. |
order.confirming | Licznik potwierdzeń postępuje. Emitowany na każdym kroku, nie na każdym bloku. |
order.deposit_confirmed | Osiągnięto liczbę potwierdzeń wymaganą przez sieć. |
order.underpaid | Otrzymana kwota jest poniżej oczekiwanej kwoty poza tolerancją. Zamówienie nie jest utracone: oczekuje na wybór między doładowaniem, kontynuacją z otrzymaną kwotą lub zwrotem. |
order.overpaid | Otrzymana kwota przekracza oczekiwaną kwotę. Początkowa kwota pozostaje po zablokowanym kursie; nadwyżka jest obsługiwana osobno. |
order.payout_sent | Przelew został wysłany w systemie płatności. Uwaga: wysłane nie jest otrzymane — pozostałe opóźnienie zależy od banku beneficjenta. |
order.completed | Rozliczenie jest potwierdzone przez system płatności. Stan końcowy. |
order.payout_failed | System płatności odrzucił lub zwrócił przelew, podając powód. Nie wykonuje się automatycznej ponownej próby w przypadku zwrotu bankowego: przyczyna prawie zawsze leży w szczegółach. |
order.refunded | Środki zostały zwrócone, wyłącznie na adres źródłowy, pomniejszone o opłaty sieciowe. |
Ładunek
Stała koperta, niezależnie od typu.
{
"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"
}
}Podpis
Każda dostawa zawiera nagłówek podpisu: znacznik czasu i HMAC-SHA256 obliczony na podstawie tego znacznika czasu, kropki i SUROWEGO treści żądania.
Fiatside-Signature: t=<timestamp unix>,v1=<hex hmac-sha256>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)
}- Podpisuj surową treść przed jakimkolwiek parsowaniem. Ponowna serializacja obiektu JSON zmienia kolejność kluczy i białe znaki: podpis nie będzie już pasował i będziesz długo szukać błędu.
- Porównuj w czasie stałym. Porównanie === zatrzymuje się na pierwszej różnej bajcie, co pozwala atakującemu zmierzyć oczekiwany podpis bajt po bajcie.
- Odrzucaj poza 300-sekundowym oknem tolerancji. Bez tego sprawdzenia ważne żądanie przechwycone wczoraj pozostaje możliwe do odtworzenia dziś.
- Odpowiadaj 200 przed przetwarzaniem. Umieść zdarzenie w kolejce i obsłuż je później: długie przetwarzanie powoduje przekroczenie limitu czasu, a tym samym ponowienie, mimo że zdarzenie zostało odebrane.
Ponowienia i kolejność
Każdy status inny niż 2xx lub brak odpowiedzi powoduje ponowienie zgodnie z poniższym harmonogramem.
| Próba | Opóźnienie |
|---|---|
| 1 | natychmiast |
| 2 | 30 s |
| 3 | 2 min |
| 4 | 10 min |
| 5 | 1 h |
| 6 | 6 h |
- Kolejność dostarczania nie jest gwarantowana. Zdarzenie wypłaty może dotrzeć przed zdarzeniem potwierdzenia, które logicznie je poprzedza: ufaj stanowi zamówienia, a nie kolejności otrzymania.
- To samo zdarzenie może nadejść dwukrotnie. Zapisz identyfikator zdarzenia i zignoruj identyfikator już obsłużony: idempotencja po Twojej stronie odbiorcy jest Twoim obowiązkiem i łatwo ją osiągnąć.
- Nigdy nie ufaj kwotom z ładunku, aby cokolwiek zaksięgować po swojej stronie. Odczytaj zamówienie ponownie przez API: ładunek mówi, że coś się stało, API mówi dokładnie co.
- Po ostatniej próbie zdarzenie jest odrzucane i pozostaje widoczne w dzienniku zdarzeń. Możesz je odtworzyć ręcznie.