Server-side tracking 3 stycznia 2026 11 min czytania

shopLayer vs dataLayer Shoper Storefront - czym się różnią

Shoper Storefront ma własną warstwę shopLayer, GTM oczekuje dataLayer. Dlaczego są dwie, jak je przetłumaczyć i co najczęściej się gubi w translacji.

Każdy kto wdrażał tracking na Shoper Storefront prędzej czy później natrafia na pytanie: dlaczego eventy nie dochodzą do GA4 mimo że na stronie jest GTM? Tag Assistant pokazuje że kontener się ładuje, ale view_item, add_to_cart, purchase są puste albo z brakującymi parametrami.

Odpowiedź zwykle leży w dwóch warstwach danych, które Shoper Storefront utrzymuje równolegle: shopLayer (specyficzny dla Shoper) i dataLayer (standard GTM). Nie są to synonimy - mają różne struktury, różne momenty publikacji eventów i różne dane. Bez świadomej translacji między nimi tracking będzie niepełny. To jeden z głównych powodów, dla których standardowe metody wdrażania GTM zawodzą na Storefront.

W tym artykule wyjaśniam czym są obie warstwy, dlaczego Shoper wprowadził własną, co najczęściej się gubi w translacji i jak zrobić to dobrze.

Co to jest dataLayer (standard GTM)

dataLayer to globalny obiekt JavaScript zdefiniowany przez Google Tag Manager. Każdy event push’owany do window.dataLayer jest dostępny dla wszystkich tagów GTM jako trigger lub źródło zmiennych.

Standard dataLayer dla e-commerce wygląda tak:

window.dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'ORD_12345',
    value: 299.99,
    currency: 'PLN',
    tax: 56.13,
    shipping: 15.00,
    coupon: 'WELCOME10',
    items: [
      {
        item_id: 'SKU_001',
        item_name: 'Etui na telefon',
        price: 99.99,
        quantity: 3,
        item_category: 'Akcesoria GSM'
      }
    ]
  }
});

Google Ads, GA4, Meta Pixel - wszystkie tagi w GTM oczekują tej struktury. Jest udokumentowana, wspierana przez wszystkie templates GTM, kompatybilna z każdym sklepem niezależnie od platformy.

Co to jest shopLayer

shopLayer to własna warstwa danych Shoper Storefront, dostępna jako window.shopLayer. Shoper wprowadził ją dla wewnętrznych integracji - przekazywania danych między komponentami Storefront, modułami własnymi i wbudowanymi integracjami marketingowymi.

Struktura shopLayer różni się od dataLayer:

window.shopLayer = {
  order: {
    id: 12345,
    total: 299.99,
    totalNet: 243.86,
    currency: 'PLN',
    tax: 56.13,
    shipping: { price: 15.00, name: 'InPost Paczkomaty' },
    coupon: { code: 'WELCOME10', value: 30.00 },
    products: [
      {
        id: 1001,
        sku: 'SKU_001',
        name: 'Etui na telefon',
        price: 99.99,
        priceNet: 81.29,
        quantity: 3,
        category: { id: 5, name: 'Akcesoria GSM', breadcrumb: 'GSM / Akcesoria' }
      }
    ]
  },
  customer: { ... },
  cart: { ... }
};

Różnice strukturalne:

  • order zamiast transaction_id na poziomie root
  • total zamiast value
  • products zamiast items + inne nazwy pól produktów
  • Brak event flagi - shopLayer to obiekt stanu, nie strumień zdarzeń
  • Dodatkowe pola typu totalNet, priceNet, breadcrumb kategorii

shopLayer jest aktualizowany przy każdej zmianie stanu sklepu (dodanie do koszyka, zakup, zmiana ilości). GTM nie wie automatycznie kiedy się zmienił - to nie strumień eventów, tylko obiekt mutowalny.

Dlaczego Shoper ma dwie warstwy

Decyzja architektoniczna Shopera:

  • shopLayer - źródło prawdy o stanie sklepu dla wewnętrznych potrzeb. Strukturalnie zgodne z modelem danych Shopera (jak wygląda zamówienie w panelu administracyjnym).
  • dataLayer - warstwa kompatybilna z GTM, generowana z shopLayer dla potrzeb integracji zewnętrznych.

