Wróć do bloga

Headless WooCommerce: sprzedaż kuponów między sklepami

Sprzedaż kuponów w headless WooCommerce obejmuje dwa sklepy i zawodną sieć. Decyzje, dzięki którym realizację można bezpiecznie ponawiać po płatności.

Jakub Czechowski

Buduje strony i sklepy internetowe w JC Web Studio, prowadzi StackCompass – publikację o architekturze treści i decyzjach stackowych – i współorganizuje CMS Conf, konferencję o systemach treści.

/ / 4 min czytania

Klient klika „kup”, płaci i oczekuje kuponu. Sklep sprzedażowy oznacza zamówienie jako zrealizowane, ale w sklepie, który weryfikuje kupony, właściwy kupon może jeszcze nie istnieć. Te dwa fakty rozdzielają sieć, dwie bazy danych i krótki okres, w którym strona potwierdzenia wyprzedza rzeczywistość.

Ta luka była właściwym tematem projektu. Sprzedaż kuponów przez WooCommerce jest dobrze rozpoznanym problemem. Realizacja kuponu w drugim sklepie nie przypomina jednak pobrania gotowego pliku, lecz obietnicę między systemami. Przy takiej architekturze ponawianie operacji, stany pośrednie i odzyskiwanie po awarii przestają być opcjonalne.

Rozdzielenie sklepów ogranicza skutki awarii

Domyślnie wszystko powinno działać w jednej instalacji WordPressa: jedna baza danych, jeden checkout i kupony generowane lokalnie. Dla pojedynczego sklepu to zwykle właściwa odpowiedź. Zaakceptowaliśmy koszt rozdzielenia, ponieważ istniejący sklep kuponowy zawierał już kluczową logikę biznesową, dane historyczne i obsługiwał realizację kuponów na żywo.

Dodanie do niego publicznego lejka sprzedażowego oznaczałoby również skoki ruchu z kampanii, pluginy marketingowe i częstsze wdrożenia. Błąd po stronie sprzedaży mógłby wtedy naruszyć system, który wystawia i honoruje kupony.

Rozdzielenie utworzyło dwa niezależne obszary awarii. Sklep sprzedażowy może zostać przebudowany albo czasowo przeciążony bez zatrzymywania realizacji kuponów. Sklep kuponowy zmienia się rzadziej i pozostaje bardziej zachowawczy. To samo rozumowanie stoi za świadomymi decyzjami architektonicznymi w zamkniętym sklepie B2B na WooCommerce: separacja ma sens tylko wtedy, gdy jej korzyści pokrywają koszt operacyjny. Tutaj korzyścią była izolacja ryzyka, nie elegancja.

Kupon powstaje dopiero po płatności

Przy zwykłym pobraniu pliku zasób zazwyczaj istnieje jeszcze przed płatnością. Kupon wystawiany w innym sklepie może wtedy jeszcze nie istnieć. Jego utworzenie wymaga wywołania między systemami, które zwiększa opóźnienie i może się nie udać.

Oczekiwanie na to wywołanie wewnątrz checkoutu uzależniłoby sprzedaż od dostępności sklepu kuponowego. Chwilowy problem z siecią mógłby oznaczać nieudaną transakcję. Wybraliśmy inny model: szybko potwierdzić płatność, utworzyć kupon asynchronicznie i jasno poinformować klienta, że realizacja nadal trwa.

W ten sposób powstaje jawny stan pośredni: zamówienie jest opłacone, ale jeszcze niezrealizowane. To stan oczekiwany, nie błąd. System zapisuje go i pokazuje, dzięki czemu może wznowić pracę zamiast udawać, że sprzedaż i wystawienie kuponu tworzą jedną niepodzielną transakcję.

Astro prezentuje ofertę, WooCommerce obsługuje checkout

Lejek zakupowy służy głównie do odczytu: przeglądania oferty, porównywania kuponów i przejścia do zakupu. Nie potrzebuje pełnego stosu szablonów WordPressa ani zapytania do bazy przy każdym wyświetleniu. Do prezentacji oferty wykorzystaliśmy więc szybki front w Astro połączony z backendem WordPressa, zasilany danymi z REST API WooCommerce.

