bagisto/bagisto-gdpr
Bagisto GDPR adds privacy compliance tools to your Bagisto store: cookie consent, data access/export and erasure requests, consent management, and customer anonymization, helping meet GDPR requirements with simple admin controls.
Confirm you're on Bagisto 1.3.x before anything else. The only meaningful release signal is 1.3.2 (Feb 2022), and its single changelog line is "Compatibility with Bagisto v1.3.3." There is no forward-compat claim for Bagisto 2.x. Check composer.json for a pinned bagisto/bagisto:1.3.* line; if you're on 2.x, close the tab.
Install and wire it up (Bagisto 1.x):
composer require bagisto/bagisto-gdpr:^1.3
php artisan vendor:publish --provider="Webkul\GDPR\Providers\GDPRServiceProvider"
php artisan migrate
php artisan config:clear && php artisan route:clear && php artisan view:clear
Bagisto 1.x registers module providers explicitly — do not count on Laravel auto-discovery. Add the provider to config/app.php and verify with:
php artisan route:list | grep -i gdpr
If that returns nothing, nothing else you do matters yet.
Read the package in this order (30–45 minutes, no more):
Providers/*ServiceProvider.php — routes, views, migrations, config, translations, event listeners. This is the map.config/gdpr.php) — cookie categories, enable flags, banner copy, DSAR toggles. This is what you'll actually edit.Routes/admin.php / Routes/shop.php — the exact endpoints for consent capture, DSAR intake, and the admin queue.Database/Migrations/ — what tables appear (gdpr_*, consent logs, request records) and whether they touch customers/orders.Http/Controllers/ + Models/ — how consent is persisted and how export/erasure are implemented.Resources/views/ — the banner partial and the customer privacy pages (this is also your theme-override surface).First use case — the "banner → row" spike. Drop the banner into the storefront layout, load the shop, accept cookies, then query the table directly:
select * from gdpr_consent_logs order by id desc limit 5;
No row means client-only consent — a UI shell, not a workflow. Once rows appear, do the second half: submit a DSAR from the storefront form and confirm a record lands in the admin queue. That two-step (banner→row, form→row) is the fastest way to learn whether this package is an actual workflow or scaffolding you'll have to build on top of.
Assign an owner before you merge. Sixteen stars, zero dependents, no release since 2022. Treat it as internal code the day it enters composer.json.
Gate on the DB row, never on the JS cookie. The package captures and displays consent; enforcement is yours. Add a middleware or Blade helper that reads the persisted consent record for the current customer/session before rendering analytics snippets, marketing pixels, or gated content. Reading document.cookie in Blade is trivially spoofable and is the single most common way stores "implement GDPR" without doing so.
Version your consent. Consent must be re-asked when the category set changes, not just when the cookie expires. Store a consent_version (or a hash of the enabled category list) alongside every row. Bump it whenever you edit the config's categories so previously-consented users get re-prompted. This is a fork-level change — upstream doesn't ship audit-grade versioning.
Turn the DSAR form into a real intake. Use the package's storefront form as the surface, then add what it lacks:
pending → verifying → in_progress → fulfilled | rejected.The admin queue is a list view. Treat it as a starting point for an operations screen, not a finished one.
Export as JSON from a queue, to a private disk. The right-of-access output must be complete and machine-readable — a PDF is not a portability format. Extend the export to pull from every table holding PII: customers, addresses, orders, order_items, invoices, reviews, wishlists, newsletter subscriptions, and the package's own consent log. Then:
Storage::disk('gdpr-private')->put($path, $json);
// deliver a signed, time-limited URL; log every download
Never attach an export to an email, and never write it to the public disk.
Erasure means anonymization, not deletion. This is where GDPR meets commerce and commerce wins on the retention question. Invoices and order records carry tax/legal retention obligations; cascade-deleting them is wrong and will also throw on foreign keys. The pattern that survives audit:
customers PII (name, email, phone, DOB) to placeholders or a stable hash.orders/invoices rows; null or hash the PII fields on the linked address and customer snapshot.Wire ACL properly on the admin side. Add the menu entry through Bagisto's admin menu config and register the ACL resource. The menu item and the route permission are often two separate things in Bagisto; both must be set so that hitting admin/gdpr/... as an unpermitted admin returns 403. Suggested permissions: gdpr.requests.view, gdpr.requests.process.
Add an append-only audit log. Actor, action, subject, timestamp, IP. Hook it via model events or explicit repository calls. The package doesn't give you one, and it's the first thing legal will ask for.
Drive behavior from config, not Blade. Banner copy, cookie lifetime, enable/disable flags — all config-driven, and use Bagisto's channel/locale helpers so per-store behavior is possible. Hardcoding English strings in the partial is a one-way ticket to a fork.
Lean on Bagisto events for the integration seam. Hook customer.registered, order.created, and similar events to stamp consent state or write audit entries — that's cheaper than modifying the package's controllers.
Write these four feature tests before trusting it:
// 1
POST /gdpr/consent with categories → assertDatabaseHas consent log
// 2
erasure job → asserts customers PII scrubbed, order totals + invoice rows intact
// 3
export job → asserts JSON fixture contains a known order's line item
// 4
GET admin/gdpr/requests without ACL → assert 403
php artisan route:list | grep gdpr remains your fastest sanity check after any dependency bump.
1.3.2 patches compatibility for Bagisto 1.3.3 and nothing else. Pin tightly and add a smoke test that loads the banner and the admin queue after every Bagisto patch — that's the only early warning you'll get.config/app.php first, bootstrap/cache/packages.php second, route:list third. In that order.pending past your SLA is a compliance incident, not a bug ticket. Monitor failed_jobs; put an alert on requests aging past threshold.Secure, SameSite=Lax, first-party, and a lifetime that does not outlive the consent record it represents.storage/app/public is one misconfiguration away from a PII leak. Set a dedicated private disk in config and verify with a Storage::disk()->url() assertion in tests.storage/logs/laravel.log. Scrub or lower the level before shipping, and rotate logs.customer.registered, order.created), and Bagisto's repository bindings if you need to override persistence without forking a controller.packages/ or a private VCS, run it through your CI, document the supported Bagisto versions, and maintain it like any other internal module. Budget a named maintainer. That's the actual cost of adopting this package.How can I help you explore Laravel packages today?