Problem polega na tym, że automatyczna translacja shopLayerdataLayer w Shoper Storefront ma kilka miejsc, gdzie dane się gubią albo zostają w niewłaściwym formacie.

Co najczęściej się gubi w translacji

1. Wartość zamówienia brutto vs netto. shopLayer rozróżnia total (brutto) i totalNet (netto). Standardowa translacja do dataLayer.value używa total - co jest poprawne dla Google Ads i Meta (te oczekują brutto). Ale jeśli sklep B2B operuje w cenach netto, panel sklepu i kampanie reklamowe pokazują różne wartości.

Fix: świadoma decyzja czy value ma być brutto czy netto, konsekwentnie w całym wdrożeniu.

2. Identyfikator produktu - id vs sku. shopLayer.products[].id to wewnętrzny ID Shopera (liczba, np. 1001). shopLayer.products[].sku to SKU widoczny w panelu (string, np. “ETUI-IPH15-BL”).

Google Merchant Center i Meta Catalog wymagają SKU. Standardowa translacja do dataLayer często używa id zamiast sku - co psuje dynamic remarketing (Meta nie potrafi powiązać item z katalogiem).

Fix: w GTM zmapuj item_id z shopLayer.products[].sku, nie z id.

3. Kategorie produktów - flat vs breadcrumb. Standard GTM oczekuje:

item_category: 'GSM'
item_category2: 'Akcesoria'
item_category3: 'Etui'

shopLayer zwykle daje:

category: { id, name: 'Etui', breadcrumb: 'GSM / Akcesoria / Etui' }

Tłumaczenie wymaga parsowania breadcrumb i rozdzielenia na poziomy. Bez tego raporty kategorii w GA4 są płaskie - widzisz tylko najniższy poziom.

4. Stany koszyka - cart vs order. shopLayer.cart to aktywny koszyk (przed zakupem). shopLayer.order powstaje po finalizacji zakupu. W momencie purchase event w dataLayer powinien zawierać dane z order (final state), ale niektóre wtyczki czytają z cart (stan przed potwierdzeniem płatności).

Skutek: jeśli klient zmodyfikował koszyk w ostatnim kroku (zmienił ilość, dodał kupon), dataLayer.purchase ma stare dane z cart, nie nowe z order.

5. Brak event flag dla view_item. shopLayer jest aktualizowany przy wejściu na kartę produktu, ale nie pchnie automatycznie eventu view_item do dataLayer. Trzeba to zrobić ręcznie - przez moduł własny JS, który nasłuchuje na zmiany routingu Storefront i pcha event.

Jak zrobić poprawną translację

Krok 1. Nie polegaj na automatycznej translacji Shopera. Standardowe integracje GTM/GA4 w panelu Storefront dają podstawowy dataLayer, ale rzadko pełen.

Krok 2. Wdroż moduł własny JS (wymaga Shoper Premium) który nasłuchuje na zmiany shopLayer i pcha eventy view_item, add_to_cart, view_cart, begin_checkout, purchase do dataLayer w standardzie GTM.

Krok 3. Mapuj pola explicit:

// Z shopLayer do dataLayer.purchase
function shopLayerToPurchaseEvent() {
  var order = window.shopLayer.order;
  if (!order) return null;
  
  return {
    event: 'purchase',
    ecommerce: {
      transaction_id: 'ORD_' + order.id,
      value: order.total,
      currency: order.currency,
      tax: order.tax,
      shipping: order.shipping.price,
      coupon: order.coupon ? order.coupon.code : undefined,
      items: order.products.map(function(p) {
        var breadcrumb = p.category.breadcrumb.split(' / ');
        return {
          item_id: p.sku,  // SKU, nie id
          item_name: p.name,
          price: p.price,
          quantity: p.quantity,
          item_category: breadcrumb[0],
          item_category2: breadcrumb[1],
          item_category3: breadcrumb[2]
        };
      })
    }
  };
}