Była to kolejna świadomie wyznaczona granica, a nie założenie, że wszystko musi działać w trybie headless. Astro odpowiada za statyczną, marketingową część serwisu. WooCommerce nadal obsługuje koszyk i checkout, gdzie ma już gotowe integracje płatności, podatki i model stanów zamówienia. Przepisanie tej ścieżki dodałoby kolejny autorski system w najbardziej wrażliwym miejscu procesu.

Większość listingów była statyczna i odświeżana po zmianie katalogu. Renderowanie dynamiczne zostało ograniczone do danych, które rzeczywiście musiały być aktualne. Dzięki temu cache przyspieszał właściwe strony, a WooCommerce pozostawał tam, gdzie ograniczał ryzyko.

Webhook sygnalizuje zadanie, ale nie potwierdza realizacji

Oczywistą ścieżką powiadomienia jest webhook: zamówienie zostaje opłacone, sklep kuponowy otrzymuje zdarzenie i rozpoczyna realizację. To dobre rozwiązanie, gdy liczy się małe opóźnienie, ale sama dostawa webhooka nie zastępuje trwałej kolejki zadań.

Odbiorca może być przeciążony, niedostępny albo właśnie przechodzić wdrożenie. Nawet jeśli nadawca ponowi żądanie, dostarczenie komunikatu i wykonanie zadania pozostają dwoma różnymi faktami. Zaakceptowane żądanie może zakończyć się awarią przed utworzeniem kuponu. Webhook powinien więc oznaczać „może być praca do wykonania”, a nie „realizacja na pewno się zakończyła”. Zrównanie tych stanów prowadzi do cichego gubienia pojedynczych zamówień.

Ponawianie wymaga idempotencji i uzgadniania stanu

Model niezawodności brzmi: potwierdź szybko, przetwarzaj asynchronicznie i uzgadniaj stan. Endpoint przyjmujący sygnał odpowiada przed wykonaniem pracy między sklepami. Tworzenie kuponu odbywa się w procesie w tle, który może się nie udać i zostać ponowiony, z wykładniczym opóźnieniem i losowym przesunięciem kolejnych prób, aby nie przeciążać odbiorcy.

Ponawianie wymaga idempotencji. Jeśli operacja „utwórz kupon dla tego zamówienia” wykona się dwa razy, powinna zwrócić ten sam wynik, a nie wystawić dwa kupony. Każde zadanie otrzymuje więc stały identyfikator rozpoznawany przez sklep kuponowy. Wywołania między sklepami są również podpisywane i weryfikowane, a klucz idempotencji pozwala bezpiecznie powtórzyć prawidłowe żądanie. Implementacja może się różnić; niezmienna pozostaje zasada, że każdą operację można wykonać ponownie.

Pod ścieżką zdarzeniową działa okresowe uzgadnianie stanu. Proces sprawdza, które opłacone zamówienia nadal nie mają kuponu, i ponownie zleca ich realizację. W ten sposób wychwytuje utracone powiadomienia, zamówienia opłacone podczas wdrożenia oraz awarie po potwierdzeniu żądania. Zdarzenia przyspieszają proces, a uzgadnianie pozwala wykazać, że opłacone zamówienia osiągnęły wymagany stan końcowy.

Najtrudniejsza jest realizacja kuponu między sklepami

Natywny mechanizm WooCommerce wystarczył do generowania kuponów. Praca inżynieryjna polegała na niezawodnym uruchamianiu tej operacji po drugiej stronie granicy systemów.

Lepsze pytanie nie brzmi „jak sprzedawać kupony w WooCommerce?”, lecz „jakie stany mogą wystąpić po płatności i w jaki sposób każdy z nich prowadzi do realizacji?”. Gdy pytanie zostanie postawione wprost, rozdzielenie sklepów, hybrydowy front headless, idempotentne zadania i uzgadnianie stanu tworzą jeden spójny projekt, a nie zbiór przypadkowych integracji.