Przejdź do treści
Ostatnia aktualizacja

W SimPay Flow działają dwa niezależne rodzaje powiadomień:

RodzajCzego dotyczyDomyślny adres i klucz
Webhooki FlowZmiany statusów submerchantów, ich kanałów płatności i wypłatAdres i klucz ustawione w usłudze Flow w Panelu Partnera SimPay
Webhooki płatności (IPN)Transakcje, zwroty, kody BLIK, subskrypcje i aliasy BLIK submerchantów - standardowe powiadomienia IPNAdres i klucz IPN usługi nadrzędnej

Dla każdego submerchanta możesz nadpisać oba adresy - zobacz Własne adresy dla submerchanta.


Webhooki Flow

Struktura powiadomienia

Webhooki Flow są wysyłane metodą POST jako JSON, z nagłówkiem User-Agent: SimPay-IPN/2.0.

{
  "type": "flow_submerchant_withdraw:status_changed",
  "webhook_id": "0199a6c2-5f3e-7d21-8b4a-2c3d4e5f6a7b",
  "attempt": 0,
  "flow_service": {
    "id": "flow_aaaabbbb",
    "name": "Moja platforma"
  },
  "date": "2026-10-01T12:00:00+02:00",
  "data": {
    "id": "0199a5f0-2b8c-7c1e-9e3a-1f2b3c4d5e6f",
    "short_id": "1f2b3c4d",
    "amount": 150.5,
    "created_at": "2026-10-01T10:00:00+02:00",
    "updated_at": "2026-10-01T12:00:00+02:00",
    "wallet": {
      "id": "0199a5f0-1111-7c1e-9e3a-1f2b3c4d5e6f",
      "currency": "PLN"
    },
    "status": {
      "previous": "accepted",
      "current": "done"
    },
    "submerchant": {
      "id": "flsm_aaaabbbb",
      "custom_id": "user-123"
    }
  },
  "signature": "eb4f27ad689b4613fd7cbeff1aa7b130b5cbe00e6fe3bc93772fb72fa037e472"
}
PoleOpis
typeTyp zdarzenia (patrz niżej)
webhook_idUnikalne ID powiadomienia (UUID). Każda kolejna próba wysyłki ma nowe webhook_id.
attemptNumer próby wysyłki, liczony od 0
flow_service.idID usługi Flow
flow_service.nameNazwa usługi Flow
dateData wysłania powiadomienia (ISO 8601)
dataDane zdarzenia - zależne od type
signatureSygnatura SHA-256 - zobacz Weryfikacja sygnatury

Odpowiedź

Powiadomienie uznajemy za dostarczone, gdy Twój serwer odpowie:

  • kodem 200 z treścią OK, lub
  • kodem 202 albo 204.

Każda inna odpowiedź, błąd połączenia lub przekroczenie czasu (5 s na połączenie, 15 s na odpowiedź) oznacza nieudaną próbę.

Ponowienia

Nieudane powiadomienia są ponawiane - maksymalnie 30 prób:

PróbyOdstęp między próbami
1-10ok. 1 minuty
11-15ok. 5 minut
16-20ok. 30 minut
21-30ok. 1 godziny
Wysyłamy tylko aktualny stan

Jeśli przed ponowieniem stan obiektu zmieni się ponownie (np. wypłata przejdzie z accepted na done), nieaktualne powiadomienie nie zostanie już wysłane - otrzymasz tylko powiadomienie o najnowszej zmianie. Dlatego zawsze traktuj current jako aktualny stan, a nie zakładaj, że otrzymasz każdą pośrednią zmianę.

Historię wysłanych webhooków (wraz z odpowiedziami Twojego serwera) znajdziesz w Panelu Partnera SimPay, w ustawieniach usługi Flow. Stamtąd możesz też wysłać powiadomienie testowe.


Zdarzenia Flow

flow_submerchant:status_changed

Zmienił się status submerchanta - np. po przekazaniu danych w onboardingu, po akceptacji przez SimPay lub po zablokowaniu konta.

