Przejdź do treści
Oplac

Zdarzenia i webhooki

Zdarzenia

Wszystko, co dzieje się na koncie, zostawia zdarzenie evt_: utworzenie i opłacenie płatności, zwrot, zmiana klienta, odnowienie subskrypcji, faktura w KSeF. Ta sama treść idzie na webhook i jest dostępna pod GET /v1/events, więc po awarii odbiorcy nic nie ginie: nadrabiasz historię z listy albo każesz wysłać zdarzenie ponownie.

Klucz testowy w przykładach

Wklej klucz sk_test_ z panelu (zakładka Deweloperzy), a każdy przykład będzie gotowy do uruchomienia. Klucz zostaje w tej karcie przeglądarki, nie wysyłamy go nigdzie.

Przykłady używają Twojego klucza testowego. Produkcyjnego klucza nie przyjmujemy.

Tryb testowy

Obiekt event#

PoleZnaczenie
id identyfikator evt_; stały, służy do obsługi idempotentnej
type typ zdarzenia, np. payment.succeeded
api_version wersja API, w której przygotowano data.object
livemode true dla zdarzeń produkcyjnych
created chwila zdarzenia (Unix)
data.object migawka obiektu z chwili zdarzenia, bez rozwinięć (pola powiązane to identyfikatory)
request.id, request.idempotency_key żądanie API, które wywołało zdarzenie, albo null dla zdarzeń z harmonogramu i strony płatności
pending_webhooks ile doręczeń jeszcze czeka
delivery tylko w GET /v1/events: liczba prób, ostatni kod HTTP, chwila doręczenia, następna próba
JSON
{
  "id": "evt_Ab12Cd34Ef56Gh",
  "object": "event",
  "type": "payment.succeeded",
  "api_version": "2026-10-03",
  "livemode": false,
  "created": 1760000000,
  "data": {
    "object": {
      "id": "pay_Ab12Cd34Ef56Gh",
      "object": "payment",
      "amount": 12900,
      "currency": "PLN",
      "status": "succeeded",
      "method": "blik",
      "metadata": {"order_id": "10482"},
      "livemode": false
    }
  },
  "request": {"id": "req_7mK2pQ9rT4vX1bN8cD3fG6hJ", "idempotency_key": "zamowienie-10482"},
  "pending_webhooks": 0,
  "delivery": {"attempts": 1, "last_status": 200, "delivered_at": 1760000001, "next_attempt": null}
}

Historia zdarzeń: GET /v1/events#

Lista zdarzeń konta w trybie klucza, od najnowszego, ze stronicowaniem kursorem i filtrami type, delivered oraz created[...]. To Twoja siatka bezpieczeństwa: po awarii odbiorcy webhooków pobierz zdarzenia z delivered=false albo z created[gte] od chwili awarii i przetwórz je tą samą funkcją, która obsługuje webhooki. Pojedyncze zdarzenie zwraca GET /v1/events/{id}.

curl "https://oplac.pl/api/v1/events?type=payment.succeeded&created[gte]=1759968000&limit=100" \
  -H "Authorization: Bearer sk_test_TWOJ_KLUCZ"
$ch = curl_init('https://oplac.pl/api/v1/events?' . http_build_query([
    'type' => 'payment.succeeded', 'created[gte]' => 1759968000, 'limit' => 100,
]));
curl_setopt_array($ch, [
    CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_test_TWOJ_KLUCZ'],
    CURLOPT_RETURNTRANSFER => true,
]);
$events = json_decode(curl_exec($ch), true);

foreach ($events['data'] as $event) {
    obsluzZdarzenie($event);   // ta sama funkcja co w odbiorcy webhooka
}
const q = new URLSearchParams({ type: 'payment.succeeded', 'created[gte]': '1759968000', limit: '100' });
const res = await fetch(`https://oplac.pl/api/v1/events?${q}`, {
  headers: { Authorization: 'Bearer sk_test_TWOJ_KLUCZ' },
});
const events = await res.json();

for (const event of events.data) {
  await obsluzZdarzenie(event); // ta sama funkcja co w odbiorcy webhooka
}
import requests

res = requests.get(
    "https://oplac.pl/api/v1/events",
    headers={"Authorization": "Bearer sk_test_TWOJ_KLUCZ"},
    params={"type": "payment.succeeded", "created[gte]": 1759968000, "limit": 100},
)
events = res.json()

for event in events["data"]:
    obsluz_zdarzenie(event)  # ta sama funkcja co w odbiorcy webhooka
Odpowiedź
HTTP/1.1 200 OK
{
  "object": "list",
  "url": "/v1/events",
  "has_more": false,
  "data": [
    {
      "id": "evt_Ab12Cd34Ef56Gh",
      "object": "event",
      "type": "payment.succeeded",
      "created": 1760000000,
      "data": {"object": {"id": "pay_Ab12Cd34Ef56Gh", "object": "payment", "status": "succeeded"}},
      "pending_webhooks": 0,
      "delivery": {"attempts": 3, "last_status": 200, "delivered_at": 1760002100, "next_attempt": null}
    }
  ]
}

