Idempotency w PSB API firmy eConnect: jak zapobiegać podwójnym wysyłkom za pomocą headera X-EConnect-DocumentId.
Przy wysyłaniu faktur i innych dokumentów za pośrednictwem API zawsze istnieje ryzyko podwójnej wysyłki. Timeout sieci, restart aplikacji lub nieoczekiwany błąd mogą spowodować, że nie ma pewności, czy dokument został faktycznie przetworzony. Bez ochrony ten sam dokument mógłby zostać wysłany ponownie, co skutkowałoby podwójnymi fakturami.
PSB API oferuje wbudowany mechanizm idempotency, który rozwiązuje ten problem. Wysyłając unikalny documentId z każdym uploadem, PSB rozpoznaje zduplikowane próby i zapobiega dwukrotnemu przetworzeniu tego samego dokumentu.
Mechanizm idempotency opiera się na headerze HTTP X-EConnect-DocumentId. Podczas wysyłania dokumentu dodaje się ten header z unikalną wartością identyfikującą dokument.
POST /api/v1/{partyId}/salesInvoice/send HTTP/1.1
Host: psb.econnect.eu
Authorization: Bearer {token}
Content-Type: application/xml
X-EConnect-DocumentId: 550e8400-e29b-41d4-a716-446655440000
<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
...
</Invoice>
PSB przetwarza dokument i zapisuje documentId. Jeśli to samo żądanie zostanie wysłane ponownie (np. jako retry po timeoucie), PSB rozpoznaje, że documentId już istnieje i zwraca odpowiedź 409 Conflict zamiast ponownie przetwarzać dokument.
HTTP/1.1 409 Conflict
Kod 409 nie jest komunikatem o błędzie w tradycyjnym sensie. To potwierdzenie, że dokument został już wcześniej pomyślnie przetworzony. Aplikacja może bezpiecznie oznaczyć żądanie jako zakończone.
DocumentId wysyłany w headerze X-EConnect-DocumentId musi spełniać kilka warunków:
@ i _ nie są dozwoloneWażne: nigdy nie należy używać numeru faktury jako documentId. Numer faktury może być ponownie użyty dla noty kredytowej lub skorygowanej faktury, co prowadzi do konfliktów. Zamiast tego należy używać UUID/GUID generowanego dla każdej próby wysyłki.
UUID jest zalecanym wyborem. Jest gwarantowanie unikalny i szeroko obsługiwany we wszystkich językach programowania.
Goed: 550e8400-e29b-41d4-a716-446655440000 (UUID)
Goed: DOC2026030800142 (eigen reeks, mits uniek)
Fout: INV-2026-001 (factuurnummer, niet gebruiken)
Fout: ab@cd (speciale tekens)
Fout: abc (te kort)
Idempotency staje się szczególnie wartościowy w połączeniu z logiką ponawiania. Jeśli wywołanie API kończy się niepowodzeniem z powodu błędu sieciowego lub błędu serwera 5xx, chce się ponowić żądanie bez ryzyka podwójnego przetworzenia.
Zalecane podejście wygląda następująco:
X-EConnect-DocumentId.200 OK: dokument został przetworzony, gotowe.409 Conflict: dokument był już przetworzony przy wcześniejszej próbie, gotowe.5xx lub timeout: odczekać i powtórzyć żądanie z tym samym documentId.4xx (inny niż 409): problem dotyczy samego żądania. Ponawianie nie ma sensu, błąd musi zostać naprawiony.Wskazówka: należy stosować exponential backoff przy ponawianiu. Zaczynając od krótkiego czasu oczekiwania (np. 1 sekundy), podwajając go przy każdej kolejnej próbie, do maksimum np. 60 sekund. Sam PSB stosuje politykę ponawiania maksymalnie 8 prób w ciągu ok. 35 godzin przy błędach 5xx.
Header X-EConnect-DocumentId jest obsługiwany na wszystkich endpointach, za pomocą których dokumenty są przesyłane do PSB. Najważniejsze z nich:
POST /api/v1/{partyId}/salesInvoice/sendPOST /api/v1/{partyId}/generic/sendPOST /api/v1/{partyId}/purchaseOrder/sendDla endpointów odbierania (pobieranie dokumentów) idempotency nie ma zastosowania: pobieranie dokumentu jest z natury idempotentne, ponieważ nie modyfikuje danych.
Idempotency to prosty mechanizm o dużym znaczeniu. Konsekwentnie wysyłając X-EConnect-DocumentId z każdym uploadem dokumentu, chronią się Państwo przed podwójnymi wysyłkami. W połączeniu z logiką ponawiania i exponential backoff budowana jest solidna integracja odporna na problemy sieciowe i tymczasowe błędy serwera.
W skrócie
Przy każdym uploadzie dokumentu należy wysłać header X-EConnect-DocumentId z unikalnym UUID. W przypadku ponowienia PSB zwraca 409 Conflict, jeśli dokument został już przetworzony. Nigdy nie należy używać numeru faktury jako documentId.
Zobacz pełną dokumentację API