Krok 4. Pchaj event w odpowiednim momencie. Dla purchase - po finalizacji, nie przy renderowaniu thank-you page. Najlepiej z webhook backendu albo z modułu własnego który nasłuchuje na flag order_finalized.

Krok 5. Walidacja w GTM Preview Mode:

  • Otwórz Preview, zrób testowy zakup
  • Sprawdź zakładkę “Data Layer” - czy event purchase ma wszystkie pola
  • Porównaj z window.shopLayer.order w konsoli przeglądarki - czy translacja zachowała wszystkie istotne dane

Pułapka: cache pomiędzy Storefront a tagami

shopLayer jest cache’owany przez Shoper Storefront między widokami SPA. Jeśli użytkownik:

  • doda produkt do koszyka
  • przejdzie do checkout
  • wróci do strony produktu

Wartość shopLayer.cart może być przestarzała dla nowej karty produktu (pokazuje koszyk z poprzedniego widoku). Tag GTM odczytujący z cache’owanego shopLayer wyśle nieaktualne dane.

Fix: w module własnym JS subskrybuj na natywne eventy Shopera (storefront:cart:update, storefront:product:view lub odpowiedniki dla konkretnej wersji) - one trigerują przeczytanie świeżego stanu, nie cache.

Najczęstsze błędy konfiguracji

1. Mapowanie id zamiast sku jako item_id. Łamie dynamic remarketing Google i Meta. Naprawiasz przez explicit mapowanie w module własnym.

2. Brak handlera dla view_item. Karty produktu nie wysyłają tego eventu, GA4 widzi tylko page_view bez parametrów produktu. Brak danych do segmentacji “produkty oglądane ale nie kupione”.

3. Purchase z cart, nie z order. W momencie purchase tag czyta z shopLayer.cart zamiast shopLayer.order. Wartość jest poprawna w 95% przypadków, ale w 5% (modyfikacja koszyka w ostatnim kroku) - rozjazd z panelem sklepu.

4. Nieparserowane kategorie. item_category = 'GSM / Akcesoria / Etui' (string z slashami) zamiast trzech osobnych pól. GA4 traktuje to jako jedną kategorię, raporty kategorii się nie agregują.

5. Reload thank-you page wysyła duplikat. Bo shopLayer.order nadal jest w stanie po zakupie. Każde renderowanie strony pcha event purchase ponownie. Rozwiązanie: idempotencja przez transaction_id (opisane w artykule o duplikacji).

6. Brak Consent Mode v2 dla zdarzeń e-commerce. shopLayer pcha eventy niezależnie od stanu consent użytkownika. Bez warstwy filtrującej w GTM (sprawdzającej Consent Mode v2 przed odpaleniem tagów Google/Meta) sklep łamie wymogi RODO i traci modelowanie konwersji. Pełna konfiguracja: Consent Mode v2 + Cookiebot + sGTM.

Podsumowanie

shopLayer i dataLayer to dwie różne warstwy danych w Shoper Storefront - jedna dla wewnętrznych potrzeb platformy, druga dla GTM. Automatyczna translacja między nimi gubi po drodze SKU (zamienia na ID), breadcrumb kategorii (zostaje jako string), brutto/netto, czasem nawet finalne dane zamówienia.

Dla sklepu kierującego do Google i Meta przez GTM, świadoma translacja przez moduł własny JS jest standardem. Bez niej view_item nie strzela, dynamic remarketing nie łączy produktów z katalogiem, raporty kategorii w GA4 są płaskie.

Standardowe integracje Storefront pokazują że “GTM działa”. Ale czy dataLayer ma wszystkie potrzebne pola w standardzie Google - to już osobne pytanie, na które warto odpowiedzieć w GTM Preview Mode.

Wdrożenie poprawnej translacji shopLayer → dataLayer jest częścią pakietu Server-Side Pro Shoper. Dla sklepów bez Storefront (klasyczny RWD) problem ten nie istnieje - tam edytujesz szablon i pchasz event bezpośrednio do dataLayer.

Porozmawiajmy o Twoim trackingu

30 minut bez zobowiązań. Bez sesji sprzedażowej. Powiem czy mogę pomóc i co realnie da się odzyskać.