"data": {
  "submerchant": {
    "id": "flsm_aaaabbbb",
    "custom_id": "user-123"
  },
  "previous": {
    "status": "client_verify",
    "aml_level": 1,
    "aml_status": "sent",
    "fully_active": false
  },
  "current": {
    "status": "client_active",
    "aml_level": 2,
    "aml_status": "accepted",
    "fully_active": true
  }
}
PoleOpis
statusStatus konta submerchanta w SimPay (np. client_new, client_verify, client_active, client_block)
aml_levelPoziom weryfikacji AML (2 - pełna weryfikacja)
aml_statusStatus weryfikacji AML (np. sent, accepted, declined)
fully_activetrue, gdy submerchant jest w pełni aktywny i może przyjmować płatności
Wskazówka

Do decyzji, czy submerchant może przyjmować płatności, używaj pola current.fully_active. Pozostałe pola mają charakter informacyjny.

flow_submerchant_payment_channel:status_changed

Zmienił się status kanału płatności submerchanta - np. kanał wysłany do weryfikacji został zaakceptowany lub odrzucony, zmienił się status BLIK OneClick lub aktywne modele BLIK Płatności Powtarzalnych.

"data": {
  "submerchant": {
    "id": "flsm_aaaabbbb",
    "custom_id": "user-123"
  },
  "channel": {
    "slug": "blik-level0",
    "name": "BLIK Level 0"
  },
  "previous": {
    "status": "active",
    "blik_one_click_status": "REGISTERED_WAITING_ACTIVATION",
    "blik_recurrent_a": null,
    "blik_recurrent_m": null,
    "blik_recurrent_o": null
  },
  "current": {
    "status": "active",
    "blik_one_click_status": "ACTIVE",
    "blik_recurrent_a": null,
    "blik_recurrent_m": null,
    "blik_recurrent_o": null
  }
}
PoleOpis
channel.slugID kanału płatności (to samo co id w GET .../payment-channels)
statusStatus kanału: new, in_progress, active, rejected
blik_one_click_statusStatus BLIK OneClick (tylko blik-level0): PENDING_REGISTRATION, REGISTERED_WAITING_ACTIVATION, ACTIVE, REJECTED lub null
blik_recurrent_a / _m / _oCzy model BLIK Płatności Powtarzalnych AUTO / MANUAL / OPEN jest aktywny (tylko blik-recurrent) lub null

flow_submerchant_withdraw:status_changed

Zmienił się status wypłaty submerchanta. Przykład data - patrz Struktura powiadomienia.

PoleOpis
id / short_idID wypłaty (UUID) i krótkie ID wypłaty
amountKwota wypłaty
wallet.id / wallet.currencyPortfel, z którego realizowana jest wypłata, i jego waluta
status.previous / status.currentPoprzedni i aktualny status wypłaty: new, accepted, waiting, done, rejected

flow:test

Powiadomienie testowe wysyłane z Panelu Partnera. data zawiera tylko losowe pole nonce:

"data": {
  "nonce": "01K6FZ8X3Q9V2N7M4T5R1W0YAB"
}

Weryfikacja sygnatury

Każdy webhook Flow zawiera pole signature - hash SHA-256 (lowercase hex). Zasada jest taka sama jak w powiadomieniach płatności:

  1. weź wszystkie wartości z powiadomienia (bez pola signature) w kolejności, w jakiej występują w JSON, spłaszczając zagnieżdżone obiekty,
  2. połącz je separatorem |,
  3. dołącz na końcu klucz webhooków Flow,
  4. policz SHA-256 i porównaj z signature bezpieczną funkcją porównującą.
type|webhook_id|attempt|flow_service.id|flow_service.name|date|data_value1|...|data_valueN|KLUCZ_WEBHOOKÓW_FLOW
Wartości logiczne, null i liczby

W odróżnieniu od powiadomień płatności webhooki Flow zawierają wartości true/false, null i liczby. Przed połączeniem zamień je na tekst tak jak PHP:

  • true → 1,
  • false → pusty ciąg,
  • null → pusty ciąg,
  • liczby → zapis bez zbędnych zer (0, 2, 150.5, 100 dla 100.0).