Ponowna wysyłka: POST /v1/events/{id}/resend#

Wysyła zdarzenie na adres webhooka trybu jeszcze raz, od razu, bez zmiany harmonogramu ponowień. Przydaje się po naprawie odbiorcy albo przy odtwarzaniu pojedynczego przypadku. To samo zrobisz w panelu, w historii zdarzeń. Odbiorca musi być gotowy na powtórkę: zdarzenie przychodzi z tym samym id.

Kolejność i powtórki#

  • Kolejność doręczeń nie jest gwarantowana: payment.succeeded może dotrzeć przed payment.created, zwłaszcza po ponowieniach. Stan obiektu czytaj z data.object albo potwierdź żądaniem GET, nie wnioskuj go z kolejności zdarzeń.
  • To samo zdarzenie może przyjść więcej niż raz (ponowienie po timeout, ręczna wysyłka). Zapisuj obsłużone id i pomijaj duplikaty.
  • Zdarzenia z piaskownicy mają livemode: false i idą na adres testowy; produkcyjne na produkcyjny. Odbiorca może być jeden, ale sprawdzaj livemode, zanim zmienisz prawdziwe zamówienie.
  • Pole request.idempotency_key pozwala dopasować zdarzenie do operacji w Twoim systemie bez przeszukiwania metadata.

Typy zdarzeń#

24 typów. Nazwa to obiekt i kropka i co się stało; data.object zawsze niesie pełną migawkę obiektu z chwili zdarzenia. Nowe typy mogą dojść bez zmiany wersji API, dlatego nieznane typy pomijaj zamiast zgłaszać błąd.

Płatności payment.*

TypKiedy przychodzi
payment.created Płatność utworzona (API, link, faktura albo okres subskrypcji). Klient jeszcze nie zapłacił.
payment.succeeded Płatność opłacona. Na tym zdarzeniu oznaczaj zamówienie jako opłacone.
payment.failed Płatność odrzucona; powód w polu failure_code (np. card_declined, blik_rejected).
payment.canceled Płatność anulowana przez API albo wygasła po expires_at.
payment.routed Smart routing wybrał konto dostawcy; pole provider_account mówi które.
TypKiedy przychodzi

Konto sprzedawcy platformy account.*

TypKiedy przychodzi
account.verified Sprzedawca platformy (Connect) przeszedł weryfikację u dostawcy i może przyjmować płatności.

Wypłaty payout.*

TypKiedy przychodzi
payout.paid Dostawca zrealizował wypłatę sprzedawcy platformy (Connect).

Zwroty refund.*

TypKiedy przychodzi
refund.created Zwrot zlecony. W piaskownicy od razu po nim przychodzi refund.succeeded.
refund.succeeded Dostawca wykonał zwrot; amount_refunded płatności jest zaktualizowane.
refund.failed Dostawca odrzucił zwrot. Sprawdź saldo i spróbuj ponownie.

Klienci customer.*

TypKiedy przychodzi
customer.created Nowy klient (z API albo razem z płatnością).
customer.updated Zmiana danych klienta albo jego metadata.
customer.deleted Klient usunięty; data.object ma deleted: true.

Subskrypcje subscription.*

TypKiedy przychodzi
subscription.created Subskrypcja utworzona w statusie incomplete, czeka na opłacenie pierwszego okresu na checkout_url.
subscription.updated Zmiana planu, kuponu, cancel_at_period_end albo metadata; proracja w polu proration.
subscription.renewed Okres odnowiony i opłacony; current_period_end przesunięty.
subscription.payment_failed Odnowienie nieudane, status past_due; Recover ponawia obciążenie i wysyła e-mail z linkiem do dopłaty.
subscription.recovered Recover odzyskał płatność, subskrypcja wraca do active.
subscription.canceled Subskrypcja zakończona (od razu albo z końcem okresu).

Faktury invoice.*

TypKiedy przychodzi
invoice.issued Faktura wystawiona (po opłaceniu albo z terminem płatności).
invoice.paid Faktura z terminem opłacona w całości.
invoice.ksef_accepted KSeF przyjął fakturę; numer w polu ksef_number.
invoice.ksef_rejected KSeF odrzucił fakturę; status ksef_status mówi dlaczego.

Powiązane

Klucz testowy dostajesz od razu po zapisie

Adres e-mail, kod z maila i piaskownica z kodami BLIK. Produkcję włączasz sam w panelu: plan, dostawca płatności, klucz sk_live_. Wolisz, żeby wdrożenie w sklepie zrobił nasz zespół? To też jest za darmo.

Zamów integrację za darmo

BLIK, karty, subskrypcje i KSeF w jednym panelu.