Przejdź do głównej zawartości

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 400 z kodem POINT_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 PENDING dłużej niż zwykle.

Cykl życia zwrotu

Opis statusów:

StatusZnaczenie
CREATEDZwrot został zarejestrowany w systemie Paymentic i czeka na walidację
ACCEPTEDZwrot przeszedł walidację i zostanie przekazany do realizacji
PENDINGZwrot został przekazany do rozliczenia i oczekuje na potwierdzenie od operatora/banku
DONEZwrot został wykonany — środki wróciły na rachunek płacącego
REJECTEDZwrot został odrzucony (np. przez operatora kanału, z powodu problemu technicznego)
CANCELLEDZwrot 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

PoleTypWymaganeOpis
amountstringTakKwota zwrotu w formacie decimal z maks. 2 miejscami po przecinku (np. "123.45"). Nie może przekroczyć salda transakcji dostępnego do zwrotu
reasonstringNiePowód zwrotu (max 64 znaki). Pojawia się w panelu i w raportach — ułatwia identyfikację
externalReferenceIdstringNieTwó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 (CREATEDACCEPTEDPENDINGDONE / 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 externalReferenceId przy tworzeniu zwrotu i przed każdym kolejnym wywołaniem sprawdzaj, czy dla danego RMA-... nie masz już zarejestrowanego refundId w 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 HTTPKod logicznyPrzyczyna
400POINT_REFUNDS_DISABLEDPoint nie ma włączonej obsługi zwrotów — skontaktuj się z opiekunem handlowym
400POINT_NOT_ACTIVEPoint nie jest aktywny
404Transakcja, point lub zwrot nie istnieje
422Walidacja: 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.