Zwroty
Paymentic udostępnia dedykowane API do obsługi zwrotów środków na opłaconych transakcjach. Zwrot może być pełny (kwota równa kwocie oryginalnej transakcji) lub częściowy (kwota mniejsza). Do jednej transakcji możesz wykonać wiele zwrotów częściowych, pod warunkiem że ich suma nie przekracza kwoty oryginalnej.
Kiedy można wykonać zwrot
- Transakcja musi być w statusie
PAID— nie zwracasz czegoś, za co nie otrzymałeś środków. - Punkt płatności musi mieć włączoną obsługę zwrotów. Jeśli na Twoim pointzie zwroty są wyłączone umownie, API zwróci
400z kodemPOINT_REFUNDS_DISABLED— w takim przypadku skontaktuj się z opiekunem handlowym. - Kanał płatności, którym transakcja została opłacona, musi obsługiwać automatyczny zwrot. Dla wybranych kanałów zwrot może wymagać dodatkowej obsługi manualnej po naszej stronie — status zwrotu może wtedy przez chwilę pozostać w
PENDINGdłużej niż zwykle.
Cykl życia zwrotu
Opis statusów:
| Status | Znaczenie |
|---|---|
CREATED | Zwrot został zarejestrowany w systemie Paymentic i czeka na walidację |
ACCEPTED | Zwrot przeszedł walidację i zostanie przekazany do realizacji |
PENDING | Zwrot został przekazany do rozliczenia i oczekuje na potwierdzenie od operatora/banku |
DONE | Zwrot został wykonany — środki wróciły na rachunek płacącego |
REJECTED | Zwrot został odrzucony (np. przez operatora kanału, z powodu problemu technicznego) |
CANCELLED | Zwrot został anulowany przed przekazaniem do realizacji |
Status zmienia się asynchronicznie — nie licz na to, że POST /refunds zwróci od razu DONE. Początkowy status w odpowiedzi to zwykle CREATED, a finalizacja następuje w ciągu sekund (karty, BLIK) lub minut/godzin (wybrane PBL-e). Aktualny status możesz odpytać przez GET, ale znacznie wygodniej odebrać notyfikację PAYMENT.REFUND_STATUS_CHANGED.
Tworzenie zwrotu
POST /v1_2/payment/points/{pointId}/transactions/{transactionId}/refunds
Request body
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
amount | string | Tak | Kwota zwrotu w formacie decimal z maks. 2 miejscami po przecinku (np. "123.45"). Nie może przekroczyć salda transakcji dostępnego do zwrotu |
reason | string | Nie | Powód zwrotu (max 64 znaki). Pojawia się w panelu i w raportach — ułatwia identyfikację |
externalReferenceId | string | Nie | Twój własny identyfikator zwrotu (max 64 znaki). Pomaga powiązać zwrot z rekordem po Twojej stronie |
Przykład
curl -X POST "https://api.sandbox.paymentic.com/v1_2/payment/points/000cb241/transactions/FJRS-LY7-3W0-30K9/refunds" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": "49.99",
"reason": "Zwrot towaru - niezgodny z zamowieniem",
"externalReferenceId": "RMA-2026-00123"
}'
Response 201
{
"data": {
"id": "AB1-AB2-AB3",
"status": "CREATED"
}
}
id to refundId w formacie 11-znakowym (^[A-Z0-9]{3}-[A-Z0-9]{3}-[A-Z0-9]{3}$) — zachowaj go po swojej stronie. Jest potrzebny do odpytania statusu i do skojarzenia przychodzących notyfikacji.
Pobranie szczegółów zwrotu
GET /v1_2/payment/points/{pointId}/transactions/{transactionId}/refunds/{refundId}
Przykład
curl -X GET "https://api.sandbox.paymentic.com/v1_2/payment/points/000cb241/transactions/FJRS-LY7-3W0-30K9/refunds/AB1-AB2-AB3" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response 200
{
"data": {
"id": "AB1-AB2-AB3",
"status": "DONE",
"amount": "49.99",
"reason": "Zwrot towaru - niezgodny z zamowieniem",
"externalReferenceId": "RMA-2026-00123",
"createdAt": "2026-04-21T10:15:00+02:00",
"updatedAt": "2026-04-21T10:15:12+02:00"
}
}
Notyfikacja o zmianie statusu
Zamiast pollować GET w pętli, skonfiguruj webhook — Paymentic wyśle Ci PAYMENT.REFUND_STATUS_CHANGED przy każdej zmianie statusu zwrotu (CREATED → ACCEPTED → PENDING → DONE / REJECTED / CANCELLED). Wszystkie notyfikacje są podpisane HMAC-SHA512 — zobacz Weryfikacja podpisów webhooków.
Zwroty częściowe
Aby zwrócić tylko część kwoty, przekaż w amount wartość mniejszą niż kwota oryginalnej transakcji. Do jednej transakcji możesz wykonać wiele zwrotów częściowych — Paymentic pilnuje, aby ich suma nie przekroczyła kwoty oryginalnej. Próba utworzenia zwrotu, który łącznie z poprzednimi przekroczyłby tę kwotę, zakończy się błędem 422.
Typowe scenariusze:
- Zwrot jednej pozycji z wielopozycyjnego koszyka — pojedynczy zwrot na kwotę tej pozycji
- Zwrot prowizji / kosztów dostawy — osobne zwroty z opisowym
reason, dla jasności w raportach - Rozłożony proces reklamacyjny — kilka zwrotów w czasie, w miarę jak klient zwraca kolejne produkty
Idempotencja i externalReferenceId
API tworzenia zwrotu nie jest idempotentne automatycznie — każde wywołanie POST /refunds z tymi samymi danymi utworzy nowy zwrot. Jeśli klient w Twojej integracji kliknie "Zwróć" dwa razy albo retry zadziała po timeoucie, istnieje ryzyko podwójnego zwrotu środków.
Rekomendowane zabezpieczenia po Twojej stronie:
- Zablokuj przycisk zwrotu w UI od razu po pierwszym kliknięciu
- Zapisuj
externalReferenceIdprzy tworzeniu zwrotu i przed każdym kolejnym wywołaniem sprawdzaj, czy dla danegoRMA-...nie masz już zarejestrowanegorefundIdw swojej bazie - Ustaw timeout na klienta HTTP krótszy niż timeout retry-policy Twojego workera, żeby nie wracać z retry, gdy pierwszy zwrot się faktycznie powiódł
Błędy
| Kod HTTP | Kod logiczny | Przyczyna |
|---|---|---|
400 | POINT_REFUNDS_DISABLED | Point nie ma włączonej obsługi zwrotów — skontaktuj się z opiekunem handlowym |
400 | POINT_NOT_ACTIVE | Point nie jest aktywny |
404 | — | Transakcja, point lub zwrot nie istnieje |
422 | — | Walidacja: np. kwota większa od dostępnego salda do zwrotu, niewłaściwy format, transakcja nie jest w statusie PAID |
Odpowiedź błędu zawiera szczegóły w polu errors[] z polami code, detail i href do dokumentacji błędu.