Duplikaty zamówień to najczęstszy objaw zawodnej integracji: to samo zamówienie z marketplace’u trafia do sklepu dwa razy, magazyn rezerwuje podwójny towar, klient dostaje dwie przesyłki albo jedną fakturę za dużo. Problem nie jest operacyjny, tylko projektowy — duplikaty trzeba wyeliminować u źródła, na poziomie klucza idempotencji, a nie filtrować po fakcie.
KEY: Duplikat powstaje, gdy ponowienie żądania po timeoucie API tworzy drugi rekord, bo system nie rozpoznaje, że to to samo zamówienie. Rozwiązanie to idempotencja: każde zamówienie niesie unikalny identyfikator z marketplace’u, a system przed zapisem sprawdza, czy taki identyfikator już istnieje — jeśli tak, aktualizuje rekord zamiast tworzyć nowy.
Skąd biorą się duplikaty zamówień
Trzy mechanizmy odpowiadają za zdecydowaną większość duplikatów
Ponowienie po timeoucie. Integracja wysyła żądanie, marketplace nie odpowiada w limicie czasu, system ponawia żądanie. Żądanie „nieudane" w rzeczywistości się powiodło — i teraz zamówienie powstaje dwa razy. To najczęstsza przyczyna.
Podwójna subskrypcja webhooków. Marketplace wysyła powiadomienie o zamówieniu na dwa endpointy (stary i nowy), albo webhook jest dostarczany więcej niż raz (co jest normalne — webhooki są „at-least-once"). Bez deduplikacji każde doręczenie tworzy zamówienie.
Import krzyżowy. Synchronizacja działa w obie strony i zamówienie utworzone w sklepie „wraca" z marketplace’u jako nowe, bo brakuje wspólnego identyfikatora po obu stronach.
- Wspólny mianownik: system nie umie odpowiedzieć na pytanie „czy to już mam?". Idempotencja to właśnie umiejętność rozpoznania powtórzonego żądania.
Czym jest idempotencja — definicja operacyjna
Idempotencja to właściwość operacji, którą można wykonać wielokrotnie z tym samym skutkiem, co jednokrotnie. W integracjach e-commerce chodzi o jedno: ponowione żądanie utworzenia zamówienia nie może utworzyć drugiego zamówienia.
Implementacja jest prosta w zamyśle i wymagająca w szczegółach
- Każde zamówienie z marketplace’u ma unikalny identyfikator nadany przez marketplace.
- Zapisujemy ten identyfikator razem z zamówieniem w sklepie (jako osobne pole lub część klucza).
- Przed utworzeniem zamówienia system sprawdza, czy zamówienie o tym identyfikatorze już istnieje.
- Jeśli istnieje — aktualizuje je (status, dane) i kończy przetwarzanie. Jeśli nie — tworzy nowe.
Kontrola musi być atomowa: dwa równoległe procesy nie mogą jednocześnie „sprawdzić i nie znaleźć". W bazie danych rozwiązuje to unikalny indeks na identyfikatorze zamówienia z marketplace’u — drugi zapis po prostu się nie powiedzie i trafi do obsługi jako duplikat, a nie jako nowe zamówienie.
Idempotencja w praktyce — wzorzec implementacji
Wzorzec, który sprawdził się w naszych wdrożeniach (Laravel + PostgreSQL)
Klucz idempotencji. Pole external_order_id z unikalnym indeksem, unikalność w obrębie kanału (channel + external_order_id). Ten sam numer zamówienia na Allegro i Amazonie to dwie różne rzeczy — kanał jest częścią klucza.
Kolejność operacji. Najpierw próba zapisu z kluczem, potem cała reszta (rezerwacja stanu, faktura, powiadomienia). Jeśli zapis się nie powiedzie z powodu duplikatu — przetwarzanie kończy się na aktualizacji, bez ponownego uruchamiania efektów ubocznych.
Efekty uboczne też muszą być idempotentne. Sama deduplikacja zamówienia nie wystarczy, jeśli rezerwacja stanu, wysyłka maila czy faktura wykonują się przy każdym doręczeniu webhooka. Każdy krok przepływu powinien być bezpieczny do powtórzenia: rezerwacja sprawdza, czy już istnieje; faktura sprawdza, czy już wystawiona.
Dziennik zdarzeń. Każde doręczenie webhooka i każda próba zapisu trafiają do logu z wynikiem (created, updated, duplicate_ignored). Bez tego nie da się odpowiedzieć na pytanie „dlaczego to zamówienie istnieje dwa razy" — a to pytanie wraca przy każdym incydencie.
Kolejka z ponowieniami — jak ponawiać, żeby nie dublować
Kolejka z ponowieniami (retry) jest niezbędna — bez niej każdy błąd przejściowy API oznacza zgubione zamówienie. Ale kolejka bez idempotencji to fabryka duplikatów. Zasady, które działają razem:
- Retry z wykładniczym opóźnieniem (np. 1 s, 5 s, 30 s, 5 min) zamiast natychmiastowych powtórzeń — daje API czas na regenerację i zmniejsza szansę na częściowe sukcesy.
- Idempotencja po stronie odbiorcy — niezależnie od tego, ile razy zadanie wróci z kolejki, efekt końcowy jest jeden.
- Limity tempa (rate limiting) — kolejka respektuje limity API, zamiast bombardować je równoległymi żądaniami i prowokować timeouty, które same generują retry.
- Dead letter queue — zadania, które nie powiodły się po N próbach, trafiają do osobnej kolejki do ręcznego przeglądu, zamiast ginąć albo zapętlać się w nieskończoność.
Webhooki: „at-least-once" znaczy „spodziewaj się duplikatów"
Warto powiedzieć to wprost: webhooki są dostarczane co najmniej raz, a nie dokładnie raz. Każdy poważny dostawca (marketplace, operator płatności, kurier) może dostarczyć to samo zdarzenie dwa razy — przy retry po stronie nadawcy, przy problemach sieciowych, przy wdrożeniu nowej wersji endpointu. To nie błąd dostawcy, to natura protokołu.
Konsekwencja projektowa: endpoint przyjmujący webhooki musi być idempotentny z założenia. Minimalny zestaw praktyk
- Weryfikacja podpisu (HMAC) — odrzuca żądania spoza dostawcy.
- Klucz zdarzenia (event ID) — deduplikacja doręczeń tego samego zdarzenia.
- Szybka odpowiedź 2xx i przetwarzanie asynchroniczne w kolejce — marketplace nie czeka na zakończenie całego przepływu.
- Dziennik doręczeń — możliwość odtworzenia, co i kiedy przyszło.
Jak wykrywać duplikaty, które już są w systemie
Jeśli duplikaty występują w Twoim systemie od miesięcy, przed wdrożeniem idempotencji trzeba je znaleźć. Dwie techniki
Raport duplikatów. Zapytanie grupujące zamówienia po identyfikatorze zewnętrznym i kanale — grupy z liczbą większą niż 1 to duplikaty. Zwykle ujawnia też wzorzec: duplikaty w konkretnym oknie czasowym (awaria API) albo na konkretnym kanale (wadliwa subskrypcja webhooków).
Audyt efektów ubocznych. Duplikaty zamówień często ciągną za sobą duplikaty faktur, rezerwacji i płatności. Przed naprawą przepływu warto zinwentaryzować skutki: które faktury wystawiono podwójnie, które stany zarezerwowano nadmiarowo.
Naprawa danych historycznych to osobny etap — idempotencja zatrzymuje powstawanie nowych duplikatów, ale nie usuwa starych.
Ile kosztuje naprawa przepływu z duplikatami
Przebudowa przepływu z idempotencją, kolejką z ponowieniami i dziennikiem zdarzeń to zwykle 2–4 tygodnie pracy, zależnie od liczby kanałów i stanu obecnej integracji. Koszt: 4 000–15 000 zł netto dla typowego sklepu wielokanałowego. Jeśli duplikaty występują w systemie od dawna, doliczyć trzeba audyt i naprawę danych historycznych.
Dla porównania — koszt jednego duplikatu: podwójna wysyłka, zwrot, obsługa klienta, korekta faktury i rezerwacji. Przy kilkudziesięciu duplikatach miesięcznie mówimy o koszcie rzędu etatu.
Case study: zero duplikatów po przebudowie przepływu
W case study integracji sprzedaży omnichannel opisujemy sklep, w którym duplikaty zamówień występowały regularnie — zespół sprawdzał każde zamówienie ręcznie w dwóch systemach. Po wdrożeniu klucza idempotencji opartego na identyfikatorze z marketplace’u i kontroli przed zapisem duplikaty spadły do zera — nie przez filtrowanie, ale przez uniemożliwienie ich powstania. Ręczna weryfikacja zamówień zniknęła, a czas obsługi zamówienia spadł o 40%.
Jeśli Twoja integracja dubluje zamówienia, zobacz usługę integracje e-commerce i API — przebudowę przepływu zaczynamy od audytu, nie od przepisywania wszystkiego.
Najczęściej zadawane pytania
Jak uniknąć duplikatów zamówień przy integracji z marketplace?
- Kluczem jest idempotencja: każde zamówienie z marketplace’u niesie unikalny identyfikator, który zapisujesz razem z zamówieniem (wraz z kanałem). Przed utworzeniem zamówienia system sprawdza, czy taki identyfikator już istnieje — jeśli tak, aktualizuje rekord zamiast tworzyć nowy. Kontrolę zabezpiecza unikalny indeks w bazie, żeby dwa równoległe procesy nie mogły utworzyć dwóch zamówień.
Dlaczego webhooki tworzą duplikaty zamówień?
Webhooki są dostarczane „co najmniej raz" — dostawca może wysłać to samo zdarzenie dwa razy przy problemach sieciowych albo retry po swojej stronie. To normalne zachowanie protokołu, nie błąd. Dlatego endpoint przyjmujący webhooki musi być idempotentny: weryfikować podpis, deduplikować zdarzenia po identyfikatorze i przetwarzać je asynchronicznie w kolejce.
Czy filtrowanie duplikatów po fakcie wystarczy?
Nie. Filtrowanie leczy objaw, nie przyczynę — duplikaty nadal powstają i generują efekty uboczne: podwójne rezerwacje stanu, faktury, maile do klienta. Duplikaty trzeba wyeliminować u źródła, na poziomie klucza idempotencji, a dodatkowo uczynić idempotentnymi wszystkie efekty uboczne przepływu.
Jak wykryć duplikaty zamówień, które już są w systemie?
Zapytanie grupujące zamówienia po identyfikatorze zewnętrznym i kanale — grupy z więcej niż jednym rekordem to duplikaty. Warto też zinwentaryzować skutki: podwójne faktury, rezerwacje i płatności. Naprawa danych historycznych to osobny etap po wdrożeniu idempotencji.
Ile trwa naprawa integracji dublującej zamówienia?
Przebudowa przepływu z idempotencją, kolejką z ponowieniami i dziennikiem zdarzeń to zwykle 2–4 tygodnie, zależnie od liczby kanałów i stanu obecnej integracji. Jeśli system zbierał duplikaty od miesięcy, doliczyć trzeba czas na audyt i naprawę danych historycznych.
Czy idempotencja wystarczy, żeby naprawić rozjeżdżające się stany magazynowe?
Idempotencja rozwiązuje duplikaty zamówień, ale rozjazdy stanów mają dodatkowe przyczyny: brak bufora bezpieczeństwa, opóźnioną synchronizację i ręczne edycje poza integracją. Pełny zestaw to idempotencja zamówień, jedno źródło prawdy dla stanów, bufory per produkt i rekoncyliacja z alertami.
Źródła i lektury
- Case study: integracja sprzedaży omnichannel — zero duplikatów po wdrożeniu idempotencji
- Integracje API i webhooki — jak projektować odporne procesy — retry, podpisy HMAC, kolejki
- Synchronizacja stanów magazynowych między kanałami — bufory i rekoncyliacja
- Integracja omnichannel — co to jest i jak wdrożyć — przewodnik po modelu
- Automatyzacja procesów sprzedażowych z API Baselinker — praktyka integracji z hubem