# DataFirefly Push Pro: kompletny przewodnik

> DataFirefly Push Pro wnosi natywne powiadomienia web push do PrestaShop 8 i 9 bez żadnej usługi zewnętrznej typu OneSignal i bez miesięcznego abonamentu. Ten przewodnik obejmuje instalację, konfigurację początkową, dziewięć…

- Strona: <https://www.datafirefly.com/pl/documentation/dfpushnotifications/>
- Język: pl
- Zaktualizowano: 2026-08-01
- Inne języki: [fr](https://www.datafirefly.com/documentation/dfpushnotifications/index.md), [en](https://www.datafirefly.com/en/documentation/dfpushnotifications/index.md), [es](https://www.datafirefly.com/es/documentation/dfpushnotifications/index.md), [de](https://www.datafirefly.com/de/documentation/dfpushnotifications/index.md), [it](https://www.datafirefly.com/it/documentation/dfpushnotifications/index.md), [nl](https://www.datafirefly.com/nl/documentation/dfpushnotifications/index.md), [pt](https://www.datafirefly.com/pt/documentation/dfpushnotifications/index.md)
- Indeks: <https://www.datafirefly.com/pl/documentation/llms.txt>

DataFirefly Push Pro wnosi natywne powiadomienia web push do PrestaShop 8 i 9 bez żadnej usługi zewnętrznej typu OneSignal i bez miesięcznego abonamentu. Ten przewodnik obejmuje instalację, konfigurację początkową, dziewięć automatyzacji, builder kampanii, segmentację, testy A/B, atrybucję przychodu, topics, inbox, webhooki oraz rozwiązywanie problemów.

## 1. Prezentacja i przypadki użycia

Moduł natywnie implementuje protokół Web Push VAPID (RFC 8292) z szyfrowaniem aes128gcm po stronie serwera. Subskrypcje są zapisywane w Twojej bazie PrestaShop, wysyłki wychodzą bezpośrednio z Twojego hostingu, żadne dane nie przechodzą przez usługę zewnętrzną.

Typowe przypadki użycia:

- **Odzyskiwanie porzuconych koszyków**: trzy rozłożone przypomnienia (1 h, 24 h, 72 h) bez potrzeby znajomości e-maila odwiedzającego.
- **Alerty produktowe**: powrót do magazynu, spadek ceny, opt-in bezpośrednio na karcie produktu.
- **Transakcyjne**: potwierdzenie zamówienia, wysyłka, prośba o opinię po dostawie.
- **Reaktywacja**: urodziny, nieaktywni klienci, digest nowych produktów.
- **Kampanie jednorazowe**: wyprzedaże, premiery, Black Friday, z segmentacją i testami A/B.

## 2. Wymagania

- PrestaShop 8.0 do 9.x
- PHP minimum 7.4, zalecane 8.x
- MySQL minimum 5.7 lub MariaDB 10.3
- **HTTPS obowiązkowy**: przeglądarkowe API Web Push działa wyłącznie na bezpiecznym origin. Jedynym wyjątkiem jest `localhost` na potrzeby testów deweloperskich.
- Zewnętrzny cron zdolny wywołać adres HTTP co 5 minut (cron systemowy Linux, zadania zaplanowane u hostingodawcy, natywny cron PrestaShop i podobne)
- Włączony OpenSSL w PHP (używany do generowania kluczy VAPID i szyfrowania aes128gcm)

**Dlaczego HTTPS?** Wszystkie przeglądarki (Chrome, Firefox, Safari, Edge) odmawiają rejestracji service workera i przyznania uprawnienia do powiadomień na stronie HTTP. To ograniczenie przeglądarek, nie modułu.

## 3. Instalacja

1. Pobierz plik ZIP `dfpushnotifications-v1.2.0-phase3.zip` ze swojego konta klienta DataFirefly.
2. Zaplecze PrestaShop > Moduły > Menedżer modułów > **Wgraj moduł** > wskaż plik ZIP.
3. Kliknij **Zainstaluj**. Instalacja tworzy 14 tabel z prefiksem `ps_dfpush_*`, generuje unikalny token crona i rejestruje zakładki zaplecza.
4. Pojawia się nowe menu: **Ulepszenia > DataFirefly Push** z 8 zakładkami (Dashboard, Campaigns, Automations, Queue, Subscribers, Topics, Webhooks, Settings).

## 4. Generowanie kluczy VAPID

Klucze VAPID identyfikują Twój serwer wobec usług push przeglądarek (FCM, Mozilla autopush, Apple push). Generuje się je jeden raz i nigdy nie należy ich zmieniać po wdrożeniu produkcyjnym, w przeciwnym razie wszyscy subskrybenci stają się nieosiągalni.

1. Przejdź do **DataFirefly Push > Opt-in & Settings**.
2. Kliknij **Generate VAPID keys**. Moduł tworzy parę kluczy ECDSA P-256 zapisaną w konfiguracji PrestaShop.
3. Wprowadź **VAPID subject**: adres `mailto:` z Twoim technicznym kontaktem (na przykład `mailto:admin@twoj-sklep.pl`). To identyfikator, pod który usługi push mogą się zgłosić w razie nadużyć.
4. Włącz moduł przełącznikiem **Push notifications enabled**.

**Zabezpiecz kopię kluczy.** Jeśli wygenerujesz klucze VAPID ponownie po starcie, wszyscy dotychczasowi subskrybenci zostaną odrzuceni przez usługi push kodem 410 Gone. Przed każdą manipulacją kluczami wykonaj kopię bazy PrestaShop.

## 5. Konfiguracja opt-in

Zakładka **Opt-in & Settings** steruje wyświetlaniem prośby o uprawnienie. Dostępne są trzy style:

- **Bell**: pływający dzwonek w prawym dolnym rogu. Użytkownik klika, następnie przeglądarka wyświetla swoją natywną prośbę. Najmniej inwazyjny.
- **Banner**: trwały baner u góry lub u dołu strony z dwoma przyciskami (zezwól, odmów).
- **Modal**: wyśrodkowane okno modalne, bardziej widoczne, ale też bardziej nachalne.

### Wyzwalacze

Aby nie wyświetlać prośby od pierwszej sekundy (wysoki współczynnik odmów), wybierz wyzwalacz:

- **Delay**: wyświetl po X sekundach (domyślnie 15 s)
- **Scroll**: wyświetl po X % przewinięcia strony (domyślnie 40 %)
- **Pages**: wyświetl po X odsłonach w sesji (domyślnie 2)

### Cooldown

Jeśli odwiedzający zamknie lub odrzuci prośbę, cooldown blokuje jej ponowne wyświetlenie przez N dni (domyślnie 7). Pozwala to uniknąć efektu „nękającego popupu", który skłania użytkowników do trwałego zablokowania domeny.

### Dzienny limit i godziny ciszy

- **Max per day**: maksymalna liczba powiadomień wysyłanych do jednego subskrybenta dziennie (domyślnie 3). Powyżej tej wartości kolejka wysyłki po cichu odrzuca nadmiarowe wiadomości.
- **Quiet hours**: przedział godzinowy, w którym żadne powiadomienie nie jest doręczane. Wiadomości dodane do kolejki w tym czasie są przekładane na poranek pierwszego okna poza ciszą.

## 6. Cron: konfiguracja i token

Cron steruje wszystkimi operacjami odroczonymi: automatyzacjami, kolejką wysyłki, opróżnianiem webhooków, czyszczeniem. Musi być wywoływany co 5 minut przez adres HTTP zabezpieczony tokenem.

### Token

Unikalny token jest generowany podczas instalacji. Aby go pobrać: **DataFirefly Push > Automations**, niebieski pasek u góry pokazuje pełny adres do skopiowania do Twojego crona.

### Przykład crona w Linuksie

```
*/5 * * * * curl -s "https://twoj-sklep.pl/module/dfpushnotifications/cron?token=TWOJ_TOKEN" > /dev/null
```

### Zadania wykonywane przy każdym uruchomieniu

1. Reset liczników dziennych (raz dziennie, przy przekroczeniu północy)
2. Wykonanie automatyzacji o dojrzałym opóźnieniu (porzucone koszyki 1 h / 24 h / 72 h, urodziny, nieaktywność, nowe produkty, zaplanowane prośby o opinię)
3. Uruchomienie zaplanowanych kampanii, których data nadeszła
4. Opróżnianie kolejki wysyłki (domyślnie do 50 powiadomień na partię, wartość konfigurowalna)
5. Opróżnianie kolejki webhooków (do 100 na partię)
6. Czyszczenie starych wpisów (sent starsze niż 60 dni, log webhooków starszy niż 30 dni)

Odpowiedź crona to JSON opisujący każdy etap:

```
{
  "success": true,
  "tasks": {
    "reset_counters": "ok",
    "triggers": { "abandoned_1h": 12, "abandoned_24h": 4, "birthday": 2, "inactivity": 38 },
    "scheduled_campaigns": 1,
    "queue": { "processed": 50, "sent": 47, "failed": 2, "expired": 1 },
    "webhooks": { "processed": 8, "sent": 8, "failed": 0, "dropped": 0 },
    "cleanup": "ok"
  },
  "duration_ms": 1842
}
```

Jeśli Twój hostingodawca nie udostępnia crona, skonfiguruj darmową usługę zewnętrzną (cron-job.org, EasyCron, cronless), wskazując ten sam adres co 5 minut.

## 7. Automatyzacje: 9 wyzwalaczy

Domyślnie wyłączone, włączasz je pojedynczo w **DataFirefly Push > Automations**. Każda karta pokazuje tytuł, treść i statystyki. Kliknij **Edit**, aby dostosować redakcję i opcje.

### Dostępne zmienne

Zależnie od wyzwalacza możesz wstawić do tytułu i treści:

- `{first_name}`: imię klienta (puste dla odwiedzających anonimowych)
- `{cart_total}`: kwota porzuconego koszyka, sformatowana z walutą
- `{order_reference}`: numer zamówienia
- `{order_total}`: łączna kwota zamówienia
- `{product_name}`: nazwa produktu (powrót do magazynu, spadek ceny)
- `{product_price}`: aktualna cena produktu
- `{products_count}`: liczba nowych produktów (digest)

### Porzucony koszyk (3 przypomnienia)

Trzy niezależne wyzwalacze: `abandoned_cart_1h`, `abandoned_cart_24h`, `abandoned_cart_72h`. Moduł zapisuje każdą modyfikację koszyka w `ps_dfpush_cart_watch` i oznacza koszyk jako skonwertowany od razu po zatwierdzeniu zamówienia. Cron wysyła każde przypomnienie po dojściu do terminu, chyba że koszyk został w międzyczasie skonwertowany.

### Powrót do magazynu

Na karcie produktu automatycznie pojawia się przycisk _Notify me when back in stock_, gdy produkt jest niedostępny. Subskrybent klika, w razie potrzeby proszony jest o opt-in push, a moduł zapisuje obserwatora w `ps_dfpush_product_watch`. Gdy tylko zmiana stanu magazynowego podniesie dostępną ilość powyżej 0, obserwator zostaje powiadomiony i oznaczony jako obsłużony.

### Spadek ceny

Ta sama zasada z przyciskiem _Notify me on price drop_. Obserwator zapisuje cenę referencyjną z momentu opt-in. Powiadomienie wychodzi, gdy aktualizacja produktu obniży cenę poniżej 99 % ceny referencyjnej (próg, który eliminuje fałszywe wyzwolenia przy zaokrągleniach).

### Potwierdzenie zamówienia

Wyzwalane przez hook `actionValidateOrder`. Wysyłane natychmiast, bez respektowania godzin ciszy (komunikacja transakcyjna). Bezpośredni link do strony szczegółów zamówienia.

### Wysyłka

Wyzwalane przez `actionOrderStatusUpdate`, gdy status przechodzi w stan oznaczony w PrestaShop jako _wysłane_. Link do szczegółów zamówienia (zawierających numer śledzenia, jeśli używasz modułu do śledzenia przesyłek).

### Prośba o opinię

Planowana X dni po przejściu zamówienia w stan _dostarczone_ (domyślnie 7 dni, konfigurowalne w JSON-owej konfiguracji wyzwalacza). Powiadomienie trafia do kolejki z przyszłym `scheduled_at`, cron doręcza je o właściwej porze.

### Urodziny

Cron odczytuje pole `birthday` klienta PrestaShop i wysyła powiadomienie w dniu urodzin o skonfigurowanej godzinie. Mechanizm deduplikacji zapobiega wielokrotnym wysyłkom w tym samym roku.

### Nieaktywność

Identyfikuje subskrybentów, których `last_visit` jest starsze niż N dni (domyślnie 30). Aby uniknąć nękania, ten sam subskrybent jest powiadamiany przez ten wyzwalacz najwyżej raz na 30 dni.

### Nowe produkty

Codzienny digest wysyłany o konfigurowalnej godzinie (domyślnie 10). Moduł agreguje produkty utworzone w ciągu ostatnich 24 godzin i wysyła powiadomienie z nazwą pierwszego z nich i łączną liczbą. Idealne dla sklepu, który regularnie rozbudowuje katalog.

## 8. Builder kampanii

Do operacji jednorazowych: **DataFirefly Push > Campaigns > New campaign**. Formularz dzieli się na pięć sekcji.

### Treść

- **Campaign name**: nazwa wewnętrzna (nigdy nie pokazywana subskrybentom)
- **Notification title**: maksymalnie 80 znaków
- **Body**: maksymalnie 250 znaków
- **Click URL**: cel przekierowania po kliknięciu
- **Icon URL**: małe logo (domyślnie logo Twojego sklepu)
- **Large image URL**: obraz główny, zalecane 1024 × 512 (tylko Chrome)
- **Badge URL**: monochromatyczny PNG 72 × 72 (Android)

### Przyciski akcji

Do dwóch przycisków z etykietą i adresem. Pojawiają się pod powiadomieniem w Chrome na desktopie i na Androidzie. Idealne do zaproponowania kilku wezwań do działania (_Zobacz ofertę_, _Zignoruj_).

### Audiencja

Zobacz sekcję Segmentacja poniżej.

### Harmonogram i doręczenie

- **Send at**: data i godzina wysyłki. Puste = natychmiast (przy najbliższym opróżnieniu kolejki przez cron).
- **Urgency**: `very-low`, `low`, `normal`, `high`. Wpływa na priorytet po stronie usługi push.
- **TTL**: czas życia w sekundach (domyślnie 86400, czyli 24 h). Jeśli przeglądarka subskrybenta pozostaje offline dłużej, powiadomienie wygasa.
- **Require interaction**: powiadomienie trwałe aż do kliknięcia lub ręcznego zamknięcia (tylko Chrome).

## 9. Segmentacja

Kryteria łączą się logicznym I. Wszystkie puste kryteria oznaczają brak ograniczeń.

| Kryterium | Działanie |
| --- | --- |
| Languages | Subskrybent mówi w co najmniej jednym z tych języków (zaznaczone pola) |
| Countries | Kraj wykryty przy zapisie (na podstawie języka, IP, geolokalizacji) |
| Devices | mobile, desktop, tablet (wykryte przy zapisie) |
| Browsers | Chrome, Firefox, Safari, Edge, Opera, Samsung Internet |
| Customer groups | Subskrybent jest powiązany z klientem PrestaShop należącym do co najmniej jednej z grup |
| Purchase history | _Any_ (domyślnie), _Has bought_, _Never bought_ |
| Active within N days | Ostatnia wizyta nie dawniej niż N dni temu (na podstawie `ps_dfpush_customer_activity`) |

Przycisk **Preview audience size** wylicza w AJAX dokładną liczbę pasujących subskrybentów. Przydatny, by sprawdzić przed wysyłką, czy targetowanie nie jest zbyt wąskie.

## 10. Testy A/B

W każdej kampanii pole **% of audience for variant B** określa część audiencji, która otrzyma wariant B (od 0 do 50). Podział jest deterministyczny: dla danego `id_subscriber` subskrybent zawsze otrzymuje ten sam wariant (na podstawie `id_subscriber % 100 < split`).

Wariant B nadpisuje tylko wypełnione pola. Możesz na przykład zmienić sam tytuł albo przetestować wyłącznie obraz. Statystyki są zapisywane osobno:

- `stats_delivered` / `stats_clicked` / `stats_revenue`: wariant A
- `stats_b_delivered` / `stats_b_clicked` / `stats_b_revenue`: wariant B

Kolumny pojawiają się na liście kampanii wraz z odznaką _A/B_ obok nazwy.

## 11. Atrybucja przychodu

Kluczowy mechanizm modułu: powiązanie zamówień z powiadomieniami, które je wywołały.

### Przebieg

1. Moduł wysyła powiadomienie z unikalnym `track_token` (32 znaki szesnastkowe) w payloadzie.
2. Service worker przechwytuje kliknięcie i przekierowuje na `/module/dfpushnotifications/track?t=TOKEN&click=1&u=URL`.
3. Kontroler `track` ustawia cookie `dfpush_attr=TOKEN` (30 dni, SameSite=Lax) i przekierowuje na docelowy adres.
4. Klient przegląda sklep, dodaje do koszyka, zatwierdza zamówienie.
5. Przy hooku `actionValidateOrder` `AttributionService` odczytuje cookie, odnajduje powiadomienie w `ps_dfpush_notifications` i zapisuje w tym wierszu `id_order`, `revenue` i `currency`.
6. Serwis zwiększa `stats_revenue` (lub `stats_b_revenue` zależnie od wariantu) kampanii albo wyzwalacza, uruchamia webhook `order.attributed`, a następnie kasuje cookie.

### Ograniczenia atrybucji

- Okno stałe, 30 dni. Po tym czasie atrybucja już nie zachodzi.
- Jedno cookie na subskrybenta. Jeśli użytkownik kliknie drugie powiadomienie przed złożeniem zamówienia, kredytowane jest to ostatnie (model _last click_).
- Atrybucja wymaga, aby kliknięcie i zamówienie miały miejsce w tej samej przeglądarce (cookie jest z nią związane).
- Raz przypisane zamówienie nie może zostać przypisane ponownie (pole `id_order > 0` blokuje podwójne liczenie).

## 12. Topics

Topics to tematyczne kanały subskrypcji, które klienci aktywują ze swojego konta. Na przykład: _Promocje_, _Nowe produkty_, _Alerty magazynowe_. Subskrybent, który zaznaczy tylko _Promocje_, otrzyma wyłącznie kampanie kierowane na ten topic.

### Tworzenie topicu

1. **DataFirefly Push > Topics > New topic**
2. **Code**: identyfikator wewnętrzny bez spacji (na przykład `flash_deals`)
3. **Display order**: pozycja na liście pokazywanej klientowi
4. **Active**: widoczny dla klientów
5. **Default opt-in**: zaznaczony domyślnie przy zapisie
6. **Translations**: nazwa i opis dla każdego zainstalowanego języka

### Kierowanie kampanii na topic

Segmentacja zawiera kryterium _Topics_ w resolwerze. Aktywacja z poziomu interfejsu jest obecnie możliwa wyłącznie przez własny JSON (pole wyboru Topics pojawi się w drobnej aktualizacji). Subskrybenci bez żadnego zapisanego topicu otrzymują wszystko, dla zachowania wstecznej zgodności.

## 13. Inbox in-app

Endpoint `/module/dfpushnotifications/inbox` zwraca w JSON 20 ostatnich powiadomień bieżącego subskrybenta. Frontend otwiera ten inbox przez ujednolicone okno modalne dostępne z konta klienta (kafelek _Push notifications_).

Okno modalne pokazuje też stan subskrypcji (z przyciskiem subscribe i unsubscribe) oraz listę topiców. Aby wywołać je ręcznie z poziomu Twojego szablonu:

```
// Otwórz okno modalne
window.dfpush.openManagePopup();

// Sprawdź stan subskrypcji
const isOn = window.dfpush.isSubscribed();
```

## 14. Webhooki

Webhooki wysyłają zdarzenia modułu do Zapiera, Make, n8n lub Twojego własnego backendu. Każdy webhook jest podpisany HMAC-SHA256 na surowej treści żądania.

### Tworzenie webhooka

1. **DataFirefly Push > Webhooks > New webhook**
2. **URL**: Twój endpoint (zalecany HTTPS)
3. **Secret**: generowany automatycznie, skopiuj go po stronie odbiorcy
4. **Events**: zaznacz zdarzenia do doręczenia spośród 8 dostępnych
5. Kliknij **Send test ping**, aby sprawdzić, czy Twój endpoint odpowiada kodem 2xx

### Dostępne zdarzenia

- `subscriber.subscribed`: nowy opt-in
- `subscriber.unsubscribed`: wypisanie
- `subscriber.expired`: usługa push zwróciła 410 Gone
- `notification.sent`: powiadomienie doręczone pomyślnie
- `notification.clicked`: zarejestrowane kliknięcie
- `notification.failed`: ostateczne niepowodzenie po 3 próbach
- `campaign.sent`: kampania zakończona
- `order.attributed`: zamówienie powiązane z powiadomieniem

### Format payloadu

```
{
  "event": "notification.clicked",
  "timestamp": "2026-05-28T14:32:18+00:00",
  "shop": 1,
  "data": {
    "id_subscriber": 482,
    "id_campaign": 12,
    "id_trigger": 0,
    "id_notification": 9182,
    "track_token": "abc123...",
    "url": "https://twoj-sklep.pl/promocje",
    "ab_variant": "A"
  }
}
```

### Wysyłane nagłówki HTTP

- `Content-Type: application/json`
- `X-DfPush-Event: notification.clicked`
- `X-DfPush-Signature: sha256=`
- `X-DfPush-Timestamp: 1748443938`
- `User-Agent: DataFireflyPush/1.2`

### Weryfikacja podpisu po stronie odbiorcy

Przykład w PHP:

```
$body     = file_get_contents('php://input');
$expected = hash_hmac('sha256', $body, $YOUR_SECRET);
$received = explode('=', $_SERVER['HTTP_X_DFPUSH_SIGNATURE'])[1] ?? '';
if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}
// Podpis prawidłowy, przetwórz payload
$payload = json_decode($body, true);
```

### Ponowne próby i porzucenie

Jeśli Twój endpoint odpowie kodem innym niż 2xx, moduł ponawia próbę z wykładniczym backoffem, maksymalnie 5 razy. Następnie log jest oznaczany jako _dropped_, a zdarzenie przepada. Logi _sent_ i _dropped_ są usuwane po 30 dniach.

## 15. Multisklep i wielojęzyczność

Wszyscy subskrybenci, kampanie, automatyzacje, topics i webhooki są powiązane z `id_shop`. W trybie multisklep:

- Wybierz docelowy sklep w selektorze zaplecza przed utworzeniem kampanii lub automatyzacji
- Cron jest jeden, ale automatycznie filtruje po sklepie
- Klucze VAPID są współdzielone między sklepami (jedna para dla wszystkich)

Po stronie językowej moduł jest dostarczany w FR, EN, ES, DE i IT. Język subskrybenta jest wykrywany przy zapisie i zapamiętywany. W kampaniach możesz segmentować po języku.

## 16. RODO i dobre praktyki

- **Zgoda**: subskrypcja opiera się na natywnym opt-in przeglądarki, który stanowi dowód zgody zgodny z RODO.
- **Dane osobowe**: moduł zapisuje endpoint Web Push, adres IP z momentu zapisu, user-agent, język i kraj. Żadnej transmisji do podmiotów trzecich.
- **Wypisanie**: kliknięcie przycisku _Unsubscribe_ w oknie modalnym albo wyłączenie powiadomień w przeglądarce ustawia subskrybenta w statusie `revoked`.
- **Prawo do usunięcia**: usunięcie konta klienta w zapleczu usuwa również powiązanych z nim subskrybentów.
- **Godziny ciszy**: domyślnie od 22 do 8. Dostosuj do swojej grupy docelowej (B2B: godziny biurowe, B2C: wieczór).
- **Dzienny limit**: domyślnie 3 na dobę. Powyżej tej wartości współczynnik wypisań rośnie bardzo szybko.

## 17. Rozwiązywanie problemów

### Opt-in się nie wyświetla

- Sprawdź, czy Twoja strona działa na HTTPS, nie na HTTP.
- Sprawdź, czy klucze VAPID zostały wygenerowane (**Settings**).
- Sprawdź, czy przełącznik **Push notifications enabled** jest włączony.
- Otwórz konsolę przeglądarki (F12) i poszukaj błędów po stronie `dfpush-frontend.js`.
- Jeśli już wcześniej odrzuciłeś lub zignorowałeś prośbę, cooldown blokuje ją przez N dni. Wyczyść cookies domeny, aby zresetować stan, albo przetestuj w innej przeglądarce.

### Cron się nie wykonuje

- Wywołaj adres ręcznie w przeglądarce: powinieneś zobaczyć JSON z `success: true`. Jeśli widzisz `403 invalid_token`, token został źle przepisany.
- Sprawdź czas ostatniego uruchomienia na pulpicie (**Dashboard**, blok _System_).
- U niektórych hostingodawców timeout cURL dla cronów wynosi 30 s. Jeśli kolejka jest bardzo duża, zwiększ częstotliwość crona do co 1 lub 2 minuty, aby przetwarzać mniejsze partie.

### Powiadomienia nie wychodzą

- Zakładka **Queue**: jeśli wiersze mają status _pending_, oznacza to, że cron nie uruchomił się od czasu ich utworzenia.
- Jeśli mają status _failed_, kliknij wiersz, aby zobaczyć kod HTTP i komunikat błędu usługi push.
- `410 Gone` oznacza, że subskrypcja jest już nieważna (użytkownik wypisał się albo wyczyścił przeglądarkę). Moduł automatycznie oznacza subskrybenta jako _expired_.
- `429 Too Many Requests` oznacza limit po stronie usługi push. Moduł ponawia próbę automatycznie z backoffem.

### Zamówienia nie są przypisywane

- Sprawdź, czy przeglądarka klienta nie blokuje cookies podmiotów trzecich (tryb prywatny, niektóre blokery śledzenia).
- Cookie wygasa po 30 dniach: zamówienie złożone 31 dni po kliknięciu nie zostanie przypisane.
- Jeśli w `ps_dfpush_notifications` widzisz `id_order = 0` mimo złożonego zamówienia, sprawdź, czy hook `actionValidateOrder` jest poprawnie zarejestrowany (**Moduły > Hooki > actionValidateOrder**).

### Błąd SQL z „LIMIT 1 LIMIT 1"

Błąd wewnętrzny poprawiony w 1.2.0 (`Db::getValue` i `Db::getRow` same dodają własne `LIMIT 1`). Jeśli nadal widzisz ten komunikat, sprawdź, czy masz zainstalowaną najnowszą wersję modułu.

## 18. FAQ

### Ilu subskrybentów obsłuży moduł?

Moduł nie ma limitu zapisanego w kodzie. W praktyce czynnikiem ograniczającym jest czas wykonania crona: na standardowym hostingu współdzielonym licz około 50 wysyłek na minutę (limit narzucany przez usługi push). Przy 10 000 subskrybentów zaplanuj cron co 1 lub 2 minuty na czas wysyłki dużej kampanii.

### Czy Safari iOS jest naprawdę wykluczone?

Safari iOS obsługuje Web Push od iOS 16.4, ale wyłącznie dla stron zainstalowanych jako PWA na ekranie głównym. W zwykłym Safari API `Notification` zwraca `undefined`. To ograniczenie Apple, nie modułu.

### Czy mogę przenieść subskrybentów z OneSignal albo Firebase?

Nie. Endpointy push są związane z parą kluczy VAPID użytą przy zapisie. Zmiana pary unieważnia istniejące subskrypcje. Migracja wymagałaby ponownego zapisu użytkowników.

### Czy moduł działa na PrestaShop 1.7?

Nie, wyłącznie PrestaShop 8.0 do 9.x. Wersja 1.7 używa innej architektury zaplecza, która wymagałaby dedykowanego forka.

### Czy mogę dostosować service worker?

Tak, plik `views/js/sw-template.js` jest renderowany przez kontroler `swjs.php` i można go rozszerzyć. Uwaga: każda zmiana service workera wymaga nowego `register()` po stronie klienta. Dotychczasowi odwiedzający zachowają poprzednią wersję, dopóki przeglądarka nie wykryje zmiany (zwykle 24 h).

### Jak wyeksportować subskrybentów?

Zakładka **Subscribers**, przycisk **Export CSV**. Plik zawiera endpoint, przeglądarkę, urządzenie, język, kraj, datę zapisu oraz liczniki wysyłek i kliknięć.

## Wsparcie

W przypadku pytań nieobjętych tym przewodnikiem skontaktuj się ze wsparciem DataFirefly. Zgłaszając błąd, dołącz wersję PrestaShop, wersję PHP oraz treść odpowiedzi JSON zwróconej przez cron.
