Zdarzenie purchase nie dochodzi, a przychód w GA4 nie zgadza się z tym, co widzisz w PrestaShop? Najczęściej problem nie leży w samym GA4, tylko w tym, że zdarzenia wysyła więcej niż jedno miejsce albo wysyła je miejsce, które nie ma dostępu do danych zamówienia. Ta część artykułu jest organizacyjna: dostajesz listę typowych błędów wraz ze sposobem ich wykrycia, checklistę wdrożeniową i pytania, które najczęściej padają przy audytach. Część techniczna — mapa zdarzeń z przypisaniem do hooków i poprawki w kontrolerze zamówienia — jest w dalszych sekcjach. Jeśli po drodze okaże się, że problem jest szerszy niż sam tag, punktem wyjścia mogą być integracje PrestaShop i dokumentacja deweloperska PrestaShop.
Zanim zaczniesz grzebać w szablonie, zrób autodiagnozę. Poniższe pięć objawów pokrywa większość przypadków, z którymi mierzymy się przy audytach sklepów na PrestaShop.
| Objaw | Najbardziej prawdopodobna przyczyna | Gdzie szukać poprawki |
|---|---|---|
| Brak purchase mimo zamówień | Brak danych zamówienia w momencie odpalenia tagu lub przekierowanie po płatności | Mapa zdarzeń — purchase |
| Przychód w GA4 wyższy niż w PrestaShop | Podwójne purchase (moduł + GTM) albo zamówienia anulowane wliczone w value | Konflikt implementacji: jedno źródło prawdy |
| Brak add_to_cart przy koszyku AJAX | Brak nasłuchu na odpowiedź kontrolera koszyka — tag tylko na page_view | Mapa zdarzeń — add_to_cart |
| (not set) w item_id | Brak reference i id_product_attribute w tablicy items | Mapa zdarzeń — struktura items |
| page_view liczone podwójnie | GTM i moduł Google Analytics aktywne jednocześnie | Konflikt implementacji: jedno źródło prawdy |
Zasada jest jedna: zdarzenia wysyła dokładnie jedno miejsce. GTM może być warstwą pośrednią — zbiera dataLayer i wysyła dalej — ale nie drugim nadawcą obok modułu.
Jak znaleźć pozostałości starych wdrożeń:
Trzy warianty wdrożenia i ich konsekwencje:
Pułapka, o której mało kto pamięta: moduł Google Analytics po aktualizacji PrestaShop potrafi włączyć się ponownie i wysyłać page_view niezależnie od GTM. Po każdej aktualizacji sprawdź jego status — PrestaShop 9.2: co sprawdzić przed aktualizacją sklepu to dobre miejsce, żeby zacząć.
Poniższa tabela to punkt odniesienia do weryfikacji wdrożenia. Nazwy hooków i kontrolerów pochodzą z dokumentacji dla deweloperów PrestaShop — warto sprawdzić je w wersji, na której stoi Twój sklep, bo rejestr hooków zmienia się między 1.7, 8 i 9.
| Zdarzenie GA4 | Miejsce w PrestaShop | Uwagi wdrożeniowe |
|---|---|---|
| view_item_list | CategoryController, kontroler wyszukiwania, nowości; hook displayHeader | Raz na widok listy, paginacja bez duplikatów |
| select_item | Klik w kafelek produktu (JS, delegacja zdarzeń) | Nie licz jako view_item |
| view_item | ProductController, hook displayProductAdditionalInfo | Cena z aktualnej kombinacji, nie z produktu bazowego |
| add_to_cart | Kontroler koszyka (AJAX) lub hook actionCartSave | Nasłuch na odpowiedź JSON, nie na page_view |
| remove_from_cart | Akcja usuwania w kontrolerze koszyka (AJAX) | Również przy zmianie liczby sztuk na 0 |
| begin_checkout | OrderController, wejście na pierwszy krok | Jedno zdarzenie na sesję checkout |
| add_shipping_info | OrderController, zmiana opcji dostawy (JS) | W checkoutcie 1.7/8 nie ma osobnego kontrolera — tylko JS |
| add_payment_info | OrderController, zmiana opcji płatności (JS) | Jak wyżej |
| purchase | OrderConfirmationController oraz hook displayOrderConfirmation | Blokada po id_order — odświeżenie strony zdubluje transakcję |
Zacznij od rozdzielenia dwóch problemów: zawyżony przychód i brakujące zamówienia mają inne przyczyny. Mieszanie ich w jednym audycie kończy się podwójnym poprawianiem tego samego miejsca.
Duplikaty. Strona potwierdzenia zamówienia to zwykły adres GET z parametrami id_order i key. Klient odświeża ją (F5), wraca przyciskiem „wstecz” z bramki płatności, klika link z maila potwierdzającego, a przeglądarka po restarcie przywraca kartę. Każde z tych zdarzeń uruchamia skrypt purchase od nowa — jeden klient potrafi wygenerować 2–4 transakcje.
Nie zakładaj, że GA4 odrzuci powtórzone zdarzenie po transaction_id. Zabezpieczenie musi być po Twojej stronie: flaga w localStorage lub sessionStorage (klucz typu dd_purchase_ + ID zamówienia) sprawdzana przed każdą wysyłką, albo znacznik w bazie ustawiany przez kontroler. localStorage trzyma dłużej niż sesja, więc wyłapuje też powrót z maila następnego dnia.
Bramki płatności. Przelewy24, PayU i Autopay mają w panelu adres powrotu. Jeśli wskazuje stronę potwierdzenia — dostajesz drugi purchase. Jeśli osobną stronę „dziękujemy” bez kodu trackingowego — nie dostajesz żadnego. Sprawdź w panelu bramki, gdzie realnie wraca klient, i porównaj z tym, co zakłada moduł płatności.
Backstop. Zamówienia, które wypadły (klient zamknął kartę, adblock), dosyłaj przez Measurement Protocol po zmianie statusu na „Płatność zaakceptowana”. Potrzebujesz api_secret, client_id z cookie _ga i tego samego transaction_id. Wysyłka serwerowa jest odporna na blokery skryptów.
Jeśli po audycie okaże się, że purchase odpala kilka modułów naraz, uporządkowanie tego to zwykle część integracji PrestaShop, a nie sama poprawka w szablonie.
Porównanie liczb. Zakres dat 1:1, ta sama strefa czasowa po obu stronach, wykluczone statusy: oczekiwanie na płatność przelewem, anulowane, zwrot. Tolerancja 2–5% jest normalna; powyżej 10% szukaj błędu w skrypcie.
| Objaw | Prawdopodobna przyczyna | Zabezpieczenie |
|---|---|---|
| Przychód w GA4 wyższy niż w PrestaShop | odświeżenie lub powrót na order-confirmation | flaga w localStorage po transaction_id |
| Dokładnie 2× purchase na zamówienie | powrót z bramki na ten sam URL | jeden punkt wysyłki, reszta wyłączona |
| Zamówienia widoczne w sklepie, brak w GA4 | powrót z bramki na stronę bez kodu trackingowego | Measurement Protocol po zmianie statusu |
| Brak purchase tylko przy przelewach | status „oczekiwanie na płatność” bez wysyłki | backstop serwerowy po akceptacji płatności |
Ten sam plik JS nie zachowa się identycznie na 1.7, 8 i 9 — i nie chodzi tylko o wersję PHP.
Koszyk AJAX. Nie podpinaj wysyłki pod klik w przycisk „Do kosza”. PrestaShop emituje zdarzenia na obiekcie prestashop: prestashop.on('updateCart', function(params){...}) odpala się po dodaniu, zmianie ilości i usunięciu — niezależnie od tego, czy klient kliknął na karcie produktu, w quick view czy w mini-koszyku. W motywie classic nasłuch siedzi w skryptach motywu. Hummingbird ma własne pliki, częściowo bez jQuery: zanim wkleisz stary kod, sprawdź w konsoli, czy zdarzenie faktycznie leci.
Szablony i hooki. Plik potwierdzenia to themes/<motyw>/templates/checkout/order-confirmation.tpl. Hook displayOrderConfirmation dostaje obiekt zamówienia w $params['order'] i działa niezależnie od motywu — to najbezpieczniejsza droga. Część hooków z 1.7 została wycofana lub zmieniła sygnaturę (m.in. displayOrderConfirmation2).
Przed aktualizacją do 9.x. Sprawdź trzy rzeczy: czy moduł analityczny nie został wyłączony jako niekompatybilny, czy snippet GTM nie był wstrzykiwany przez moduł, który po upgrade przestanie działać, oraz czy nowy motyw nie nadpisał customowego head.tpl. Po aktualizacji przejdź całą ścieżkę zakupu na stagingu i porównaj z DebugView. Zanim wejdziesz na produkcję, przejrzyj co sprawdzić przed aktualizacją do PrestaShop 9.2.
Override czy hook. Override szablonu lub klasy kontrolera stosuj tylko wtedy, gdy hook nie ma dostępu do danych, których potrzebujesz. Override w /override/ nie jest przenoszony automatycznie i po aktualizacji potrafi wywalić błąd 500, bo zmieniła się sygnatura metody.
| Wersja / motyw | Gdzie szukać emisji updateCart | Gdzie wstawić purchase | Na co uważać |
|---|---|---|---|
| 1.7.x, classic | skrypty motywu, prestashop.emit('updateCart') | hook displayOrderConfirmation | zdarzenie leci też przy zmianie ilości w koszyku |
| 8.x, classic | jak w 1.7 | hook displayOrderConfirmation | moduły analityczne z 1.7 wymagają sprawdzenia kompatybilności |
| 8.x / 9.x, hummingbird | własne pliki motywu, część bez jQuery | hook, nie wklejka w .tpl | kod oparty o $ może w ogóle nie wystartować |
| 9.x po aktualizacji | — | — | moduły analityczne wyłączone lub nadpisane, nowy head.tpl |
Consent Mode v2 to cztery sygnały i warto wiedzieć, co który blokuje:
analytics_storage — cookies GA4 (_ga, _ga_XXXXXXX) i wysyłka zdarzeń; bez tego nie masz ani ruchu, ani zakupów.ad_storage — cookies reklamowe Google (Ads, Floodlight); blokuje remarketing i atrybucję kliknięć.ad_user_data — przekazywanie danych użytkownika do Google w celach reklamowych, m.in. enhanced conversions.ad_personalization — personalizacja reklam i listy odbiorców.Kolejność ładowania. Wywołanie gtag('consent','default',{analytics_storage:'denied', ad_storage:'denied', ad_user_data:'denied', ad_personalization:'denied'}) musi wykonać się przed pierwszym hitem i przed wczytaniem GTM. Jeśli banner startuje asynchronicznie i dopiero wtedy ustawia stan, pierwszy page_view albo leci bez zgody, albo przepada. Praktyka: domyślny stan wpisany na sztywno w <head>, banner tylko wywołuje gtag('consent','update',...). Przy wdrożeniach PrestaShop ten fragment często siedzi w module CMP — sprawdź, czy nie ładuje się po tagu Google.
Weryfikacja w DebugView. Wejdź w zakładkę Consent i sprawdź konkretnie purchase: czy w chwili wysyłki analytics_storage ma stan granted. Jeśli update zgody dochodzi po zakupie, zdarzenie poleci jako denied.
Ile tracisz. Podziel liczbę purchase w GA4 przez liczbę zamówień w PrestaShop z tego samego okresu, po wykluczeniu anulowanych. 1000 zamówień i 780 purchase to luka 22% — suma odmów i błędów technicznych. Rozdziel je, mierząc osobno odsetek sesji z odmową.
Modelowanie konwersji. Wymaga ruchu z EEA i progów wolumenowych, których Google nie publikuje. Uzupełnia raporty i konwersje Ads, ale nie naprawi zepsutego skryptu i nie da danych per zamówienie. Przy kilkuset zamówieniach miesięcznie punktem odniesienia powinny być zamówienia z PrestaShop, a GA4 — źródłem trendów.
Testy wykonuj zawsze w tej samej kolejności — przed wdrożeniem i po każdym większym narzucie. Inaczej zostaje Ci „wygląda dobrze”, a nie „działa”.
_dbg=1. Oczekiwana kolejność: view_item_list → view_item → add_to_cart → view_cart → begin_checkout → add_payment_info → purchase. Brak view_item najczęściej oznacza, że motyw nadpisał szablon i zgubił dataLayer.purchase musi mieć transaction_id, value, currency, tax, shipping, coupon oraz tablicę items[] z item_id, item_name, price, quantity (dla wariantów też item_variant). Jeden brakujący item_id i produkt ląduje jako (not set), a przychód nie schodzi się z raportem sprzedaży.event_name = 'purchase' z sumą ecommerce.purchase_revenue daje liczbę bez próbkowania i bez wpływu interfejsu — GA4 w UI potrafi próbkować większe zbiory.| Co porównujesz | Tolerancja | Reakcja przy odchyleniu |
|---|---|---|
| Liczba purchase, 7 dni | ±3% | Sprawdź duplikaty transaction_id i statusy zamówień mapowane jako konwersja |
| Przychód, 7 dni | ±2% | Porównaj value z suma pozycji + shipping − rabat; szukaj brakującego tax/shipping |
| Liczba purchase, 30 dni | ±5% | Uwzględnij zwroty i anulacje — sprawdź, czy nie wpadają jako purchase |
| Przychód, 30 dni | ±5% | Zestaw z eksportem BigQuery, nie z widokiem w UI |
Zakres prac zależy od tego, ile miejsc wysyła zdarzenia i czy purchase dubluje się. Trzy realne scenariusze:
actionCartSave, actionValidateOrder, displayOrderConfirmation) i wystawić view_item_list, view_item, add_to_cart, begin_checkout, purchase. To już praca w module, z kontrolą wersji PrestaShop i PHP.transaction_id, wysyłka serwerowa i testy na zamówieniach historycznych.Własny moduł wygrywa z płatną wtyczką z marketplace w trzech sytuacjach: nie dokładasz kolejnej zależności do sklepu (mniej konfliktów przy aktualizacjach), moduł jest pisany pod Twoją wersję PrestaShop i Twój motyw, a nie „pod najpopularniejszą”, oraz nie płacisz abonamentu rocznego za funkcję, która po wdrożeniu się nie zmienia. Wtyczka ma sens, gdy potrzebujesz wsparcia producenta i szybkiego startu.
Do zlecenia przygotuj: dostęp do kontenera GTM (uprawnienia edytora, nie tylko publikującego), dokładną wersję PrestaShop i PHP, nazwę motywu oraz listę statusów zamówienia z informacją, które są mapowane jako konwersja. Do tego zrzut obecnych tagów i przykładowe transaction_id z ostatnich 7 dni.
Kontekst szerszy: integracje PrestaShop i PrestaShop. Jeśli problem dotyczy całego wdrożenia, a nie tylko pomiaru — zobacz wdrożenia PrestaShop: organizacja, terminy, koszty. Hooki i strukturę modułu opisuje dokumentacja dla deweloperów PrestaShop.
| Scenariusz | Widełki | Co dostajesz |
|---|---|---|
| Korekta w GTM | 4–8 h | Poprawione tagi, triggery, parametry items |
| Moduł z mapą zdarzeń | 12–24 h | Komplet zdarzeń ecommerce z hooków, bez GTM w środku |
| Deduplikacja + Measurement Protocol | 20–40 h | Jedno purchase na zamówienie, także poza przeglądarką |
Dwa źródła zdarzeń działają równolegle: moduł gtag z marketplace i kontener GTM, a do tego stary analytics.js z czasów Universal Analytics.
Jak wykryć: Otwórz źródło strony w przeglądarce i wyszukaj frazy gtag(, gtag/js, analytics.js, GTM-. Jeśli znajdziesz dwa różne identyfikatory strumienia albo dwa wywołania page_view, masz konflikt. W DebugView zobaczysz zdarzenia zdublowane w tej samej sekundzie.
Jak naprawić: Zostaw jedno źródło prawdy. Wyłącz moduł wysyłający gtag albo usuń kontener GTM — ale nie oba naraz. GTM może zostać warstwą pośrednią, pod warunkiem że to on, i tylko on, wysyła zdarzenia do GA4.
Zdarzenie purchase w ogóle nie jest wysyłane, bo implementacja opiera się na szablonie order-confirmation.tpl, którego motyw nie używa albo który został nadpisany przy aktualizacji.
Jak wykryć: Zrób jedno testowe zamówienie w trybie płatności testowej i patrz na GA4 Realtime oraz DebugView. Jeśli nie pojawia się purchase, sprawdź w plikach motywu, czy istnieje plik szablonu strony potwierdzenia i czy zawiera jakiekolwiek wywołanie zdarzenia.
Jak naprawić: Wysyłaj purchase z miejsca, które faktycznie wykonuje się po złożeniu zamówienia — z kontrolera potwierdzenia lub hooka wywoływanego na stronie potwierdzenia, a nie z szablonu, który może być nadpisany. Potwierdź to testem, nie założeniem.
Brak add_to_cart przy koszyku AJAX. Zdarzenie jest podpięte pod przeładowanie strony, a koszyk dodaje produkt bez przeładowania.
Jak wykryć: Kliknij „dodaj do koszyka” i jednocześnie patrz w DebugView. Jeśli strona się nie przeładowuje i nie pojawia się żadne zdarzenie, problem jest potwierdzony.
Jak naprawić: Podepnij zdarzenie pod odpowiedź serwera na żądanie dodania do koszyka (callback po sukcesie) albo pod zdarzenie frontowe emitowane przez motyw. Przetestuj również dodanie z listy produktów i ze strony produktu — to dwa różne miejsca.
(not set) w item_id i zlewanie się wariantów w raportach item, bo w items przekazywane jest samo id_product bez kombinacji.
Jak wykryć: Wejdź w raporty e-commerce GA4, do sekcji pozycji, i sprawdź, czy item_id ma wartość (not set), a produkty z wariantami występują jako jedna pozycja o sumarycznej sprzedaży.
Jak naprawić: Przekazuj item_id jako reference produktu, a dla wariantu dołóż id_product_attribute. Ustal jedną konwencję (np. reference-id_product_attribute) i trzymaj się jej we wszystkich zdarzeniach, nie tylko w purchase.
Zawyżony przychód przez wielokrotny purchase: klient odświeża stronę potwierdzenia, wraca z historii albo otwiera ją z linku w mailu i skrypt wysyła zdarzenie ponownie.
Jak wykryć: Porównaj liczbę purchase w GA4 z liczbą zamówień w PrestaShop w tym samym zakresie dat. Jeśli w GA4 jest ich więcej, sprawdź, czy powtarzają się te same transaction_id.
Jak naprawić: GA4 nie odrzuci drugiego zdarzenia z tym samym transaction_id, więc zabezpieczenie musi być po Twojej stronie: flaga w sessionStorage lub localStorage blokująca ponowną wysyłkę, a przy dużej skali — zapis po stronie serwera. Jako backstop uruchom uzupełnianie brakujących zakupów przez Measurement Protocol po zmianie statusu zamówienia.
Niespójna wartość zamówienia: GA4 dostaje cenę netto bez dostawy, a w PrestaShop porównujesz total_paid brutto z kosztem przesyłki. Albo waluta jest przekazywana tylko w purchase, a w pozostałych zdarzeniach jej brak.
Jak wykryć: Zestaw revenue z GA4 z polem total_paid z PrestaShop dla tych samych, kilku konkretnych zamówień. Różnica stała i powtarzalna oznacza różnicę definicji, nie błąd tagu.
Jak naprawić: Ustal jedną definicję wartości (najczęściej brutto z dostawą) i stosuj ją we wszystkich zdarzeniach oraz w porównaniach raportowych. Walutę przekazuj w każdym zdarzeniu e-commerce, nie tylko w purchase.
Prawidłowa integracja PrestaShop z GA4 to przede wszystkim jedno źródło zdarzeń i spójna definicja wartości zamówienia. Objawy, które widzisz w raportach — brak purchase, zawyżony przychód, (not set) w item_id — prawie zawsze prowadzą do jednego z dwóch miejsc: konfliktu implementacji albo zdarzenia wysyłanego z miejsca bez dostępu do danych. Zacznij od testowego zamówienia i DebugView, a nie od przebudowy całego sklepu. Checklistę przechodź punkt po punkcie, bo każdy pominięty element wraca w postaci rozjazdu liczb.
Nie. GA4 nie odrzuca drugiego zdarzenia zakupu z tym samym transaction_id, więc duplikat po prostu zawyża przychód i liczbę transakcji. Zabezpieczenie musi być po stronie skryptu wysyłającego — na przykład flaga w sessionStorage lub localStorage — albo po stronie serwera.
Technicznie tak, praktycznie prawie zawsze kończy się to duplikatami page_view i rozjazdem atrybucji. Jeśli GTM ma być warstwą pośrednią, musi być jedynym miejscem, które wysyła zdarzenia do GA4. Moduł gtag wtedy wyłączasz albo ograniczasz do minimum i sprawdzasz w DebugView, czy faktycznie nic nie wysyła.
Najczęstsze powody to brak zdarzenia purchase na stronie potwierdzenia, blokada skryptów przez przeglądarkę lub rozszerzenie, odrzucenie zgody w banerze oraz zamówienia, które nie wróciły z bramki płatności na stronę sklepu. Zanim zaczniesz zmieniać kod, porównaj liczby w tym samym zakresie dat i po wykluczeniu statusów anulowanych.
Czasem wystarczy, ale trzeba sprawdzić dwie rzeczy: czy wysyła wszystkie potrzebne zdarzenia e-commerce, a nie tylko page_view, oraz czy po aktualizacji PrestaShop nie zaczęła wysyłać zdarzeń równolegle do GTM. Moduł, który działał pół roku temu, po aktualizacji może zachowywać się inaczej — dlatego test po każdej aktualizacji nie jest przesadą.
Użyj DebugView i Realtime, a testy rób na strumieniu testowym lub z wykluczeniem ruchu wewnętrznego. Zamówienie testowe wykonaj w trybie płatności testowej. Dopiero po takim teście porównaj dane produkcyjne z PrestaShop za zamknięty zakres dat.
To problem implementacji, nie GA4. Najczęściej zdarzenie nie dostaje identyfikatora produktu, bo jest wysyłane z miejsca, które go nie zna, albo wariant jest mapowany na samo id_product bez kombinacji. Poprawka polega na przekazaniu reference oraz id_product_attribute w strukturze items.
Tak — przez Measurement Protocol, uruchamiany po zmianie statusu zamówienia na taki, który oznacza potwierdzoną płatność. To backstop, nie zamiennik poprawnego zdarzenia po stronie sklepu: bez deduplikacji i spójnej definicji wartości dosyłanie tylko powiększy bałagan w raportach.
Jeśli po przejściu checklisty nadal nie wiesz, gdzie ginie purchase albo skąd bierze się nadwyżka przychodu, napisz do nas — przy audycie zwykle wystarczy dostęp do back office i jeden testowy koszyk. Zanim cokolwiek zmienimy, pokażemy Ci, które miejsce wysyła zdarzenia i co konkretnie trzeba wyłączyć.