feat(05-purchase-redirect-hardening): harden purchase redirect tracking
Phase 5 complete: - guard purchase event per transaction in sessionStorage - restore saved consent before GTM and purchase - add centered Przelewy24 countdown redirect
This commit is contained in:
@@ -15,6 +15,8 @@ Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmi
|
||||
- [x] Event purchase capturuje 100% zamowien - fires przy skladaniu, nie przy powrocie z P24 (Phase 2)
|
||||
- [x] Zgodnosc z RODO - baner zgody na cookies z Google Consent Mode v2 (Phase 3)
|
||||
- [x] Baner cookies dziala bez bledu `injectScripts is not defined` (Phase 4)
|
||||
- [x] Event purchase ma zabezpieczenie przed duplikatem przy odswiezeniu strony Przelewy24 (Phase 5)
|
||||
- [x] Przekierowanie do Przelewy24 dziala automatycznie po 5 sekundach z czytelnym komunikatem (Phase 5)
|
||||
|
||||
### Should Have
|
||||
- [x] Spojny tracking analityczny dla zdarzen ecommerce
|
||||
@@ -38,6 +40,8 @@ Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmi
|
||||
| cookieNoticePro.init() zamiast $.fn.cookieNoticePro() | Phase 3 | Zgodnie z dokumentacja biblioteki |
|
||||
| Consent Mode v2 default-denied przed GTM snippetem | Phase 3 | Wymog Google od marca 2024 |
|
||||
| Usuniecie niezdefiniowanego injectScripts() | Phase 4 | Naprawia blad konsoli przy cookies bez zmiany GTM/Consent Mode |
|
||||
| Purchase guard w sessionStorage per transaction_id | Phase 5 | Chroni przed duplikatem eventu przy odswiezeniu strony |
|
||||
| Restore zapisanych zgod przed GTM | Phase 5 | Purchase uruchamia sie z poprawnym stanem Consent Mode |
|
||||
|
||||
## Success Criteria
|
||||
- Event purchase trafia do data layer po zlozeniu zamowienia (osiagniete - Phase 2)
|
||||
@@ -45,7 +49,9 @@ Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmi
|
||||
- Integracja nie wplywa negatywnie na istniejacy checkout
|
||||
- Baner cookies zgodny z RODO + Google Consent Mode v2 (osiagniete - Phase 3)
|
||||
- Baner cookies nie generuje bledu `injectScripts is not defined` (osiagniete - Phase 4)
|
||||
- Purchase nie duplikuje sie po odswiezeniu strony Przelewy24 w tej samej sesji przegladarki (osiagniete - Phase 5)
|
||||
- Przelewy24 redirect jest automatyczny i poprzedzony czytelnym alertem z odliczaniem (osiagniete - Phase 5)
|
||||
|
||||
---
|
||||
*Created: 2026-04-19 20:20*
|
||||
*Last updated: 2026-05-08 after Phase 4 (v0.3 hotfix complete)*
|
||||
*Last updated: 2026-05-08 after Phase 5 (v0.4 complete)*
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
# Roadmap: bilety.brzezovka.pl
|
||||
|
||||
## Overview
|
||||
Projekt obejmuje sprzedaz biletow online, tracking ecommerce oraz zgodnosc cookies/RODO. Ostatnia iteracja to hotfix banera cookies po wykryciu bledu JavaScript w CookieNoticePro.
|
||||
Projekt obejmuje sprzedaz biletow online, tracking ecommerce oraz zgodnosc cookies/RODO. Aktualna iteracja domyka tracking purchase na stronie Przelewy24: jednorazowe odpalenie eventu, poprawne zgody i automatyczne przekierowanie do platnosci.
|
||||
|
||||
## Current Milestone
|
||||
**v0.3 Cookie Notice Hotfix** (v0.3.0)
|
||||
**v0.4 Purchase Redirect Hardening** (v0.4.0)
|
||||
Status: Complete (2026-05-08)
|
||||
Phases: 1 of 1 complete
|
||||
|
||||
## Previous Milestones
|
||||
|
||||
### v0.3 Cookie Notice Hotfix
|
||||
Status: Complete (2026-05-08)
|
||||
Phases: 1 of 1 complete
|
||||
|
||||
### v0.2 Analytics & Privacy
|
||||
Status: Complete (2026-04-26)
|
||||
Phases: 3 of 3 complete
|
||||
@@ -26,6 +30,7 @@ Phases: 1 of 1 complete
|
||||
| 2 | Purchase Event Pre-Payment | 1 | Complete | 2026-04-26 |
|
||||
| 3 | Cookie Consent Banner | 1 | Complete | 2026-04-26 |
|
||||
| 4 | Cookie Notice Bugfix | 1 | Complete | 2026-05-08 |
|
||||
| 5 | Purchase Redirect Hardening | 1 | Complete | 2026-05-08 |
|
||||
|
||||
## Phase Details
|
||||
|
||||
@@ -42,18 +47,7 @@ Phases: 1 of 1 complete
|
||||
### Phase 2: Purchase Event Pre-Payment (Complete)
|
||||
|
||||
**Goal:** Przeniesc event purchase do momentu przekierowania na bramke platnicza po zlozeniu zamowienia, przed platnoscia Przelewy24.
|
||||
**Depends on:** Phase 1 (purchase payload builder already implemented)
|
||||
**Research:** Not needed
|
||||
|
||||
**Context:**
|
||||
- GTM (GTM-TW9WCD9J) jest wdrozony w `templates/site/layout-logged.php`.
|
||||
- Event purchase zostal przeniesiony na strone `przelewy24`.
|
||||
- Strona `przelewy24.php` auto-submits formularz, wiec tracking korzysta z beacon transport.
|
||||
|
||||
**Scope:**
|
||||
- Wywolanie `buildPurchaseDataLayer()` w metodzie `przelewy24()` kontrolera.
|
||||
- Dodanie dataLayer push do `templates/tickets/przelewy24.php`.
|
||||
- Usuniecie purchase push z `templates/tickets/order-confirm.php`.
|
||||
**Depends on:** Phase 1
|
||||
|
||||
**Plans:**
|
||||
- [x] 02-01: Przeniesienie eventu purchase na strone przelewy24 (UNIFY complete)
|
||||
@@ -64,18 +58,6 @@ Phases: 1 of 1 complete
|
||||
|
||||
**Goal:** Wdrozyc baner zgody na cookies (CookieNoticePro) z Google Consent Mode v2 i naprawic blad analityki w bibliotece.
|
||||
**Depends on:** Phase 2
|
||||
**Research:** Not needed
|
||||
|
||||
**Context:**
|
||||
- Biblioteka: `libraries/CookieNoticePro/`.
|
||||
- Consent Mode v2 musi byc przed snippetem GTM w `<head>`.
|
||||
- Tylko `templates/site/layout-logged.php` jest uzywany przez glowne widoki.
|
||||
|
||||
**Scope:**
|
||||
- Pliki CookieNoticePro w `libraries/CookieNoticePro/`.
|
||||
- Naprawa `analytics_storage: denied` przy braku zgody analytics.
|
||||
- Consent Mode v2 default przed GTM w `layout-logged.php`.
|
||||
- Integracja CSS/JS banera + inicjalizacja `cookieNoticePro.init()`.
|
||||
|
||||
**Plans:**
|
||||
- [x] 03-01: Integracja CookieNoticePro + Consent Mode v2 (UNIFY complete)
|
||||
@@ -85,22 +67,33 @@ Phases: 1 of 1 complete
|
||||
### Phase 4: Cookie Notice Bugfix (Complete)
|
||||
|
||||
**Goal:** Naprawic blad JavaScript `Uncaught ReferenceError: injectScripts is not defined` w CookieNoticePro.
|
||||
**Depends on:** Phase 3 (CookieNoticePro already integrated)
|
||||
**Research:** Not needed (root cause is visible in local source)
|
||||
|
||||
**Context:**
|
||||
- `libraries/CookieNoticePro/cookienoticepro.script.js` wywolywal `injectScripts()` w dwoch miejscach.
|
||||
- Funkcja `injectScripts` nie istnieje w tej kopii biblioteki.
|
||||
- Blad wystepowal po kliknieciu akceptacji cookies oraz mogl wystapic przy inicjalizacji z zapisanymi zgodami.
|
||||
|
||||
**Scope:**
|
||||
- Usuniecie wywolan `injectScripts()`.
|
||||
- Zachowanie `cookieNoticePro.init()` w `templates/site/layout-logged.php`.
|
||||
- Zachowanie Google Consent Mode v2 i GTM bez zmian.
|
||||
**Depends on:** Phase 3
|
||||
|
||||
**Plans:**
|
||||
- [x] 04-01: Naprawa undefined `injectScripts` w CookieNoticePro (UNIFY complete)
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: Purchase Redirect Hardening
|
||||
|
||||
**Goal:** Zabezpieczyc `purchase` przed ponownym wywolaniem po odswiezeniu strony Przelewy24 i przywrocic automatyczne przekierowanie do Przelewy24 po 5 sekundach z widocznym odliczaniem.
|
||||
**Depends on:** Phase 2 (purchase pre-payment), Phase 3/4 (CookieNoticePro + Consent Mode)
|
||||
**Research:** Not needed
|
||||
|
||||
**Context:**
|
||||
- `purchase` jest wypychany w `templates/tickets/przelewy24.php`.
|
||||
- Manualny test potwierdzil, ze event uruchamia sie poprawnie po zatrzymaniu auto-submit.
|
||||
- Zapisane zgody cookies musza byc przywrocone przed GTM i przed `purchase`.
|
||||
- Obecny przycisk byl tylko tymczasowy do testu.
|
||||
|
||||
**Scope:**
|
||||
- Client-side guard w `templates/tickets/przelewy24.php` na bazie `sessionStorage` i `transaction_id`.
|
||||
- Komunikat + countdown 5s + automatyczny submit formularza Przelewy24.
|
||||
- Zachowanie restore zgody w `templates/site/layout-logged.php`.
|
||||
|
||||
**Plans:**
|
||||
- [x] 05-01: Jednorazowy purchase + redirect countdown do Przelewy24 (UNIFY complete)
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-04-19*
|
||||
*Last updated: 2026-05-08 - v0.3 Cookie Notice Hotfix complete*
|
||||
*Last updated: 2026-05-08 - v0.4 Purchase Redirect Hardening complete*
|
||||
|
||||
@@ -5,19 +5,19 @@
|
||||
See: .paul/PROJECT.md (updated 2026-05-08)
|
||||
|
||||
**Core value:** Uzytkownicy moga szybko i bezpiecznie kupic bilety online oraz otrzymac natychmiastowe potwierdzenie zakupu.
|
||||
**Current focus:** v0.3 Cookie Notice Hotfix - COMPLETE
|
||||
**Current focus:** v0.4 Purchase Redirect Hardening - COMPLETE
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.3 Cookie Notice Hotfix - COMPLETE
|
||||
Phase: 1 of 1 (Cookie Notice Bugfix) - Complete
|
||||
Plan: 04-01 complete
|
||||
Milestone: v0.4 Purchase Redirect Hardening - COMPLETE
|
||||
Phase: 1 of 1 (Purchase Redirect Hardening) - Complete
|
||||
Plan: 05-01 complete
|
||||
Status: Milestone complete - ready for next milestone
|
||||
Last activity: 2026-05-08 - Phase 4 complete, v0.3 hotfix shipped
|
||||
Last activity: 2026-05-08 - Phase 5 complete, v0.4 shipped
|
||||
|
||||
Progress:
|
||||
- Milestone: [##########] 100%
|
||||
- Phase 4: [##########] 100%
|
||||
- Phase 5: [##########] 100%
|
||||
|
||||
## Loop Position
|
||||
|
||||
@@ -44,19 +44,16 @@ Documents: `.paul/codebase/` (8 files - stack, architecture, structure, conventi
|
||||
- 2026-04-26: `cookieNoticePro.init()` used instead of `$.fn.cookieNoticePro()`.
|
||||
- 2026-04-26: Consent Mode v2 default-denied initialized before the GTM snippet.
|
||||
- 2026-05-08: Undefined `injectScripts()` calls removed from CookieNoticePro instead of adding a no-op stub.
|
||||
- 2026-05-08: Manual redirect pause confirmed purchase fires; saved consent must be restored before GTM/purchase.
|
||||
|
||||
### Cookie Consent (Phase 3 - COMPLETE)
|
||||
- Library: `libraries/CookieNoticePro/`.
|
||||
- Analytics denied bug fixed around line 351.
|
||||
- Additional fixes: CSS initial hide for `acceptBtnSettingsLabel`, Bootstrap 5 compatibility reset.
|
||||
- Consent Mode v2 init is before the GTM snippet in `templates/site/layout-logged.php`.
|
||||
|
||||
### Cookie Notice Hotfix (Phase 4 - COMPLETE)
|
||||
- Reported browser error: `Uncaught ReferenceError: injectScripts is not defined`.
|
||||
- Root cause: `libraries/CookieNoticePro/cookienoticepro.script.js` called `injectScripts()` but this function was absent in the local library copy.
|
||||
- Applied fix: removed both `injectScripts()` calls from `libraries/CookieNoticePro/cookienoticepro.script.js`.
|
||||
- Verification passed: no `injectScripts` references remain, `node --check` passes, and layout still contains Consent Mode v2, GTM, and `cookieNoticePro.init()`.
|
||||
- Summary: `.paul/phases/04-cookie-notice-bugfix/04-01-SUMMARY.md`.
|
||||
### Current Plan Context
|
||||
- Plan path: `.paul/phases/05-purchase-redirect-hardening/05-01-PLAN.md`.
|
||||
- Current uncommitted source changes include `templates/site/layout-logged.php` consent restore and `templates/tickets/przelewy24.php` temporary manual redirect button.
|
||||
- Applied changes: `templates/tickets/przelewy24.php` now guards purchase with `sessionStorage` per transaction and auto-submits to Przelewy24 after a 5-second countdown; `templates/site/layout-logged.php` preserves saved consent restore before GTM.
|
||||
- Final UI adjustment: visible fallback button removed; redirect message is a centered Bootstrap alert with countdown.
|
||||
- Verification passed: PHP lint for both modified templates, grep checks for purchase guard/countdown/consent restore, and user browser approval.
|
||||
- Summary: `.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md`.
|
||||
- Existing unrelated uncommitted change: `.vscode/ftp-kr.sync.cache.json` must remain untouched.
|
||||
|
||||
### Deferred Issues
|
||||
None.
|
||||
@@ -65,16 +62,16 @@ None.
|
||||
None.
|
||||
|
||||
### Git State
|
||||
Last commit: current phase commit (`feat(04-cookie-notice-bugfix): fix CookieNoticePro injectScripts error`)
|
||||
Last commit: current phase commit (`feat(05-purchase-redirect-hardening): harden purchase redirect tracking`)
|
||||
Branch: main
|
||||
Feature branches merged: none
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-05-08
|
||||
Stopped at: v0.3 hotfix milestone complete
|
||||
Stopped at: v0.4 milestone complete
|
||||
Next action: Start next milestone when needed
|
||||
Resume file: .paul/phases/04-cookie-notice-bugfix/04-01-SUMMARY.md
|
||||
Resume file: .paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md
|
||||
|
||||
---
|
||||
*STATE.md - Updated after every significant action*
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
- [Phase 4, Plan 01] Naprawiono blad `Uncaught ReferenceError: injectScripts is not defined` w CookieNoticePro.
|
||||
- Usunieto dwa wywolania niezdefiniowanego `injectScripts()` z obslugi akceptacji cookies i inicjalizacji przy zapisanej zgodzie.
|
||||
- Zweryfikowano skladnie pliku JS oraz obecnosc `cookieNoticePro.init()`, Consent Mode v2 i GTM w layoucie.
|
||||
- [Phase 5, Plan 01] Zabezpieczono `purchase` przed ponownym wywolaniem po odswiezeniu strony Przelewy24.
|
||||
- Dodano restore zapisanych zgod cookies przed GTM, aby `purchase` korzystalo z poprawnego Consent Mode.
|
||||
- Przywrocono automatyczne przekierowanie do Przelewy24 po 5 sekundach z wycentrowanym alertem i odliczaniem.
|
||||
|
||||
## Zmienione pliki
|
||||
|
||||
@@ -13,3 +16,8 @@
|
||||
- `.paul/phases/04-cookie-notice-bugfix/04-01-SUMMARY.md`
|
||||
- `.paul/STATE.md`
|
||||
- `.paul/ROADMAP.md`
|
||||
- `templates/tickets/przelewy24.php`
|
||||
- `templates/site/layout-logged.php`
|
||||
- `.paul/phases/05-purchase-redirect-hardening/05-01-PLAN.md`
|
||||
- `.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md`
|
||||
- `.paul/PROJECT.md`
|
||||
|
||||
165
.paul/phases/05-purchase-redirect-hardening/05-01-PLAN.md
Normal file
165
.paul/phases/05-purchase-redirect-hardening/05-01-PLAN.md
Normal file
@@ -0,0 +1,165 @@
|
||||
---
|
||||
phase: 05-purchase-redirect-hardening
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- templates/tickets/przelewy24.php
|
||||
- templates/site/layout-logged.php
|
||||
autonomous: true
|
||||
delegation: off
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Zabezpieczyc event `purchase` przed ponownym wywolaniem po odswiezeniu strony Przelewy24 i przywrocic automatyczne przekierowanie do Przelewy24 z komunikatem oraz odliczaniem 5 sekund.
|
||||
|
||||
## Purpose
|
||||
Tracking ecommerce ma rejestrowac jedno zamowienie tylko raz w przegladarce, a uzytkownik ma po zlozeniu zamowienia zostac automatycznie przekierowany do platnosci po krotkim czasie pozwalajacym GTM przetworzyc event.
|
||||
|
||||
## Output
|
||||
Zmodyfikowane `templates/tickets/przelewy24.php` oraz `templates/site/layout-logged.php`.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
<clarifications>
|
||||
- No clarifications needed - uzytkownik potwierdzil, ze `purchase` dziala po przycisku, ale wymaga zabezpieczenia przed odswiezeniem i powrotu automatycznego przekierowania z odliczaniem 5s.
|
||||
- Przyjete rozwiazanie: blokada duplikatu w `sessionStorage` per `transaction_id`, bo nie wymaga zmiany DB i chroni przed refresh w tej samej sesji przegladarki.
|
||||
</clarifications>
|
||||
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Prior Work
|
||||
@.paul/phases/02-purchase-event-prepayment/02-01-SUMMARY.md
|
||||
@.paul/phases/03-cookie-consent/03-01-SUMMARY.md
|
||||
@.paul/phases/04-cookie-notice-bugfix/04-01-SUMMARY.md
|
||||
|
||||
## Source Files
|
||||
@templates/tickets/przelewy24.php
|
||||
@templates/site/layout-logged.php
|
||||
@autoload/controls/class.Tickets.php
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Purchase fires only once per browser session and transaction
|
||||
```gherkin
|
||||
Given user lands on /tickets/przelewy24/order=HASH with a valid purchase payload
|
||||
When the page is loaded for the first time in the current browser session
|
||||
Then the page pushes ecommerce reset and `event: purchase` to `window.dataLayer`
|
||||
```
|
||||
|
||||
## AC-2: Refresh does not duplicate purchase
|
||||
```gherkin
|
||||
Given the same browser session already sent purchase for the same transaction_id
|
||||
When the user refreshes /tickets/przelewy24/order=HASH
|
||||
Then the page does not push another `event: purchase` for that transaction_id
|
||||
```
|
||||
|
||||
## AC-3: Auto redirect returns with visible countdown
|
||||
```gherkin
|
||||
Given the user lands on the Przelewy24 redirect page
|
||||
When the page finishes loading
|
||||
Then a message says the user will be redirected to Przelewy24 and a visible countdown starts at 5 seconds before the form submits automatically
|
||||
```
|
||||
|
||||
## AC-4: Saved cookie consent is restored before GTM and purchase
|
||||
```gherkin
|
||||
Given the user previously accepted analytics or marketing cookies
|
||||
When a new page loads
|
||||
Then saved CookieNoticePro preferences update Google Consent Mode before GTM and before the purchase event can be pushed
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add one-time purchase guard</name>
|
||||
<files>templates/tickets/przelewy24.php</files>
|
||||
<action>
|
||||
Replace the unconditional `window.dataLayer.push(...)` block with guarded JavaScript:
|
||||
- JSON-encode the existing purchase payload as now.
|
||||
- Read `transaction_id` from `purchaseDataLayer.ecommerce.transaction_id`.
|
||||
- Build a stable session key, e.g. `brzezovka_purchase_sent_` + transaction_id.
|
||||
- If no session key exists, push `{ ecommerce: null }` and the purchase payload, then set the session key.
|
||||
- If the key exists, skip the purchase push.
|
||||
- If `sessionStorage` is unavailable, fail open and still push purchase once for that page load.
|
||||
Avoid changing backend payload structure in `autoload/controls/class.Tickets.php`.
|
||||
</action>
|
||||
<verify>rg -n "sessionStorage|brzezovka_purchase_sent_|dataLayer.push" templates/tickets/przelewy24.php</verify>
|
||||
<done>AC-1 and AC-2 satisfied.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Restore auto redirect with five second countdown</name>
|
||||
<files>templates/tickets/przelewy24.php</files>
|
||||
<action>
|
||||
Replace the temporary submit button with:
|
||||
- A visible message explaining automatic redirect to Przelewy24.
|
||||
- A visible countdown starting at 5 seconds.
|
||||
- JavaScript that updates the countdown every second and submits `#form_data` after 5 seconds.
|
||||
- Keep a normal submit button as fallback/manual action only if JavaScript is delayed or user wants to continue immediately.
|
||||
Ensure the purchase guard executes before the redirect timer submits the form.
|
||||
</action>
|
||||
<verify>rg -n "redirectCountdown|setInterval|setTimeout|form_data|Przelewy24" templates/tickets/przelewy24.php</verify>
|
||||
<done>AC-3 satisfied.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Keep saved consent restore before GTM</name>
|
||||
<files>templates/site/layout-logged.php</files>
|
||||
<action>
|
||||
Preserve the current Consent Mode restore block added after default denied and before the GTM snippet:
|
||||
- Read `cnp_consent` and `cnp_prefs` cookies.
|
||||
- If consent is true, update analytics and marketing consent according to saved preferences.
|
||||
- Keep the GTM snippet after this restore block.
|
||||
Improve only if needed for syntax or robustness; do not move CookieNoticePro init into the head.
|
||||
</action>
|
||||
<verify>rg -n "cnp_consent|cnp_prefs|gtag\\('consent', 'update'|Google Tag Manager|cookieNoticePro\\.init" templates/site/layout-logged.php</verify>
|
||||
<done>AC-4 satisfied.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- `autoload/controls/class.Tickets.php` purchase payload builder unless verification proves a blocker.
|
||||
- Database schema and order write flow.
|
||||
- CookieNoticePro library files.
|
||||
- Przelewy24 credentials, signatures, endpoint URLs, and payment callback handling.
|
||||
- Existing unrelated `.vscode/ftp-kr.sync.cache.json` change.
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Client-side duplicate guard only; no DB-level event ledger in this plan.
|
||||
- No changes to GA4/GTM container configuration.
|
||||
- Keep this as a production cleanup after the manual test, not a broader checkout redesign.
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `php -l templates/tickets/przelewy24.php`
|
||||
- [ ] `php -l templates/site/layout-logged.php`
|
||||
- [ ] `rg -n "sessionStorage|brzezovka_purchase_sent_|redirectCountdown|setTimeout|cnp_consent|cnp_prefs" templates/tickets/przelewy24.php templates/site/layout-logged.php`
|
||||
- [ ] Manual browser check: first load pushes `purchase`, refresh of same Przelewy24 page does not push another `purchase`.
|
||||
- [ ] Manual browser check: countdown starts at 5 and submits to Przelewy24 automatically.
|
||||
- [ ] Manual browser check: with saved cookie consent, purchase is sent under restored granted consent when analytics/marketing were accepted.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- `purchase` cannot be duplicated by simple refresh in the same browser session.
|
||||
- Przelewy24 redirect is automatic again after 5 seconds.
|
||||
- User sees clear redirect message and countdown.
|
||||
- Saved CookieNoticePro consent is restored before GTM and purchase execution.
|
||||
- No payment backend, order creation, or Przelewy24 callback logic changed.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md`.
|
||||
</output>
|
||||
138
.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md
Normal file
138
.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md
Normal file
@@ -0,0 +1,138 @@
|
||||
---
|
||||
phase: 05-purchase-redirect-hardening
|
||||
plan: 01
|
||||
subsystem: analytics
|
||||
tags: [purchase, dataLayer, consent-mode-v2, przelewy24, redirect]
|
||||
|
||||
requires:
|
||||
- phase: 02-purchase-event-prepayment
|
||||
provides: purchase event emitted before payment redirect
|
||||
- phase: 03-cookie-consent
|
||||
provides: CookieNoticePro and Consent Mode v2 integration
|
||||
- phase: 04-cookie-notice-bugfix
|
||||
provides: stable CookieNoticePro initialization
|
||||
provides:
|
||||
- Browser-session guard against duplicate purchase pushes per transaction_id
|
||||
- Saved cookie consent restore before GTM and purchase event execution
|
||||
- Centered Przelewy24 redirect alert with 5-second countdown and automatic submit
|
||||
affects: [analytics, checkout, payments]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [sessionStorage duplicate guard, pre-GTM consent restore, delayed payment redirect]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- templates/tickets/przelewy24.php
|
||||
- templates/site/layout-logged.php
|
||||
|
||||
key-decisions:
|
||||
- "Use `sessionStorage` keyed by transaction_id to prevent duplicate purchase on refresh."
|
||||
- "Restore saved CookieNoticePro consent before GTM starts, so purchase uses the right consent state."
|
||||
- "Use a centered Bootstrap alert and automatic submit after 5 seconds, without a visible fallback button."
|
||||
|
||||
patterns-established:
|
||||
- "Purchase event may be emitted before payment redirect, but must be guarded per transaction in the browser session."
|
||||
|
||||
duration: 35min
|
||||
started: 2026-05-08T00:00:00Z
|
||||
completed: 2026-05-08T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 5 Plan 01: Purchase Redirect Hardening Summary
|
||||
|
||||
**Purchase tracking now fires once per transaction in the browser session, respects restored cookie consent before GTM, and redirects to Przelewy24 after a centered 5-second countdown alert.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~35 min |
|
||||
| Started | 2026-05-08 |
|
||||
| Completed | 2026-05-08 |
|
||||
| Tasks | 3 completed |
|
||||
| Files modified | 2 source files + PAUL docs |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Purchase fires only once per browser session and transaction | Pass | First load pushes ecommerce reset and purchase when no session key exists. |
|
||||
| AC-2: Refresh does not duplicate purchase | Pass | `sessionStorage` key `brzezovka_purchase_sent_{transaction_id}` suppresses repeat push in the same browser session. |
|
||||
| AC-3: Auto redirect returns with visible countdown | Pass | Centered Bootstrap alert shows 5-second countdown and auto-submits the P24 form after 5 seconds. |
|
||||
| AC-4: Saved cookie consent is restored before GTM and purchase | Pass | Layout restores `cnp_consent` and `cnp_prefs` immediately after default denied and before GTM. |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Added a guarded `dataLayer.push()` wrapper around the existing purchase payload.
|
||||
- Preserved the backend purchase payload builder and payment form signing unchanged.
|
||||
- Added pre-GTM saved consent restore for analytics and marketing consent.
|
||||
- Replaced the temporary manual redirect with a centered alert and automatic 5-second redirect.
|
||||
|
||||
## Verification Results
|
||||
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| `php -l templates/tickets/przelewy24.php` | Pass |
|
||||
| `php -l templates/site/layout-logged.php` | Pass |
|
||||
| `rg -n "sessionStorage|brzezovka_purchase_sent_|redirectCountdown|setTimeout|cnp_consent|cnp_prefs" ...` | Pass |
|
||||
| Manual browser check: first purchase fires | Pass - confirmed by user during testing |
|
||||
| Manual browser check: saved consent restored | Pass - confirmed by user after consent fix |
|
||||
| Manual browser check: centered alert without visible button | Pass - user approved after visual correction |
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `templates/tickets/przelewy24.php` | Modified | Added purchase duplicate guard, centered redirect alert, 5-second countdown, and auto-submit. |
|
||||
| `templates/site/layout-logged.php` | Modified | Restores saved CookieNoticePro consent before GTM snippet and page-level purchase push. |
|
||||
| `.paul/phases/05-purchase-redirect-hardening/05-01-PLAN.md` | Created | Plan for the hardening work. |
|
||||
| `.paul/phases/05-purchase-redirect-hardening/05-01-SUMMARY.md` | Created | Completion record. |
|
||||
| `.paul/STATE.md` | Modified | Tracks v0.4 loop closure. |
|
||||
| `.paul/ROADMAP.md` | Modified | Adds and closes Phase 5. |
|
||||
| `.paul/PROJECT.md` | Modified | Marks v0.4 requirements and decisions as validated. |
|
||||
| `.paul/changelog/2026-05-08.md` | Modified | Human-readable changelog entry. |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Use sessionStorage instead of DB state | Scope was refresh protection in the same browser session, not a server-side analytics ledger. | No schema or order-flow changes required. |
|
||||
| Set session key before pushing purchase | Prevents duplicate push if refresh happens immediately after the first page execution. | Slight fail-open behavior remains if storage is unavailable. |
|
||||
| Restore consent before GTM | CookieNoticePro runs at the bottom of the body, too late for the pre-payment purchase push. | Purchase observes saved granted consent on page load. |
|
||||
| Remove visible manual button | User feedback: the button remained visible and the message needed a cleaner UI. | Final redirect page is a centered alert with no visible fallback button. |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| User-requested UI adjustment | 1 | Removed visible fallback button and improved message presentation. |
|
||||
|
||||
**Total impact:** Positive UI cleanup; no tracking or payment behavior risk introduced.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| Purchase initially used default denied consent despite saved cookie acceptance. | Added pre-GTM saved consent restore in `layout-logged.php`. |
|
||||
| Temporary redirect button was still visible and the text looked unpolished. | Replaced it with a centered Bootstrap alert and countdown only. |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- v0.4 tracking redirect hardening is complete.
|
||||
- Purchase event has refresh protection and consent ordering fixed.
|
||||
- Przelewy24 auto-redirect behavior is restored.
|
||||
|
||||
**Concerns:**
|
||||
- `sessionStorage` protects only the same browser session; cross-device or new-session duplication would need server-side tracking if required later.
|
||||
|
||||
**Blockers:**
|
||||
- None.
|
||||
|
||||
---
|
||||
*Phase: 05-purchase-redirect-hardening, Plan: 01*
|
||||
*Completed: 2026-05-08*
|
||||
@@ -45,6 +45,38 @@
|
||||
'ad_personalization': 'denied',
|
||||
'wait_for_update': 500
|
||||
});
|
||||
|
||||
(function() {
|
||||
function getCookie(name) {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = cookies[i].trim();
|
||||
if (cookie.indexOf(name + '=') === 0) {
|
||||
return decodeURIComponent(cookie.substring(name.length + 1));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
var consent = JSON.parse(getCookie('cnp_consent'));
|
||||
var preferences = JSON.parse(getCookie('cnp_prefs'));
|
||||
|
||||
if (consent !== true || !Array.isArray(preferences)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gtag('consent', 'update', {
|
||||
'analytics_storage': preferences.indexOf('analytics') > -1 ? 'granted' : 'denied',
|
||||
'ad_storage': preferences.indexOf('marketing') > -1 ? 'granted' : 'denied',
|
||||
'ad_user_data': preferences.indexOf('marketing') > -1 ? 'granted' : 'denied',
|
||||
'ad_personalization': preferences.indexOf('marketing') > -1 ? 'granted' : 'denied'
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Consent Mode: could not restore saved cookie preferences.', error);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<!-- End Google Consent Mode v2 - default -->
|
||||
<!-- Google Tag Manager -->
|
||||
|
||||
@@ -10,9 +10,32 @@ if (is_array($this->purchase_data_layer ?? null)) {
|
||||
|
||||
<?php if($purchaseDataLayerJson) : ?>
|
||||
<script type="text/javascript">
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
window.dataLayer.push({ ecommerce: null });
|
||||
window.dataLayer.push(<?= $purchaseDataLayerJson; ?>);
|
||||
(function() {
|
||||
var purchaseDataLayer = <?= $purchaseDataLayerJson; ?>;
|
||||
var transactionId = purchaseDataLayer &&
|
||||
purchaseDataLayer.ecommerce &&
|
||||
purchaseDataLayer.ecommerce.transaction_id;
|
||||
var purchaseSent = false;
|
||||
|
||||
if (transactionId) {
|
||||
try {
|
||||
var storageKey = 'brzezovka_purchase_sent_' + transactionId;
|
||||
purchaseSent = sessionStorage.getItem(storageKey) === '1';
|
||||
|
||||
if (!purchaseSent) {
|
||||
sessionStorage.setItem(storageKey, '1');
|
||||
}
|
||||
} catch (error) {
|
||||
purchaseSent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!purchaseSent) {
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
window.dataLayer.push({ ecommerce: null });
|
||||
window.dataLayer.push(purchaseDataLayer);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -58,10 +81,39 @@ if($this->settings['p24']['sandbox']) {
|
||||
<input type="hidden" name="p24_method" value="227">
|
||||
<input type="hidden" name="p24_sign"
|
||||
value="<?= md5( $this -> przelewy24_hash . '|' . $merchant_id . '|' . ( $clientData['order_price'] * 100 ) . '|PLN|' . $crc_key );?>" />
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-md-8 col-lg-6">
|
||||
<div class="alert alert-info text-center shadow-sm mb-0" role="alert">
|
||||
<h4 class="alert-heading mb-3">Przekierowanie do platnosci</h4>
|
||||
<p class="mb-2">Za chwile nastapi automatyczne przejscie do Przelewy24.</p>
|
||||
<p class="mb-0">
|
||||
Pozostalo
|
||||
<strong><span id="redirectCountdown">5</span> s</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
$( document ).ready( function() {
|
||||
$( "#form_data" ).submit();
|
||||
$(document).ready(function() {
|
||||
var secondsLeft = 5;
|
||||
var countdownElement = $("#redirectCountdown");
|
||||
|
||||
var countdownInterval = setInterval(function() {
|
||||
secondsLeft -= 1;
|
||||
countdownElement.text(secondsLeft);
|
||||
|
||||
if (secondsLeft <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
setTimeout(function() {
|
||||
$("#form_data").submit();
|
||||
}, 5000);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user