W JavaScript String(true) daje "true", a w Pythonie str(True) daje "True" - bez konwersji sygnatura się nie zgodzi.

Klucz: jeśli submerchant ma własny adres webhooków Flow, powiadomienia o nim są podpisywane jego kluczem. W pozostałych przypadkach - kluczem webhooków ustawionym w usłudze Flow.

Zobacz wyliczenie sygnatury dla przykładu powyżej

Klucz: FLOW_WEBHOOK_KEY_64

flow_submerchant_withdraw:status_changed|0199a6c2-5f3e-7d21-8b4a-2c3d4e5f6a7b|0|flow_aaaabbbb|Moja platforma|2026-10-01T12:00:00+02:00|0199a5f0-2b8c-7c1e-9e3a-1f2b3c4d5e6f|1f2b3c4d|150.5|2026-10-01T10:00:00+02:00|2026-10-01T12:00:00+02:00|0199a5f0-1111-7c1e-9e3a-1f2b3c4d5e6f|PLN|accepted|done|flsm_aaaabbbb|user-123|FLOW_WEBHOOK_KEY_64

SHA-256: eb4f27ad689b4613fd7cbeff1aa7b130b5cbe00e6fe3bc93772fb72fa037e472

<?php

function simpayFlowSignatureValid(array $payload, string $key): bool
{
    $signature = $payload['signature'] ?? '';
    unset($payload['signature']);

    $values = [];
    array_walk_recursive($payload, function ($value) use (&$values) {
        $values[] = $value;
    });
    $values[] = $key;

    // implode() zamienia true na "1", a false i null na pusty ciąg
    return hash_equals(hash('sha256', implode('|', $values)), $signature);
}

$payload = json_decode(file_get_contents('php://input'), true);

if (!is_array($payload) || !simpayFlowSignatureValid($payload, 'KLUCZ_WEBHOOKÓW_FLOW')) {
    http_response_code(403);
    exit('INVALID_SIGNATURE');
}

// obsługa zdarzenia $payload['type']

echo 'OK';

Webhooki płatności (IPN) w SimPay Flow

Powiadomienia o transakcjach, zwrotach, kodach BLIK, subskrypcjach i aliasach BLIK submerchantów to standardowe powiadomienia IPN - z tą samą strukturą i tym samym sposobem wyliczania sygnatury. W SimPay Flow różnią się kilkoma szczegółami.

Adres i klucz

SytuacjaAdresKlucz do sygnatury
Submerchant nie ma własnego adresu IPNAdres IPN usługi nadrzędnejKlucz IPN usługi nadrzędnej
Submerchant ma własny adres IPNWłasny adres submerchantaKlucz IPN submerchanta (zwrócony przy ustawianiu adresu)

Dzięki temu w podstawowej konfiguracji obsługujesz powiadomienia wszystkich submerchantów jednym endpointem i jednym kluczem - tym samym, którego używasz dla usługi nadrzędnej.

service_id

We wszystkich powiadomieniach płatności dotyczących submerchantów (transaction:status_changed, transaction_refund:status_changed, transaction_blik_level0:code_status_changed, subscription:status_changed, blik:alias_status_changed) pole service_id zawiera ID usługi nadrzędnej - tej, którą podajesz w ścieżce /payment/{serviceId}/.... Wewnętrzna usługa submerchanta nigdy nie pojawia się w powiadomieniach.

Obiekt data.flow

Każde powiadomienie płatności dotyczące submerchanta (wszystkie zdarzenia poza ipn:test) zawiera w data obiekt flow. Dla zwykłych usług Płatności Online obiekt nie występuje.

PoleOpis
flow.submerchant_idID submerchanta (flsm_...)
flow.submerchant_custom_idcustomId submerchanta
flow.flow_service_idID usługi Flow

