Webhooki
Webhooki to Twoje źródło prawdy o zdarzeniach w dpay Connect. Zamiast odpytywać API w pętli, otrzymujesz push na swój endpoint zawsze, gdy zmieni się status onboardingu, płatności, wypłaty lub delegacji.
Konfiguracja
Adres endpointu (webhook_url) i sekret webhooka ustawiasz przy onboardingu partnera lub w portalu partnera. Sekret służy do weryfikacji podpisu - trzymaj go bezpiecznie, możesz go w każdej chwili zrotować.
Katalog zdarzeń
Onboarding merchanta
Zdarzenia onboardingu nazywają się tak samo jak status, który przyjmuje konto (patrz cykl życia):
| Zdarzenie | Kiedy |
|---|---|
started | Draft utworzony. |
in_progress | Onboarding w toku (dane / akty osobiste). |
agreement_signed | Merchant podpisał umowę. |
pending_transfer | Oczekiwanie na przelew weryfikacyjny. |
under_review | Weryfikacja manualna dpay. |
completed | Konto aktywne. |
blocked | Konto zablokowane. |
Płatności i wypłaty
| Zdarzenie | Kiedy |
|---|---|
payment.succeeded | Płatność opłacona. |
payment.failed | Płatność nieudana lub anulowana. |
payout.paid | Wypłata zrealizowana na rachunek merchanta. |
payout.failed | Wypłata odrzucona. |
Delegacja i reweryfikacja
| Zdarzenie | Kiedy |
|---|---|
authorization.revoked | Merchant cofnął delegację. Cofnięcie obejmuje wszystkie zakresy naraz i działa natychmiast. |
reverification_submitted | Merchant złożył dane w reweryfikacji okresowej. |
Zaproszenia (onboarding przez API)
| Zdarzenie | Kiedy |
|---|---|
invitation.identity_verified | Zaproszona osoba zweryfikowała tożsamość (tylko ścieżka eID - nie polegaj na tym zdarzeniu, ścieżka manualna go nie emituje). |
invitation.completed | Zaproszona osoba ukończyła swój przepływ - jedyny gwarantowany terminal sukcesu zaproszenia. |
invitation.signed | Zaproszony reprezentant podpisał umowę. |
Wypłata marży partnera
| Zdarzenie | Kiedy |
|---|---|
partner_payout.paid | dpay wypłacił Twoją marżę (patrz rozliczenia ISV). |
partner_payout.declined | Wypłata marży odrzucona. |
Zdarzenia partner_payout.* dotyczą Ciebie (partnera), nie merchanta - ich payload nie zawiera pola ref.
Katalog może się rozszerzać. Zaprojektuj handler tak, aby nieznane event było ignorowane (a nie powodowało błędu). Nie zakładaj zamkniętej listy.
Format ładunku
Każdy webhook to POST z płaskim JSON-em. Typ zdarzenia niesie pole event; pozostałe pola leżą na najwyższym poziomie (nie ma zagnieżdżonego obiektu data).
Zmiana statusu onboardingu:
POST /webhooki/dpay HTTP/1.1
Content-Type: application/json
X-Dpay-Signature: sha256=3a7bd3e2360a...
X-Dpay-Timestamp: 1782561600
{
"event": "agreement_signed",
"ref": "faktura-klient-123",
"status": "agreement_signed",
"channel": "fakturka",
"occurred_at": "2026-07-01T10:00:00+00:00",
"event_id": "12345:agreement_signed"
}
Płatność:
{
"event": "payment.succeeded",
"ref": "faktura-klient-123",
"payment_id": "8F2A11C4-...",
"value": 149.00,
"currency": "PLN",
"status": 1,
"channel": "fakturka",
"occurred_at": "2026-07-01T10:05:00+00:00",
"event_id": "pay:8F2A11C4-...:payment.succeeded"
}
Wypłata:
{
"event": "payout.paid",
"ref": "faktura-klient-123",
"payout_id": 98765,
"net": 3480.50,
"currency": "PLN",
"channel": "fakturka",
"occurred_at": "2026-07-02T09:00:00+00:00",
"event_id": "payout:98765:payout.paid"
}
event- typ zdarzenia (dla zdarzeń onboardingu równy nazwie statusu).ref- Twoja referencja merchanta, którego dotyczy zdarzenie (brak wpartner_payout.*).statusw zdarzeniach płatności to numeryczny status transakcji dpay (patrz statusy transakcji).event_id- unikalny identyfikator zdarzenia. Deduplikuj po nim - to samo zdarzenie może zostać dostarczone więcej niż raz.- Zdarzenia zaproszeń niosą dodatkowo
invitation_ref,invitation_role,invitation_status(payload bez danych osobowych zaproszonego).
Weryfikacja podpisu (HMAC)
Zawsze weryfikuj podpis, zanim zaufasz treści webhooka. dpay podpisuje każdy webhook nagłówkiem X-Dpay-Signature.
Podpis liczony jest jako HMAC-SHA256 z sekretu webhooka nad ciągiem "{timestamp}.{surowe_body}", gdzie timestamp pochodzi z nagłówka X-Dpay-Timestamp:
signature = HMAC_SHA256(secret, timestamp + "." + raw_body)
X-Dpay-Signature: sha256=<signature_hex>
Licz HMAC na surowej treści żądania, dokładnie tak jak przyszła (bajt w bajt). Nie parsuj JSON-a i nie serializuj go ponownie przed weryfikacją - zmiana białych znaków czy kolejności kluczy zepsuje podpis. Timestamp jest częścią podpisu, więc chroni też przed atakiem replay.
Przykład: PHP
function verifyDpayWebhook(string $rawBody, array $headers, string $secret): bool
{
$timestamp = $headers['X-Dpay-Timestamp'] ?? '';
$received = $headers['X-Dpay-Signature'] ?? '';
// odrzuć zbyt stary timestamp (ochrona przed replay)
if (abs(time() - (int) $timestamp) > 300) {
return false;
}
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $rawBody, $secret);
return hash_equals($expected, $received);
}
Przykład: Node.js
const crypto = require('crypto');
function verifyDpayWebhook(rawBody, headers, secret) {
const timestamp = headers['x-dpay-timestamp'] || '';
const received = headers['x-dpay-signature'] || '';
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return false; // za stary - możliwy replay
}
const expected =
'sha256=' +
crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}
Odpowiadaj szybko i idempotentnie
- Odpowiadaj
2xxod razu. Ciężką pracę (aktualizacja bazy, wysyłka maili) zrób asynchronicznie po zwróceniu odpowiedzi. Zbyt wolna odpowiedź jest traktowana jak błąd i webhook zostanie ponowiony. - Bądź idempotentny. Ten sam webhook może przyjść więcej niż raz. Deduplikuj po
event_id- nie księguj tej samej płatności dwa razy.
Ponawianie i dead-letter
Jeśli Twój endpoint nie odpowie 2xx (limit czasu odpowiedzi: 10 sekund), dpay ponawia dostarczenie - łącznie do 5 prób, z odstępami 10 s, 60 s, 300 s i 900 s. Po wyczerpaniu prób zdarzenie trafia do dead-letter (kolejki nieudanych dostaw) i możesz je podejrzeć w portalu partnera razem z logiem dostaw.

Sekret webhooka możesz zrotować w portalu partnera. Podczas rotacji zaktualizuj sekret po swojej stronie. Jeśli podejrzewasz wyciek - zrotuj natychmiast; stary sekret przestanie podpisywać nowe dostawy.
Co dalej
- Cykl życia konta - znaczenie statusów w zdarzeniach onboardingu.
- Płatności on-behalf - zdarzenia
payment.*. - Wypłaty on-behalf - zdarzenia
payout.*.