Ponieważ service_id wskazuje usługę nadrzędną, to właśnie obiekt flow pozwala przypisać transakcję, zwrot, subskrypcję czy alias do właściwego submerchanta w Twoim systemie.

"flow": {
  "submerchant_id": "flsm_aaaabbbb",
  "submerchant_custom_id": "user-123",
  "flow_service_id": "flow_aaaabbbb"
}

Zmiany w transaction:status_changed

Dodatkowo w zdarzeniu transaction:status_changed pojawia się pole amount.commission_flow - Twoja prowizja Flow od transakcji (string, format %.2f). Występuje tylko, gdy prowizja jest większa od zera.

{
  "type": "transaction:status_changed",
  "notification_id": "0199a6d0-1234-7abc-9def-0123456789ab",
  "date": "2026-10-01T12:05:00+02:00",
  "data": {
    "id": "0199a6cf-aaaa-7bbb-8ccc-0123456789ab",
    "payer_transaction_id": "Q68KLAKN",
    "service_id": "252b85c9",
    "status": "transaction_paid",
    "amount": {
      "final_currency": "PLN",
      "final_value": "49.99",
      "original_currency": "PLN",
      "original_value": "49.99",
      "commission_system": "0.60",
      "commission_partner": "49.14",
      "commission_currency": "PLN",
      "commission_flow": "0.25"
    },
    "control": "order-1001",
    "payment": {
      "channel": "blik",
      "type": "blik"
    },
    "customer": {
      "country_code": "PL"
    },
    "paid_at": "2026-10-01T12:04:58+02:00",
    "created_at": "2026-10-01T12:03:10+02:00",
    "flow": {
      "submerchant_id": "flsm_aaaabbbb",
      "submerchant_custom_id": "user-123",
      "flow_service_id": "flow_aaaabbbb"
    }
  },
  "signature": "..."
}
Sygnatura obejmuje nowe pola

Pola commission_flow i flow biorą udział w wyliczaniu sygnatury jak każde inne pole obiektu data. Jeśli Twój walidator dynamicznie spłaszcza cały payload (tak jak w przykładach), nie musisz niczego zmieniać.


Własne adresy dla submerchanta

Domyślnie wszystkie powiadomienia trafiają na adresy wspólne dla całej usługi. Dla wybranego submerchanta możesz ustawić osobne adresy - np. gdy każdy z Twoich Klientów ma własną instancję aplikacji.

Aktualne ustawienia pobierzesz przez GET .../submerchants/{submerchantId}/webhooks, a zmienisz je przez PATCH .../submerchants/{submerchantId}/webhooks:

curl -X PATCH "https://api.simpay.pl/flow/{flowServiceId}/submerchants/user-123/webhooks" \
  -H "Authorization: Bearer TWÓJ_TOKEN_API" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": { "customUrl": "https://klient-123.example.com/webhooks/flow" },
    "payment": { "customUrl": "https://klient-123.example.com/webhooks/ipn" }
  }'
{
  "success": true,
  "data": {
    "paymentNewIpnKey": "aVeryLongAndSecureKeyFor32Chars0",
    "flowNewIpnKey": "..."
  }
}
Zapisz klucze z odpowiedzi

Przy pierwszym ustawieniu własnego adresu SimPay generuje dla submerchanta osobny klucz do weryfikacji sygnatury powiadomień. Klucz jest zwracany tylko raz - w odpowiedzi na to żądanie. Przy kolejnych zmianach adresu klucz się nie zmienia, a pole w odpowiedzi ma wartość null.

Aby wrócić do adresu domyślnego, wyślij {"flow": {"useDefault": true}} (lub payment). Własny adres i klucz submerchanta zostaną usunięte, a powiadomienia wrócą na adres i klucz usługi.

Nowy klucz

Jeśli klucz wyciekł lub go zgubiłeś, wygeneruj nowy przez PATCH .../webhooks/key z {"type": "flow"} lub {"type": "payment"}. Działa to tylko dla submerchantów z własnym adresem webhooków danego typu. Po wygenerowaniu nowego klucza poprzedni przestaje być używany.