Takaisin blogiin
Technical

E-commerce Privacy: Extensions for Magento and PrestaShop

GetCookies-tiimiNovember 11, 202510 min lukuaika
MagentoPrestaShopE-commercePlugins

TLDR: Cookie consent on Magento 2 and PrestaShop requires platform-specific integration—generic plugins often break checkout.

Read full summary

Technical guide to implementing consent management on Magento 2 and PrestaShop. Covers platform architecture differences, plugin selection criteria, checkout integration, and avoiding common pitfalls that hurt conversions.

Summary by Claude AI


title: "Magento 2 and PrestaShop Cookie Consent: Complete Plugin Implementation Guide 2025" slug: "magento-prestashop-cookie-consent-plugins" excerpt: "Master cookie consent implementation on Magento 2 and PrestaShop with our comprehensive guide covering top plugins, custom development, checkout protection, and multi-store configurations for GDPR and ePrivacy compliance." category: "E-commerce Platforms" tags: ["Magento 2", "PrestaShop", "Cookie Consent", "E-commerce", "GDPR", "Plugin Development"] publishedAt: "2025-01-18" readTime: "22 min read"

Magento 2 and PrestaShop Cookie Consent: Complete Plugin Implementation Guide

Implementing cookie consent on enterprise e-commerce platforms like Magento 2 and PrestaShop presents unique challenges that simple WordPress plugins cannot address. These platforms have complex JavaScript architectures, RequireJS module loaders, multi-store configurations, and checkout flows that must remain functional even when marketing cookies are blocked. This comprehensive guide walks you through every aspect of implementing robust cookie consent on both platforms.

Understanding the E-commerce Cookie Consent Challenge

Unlike content websites where blocking cookies simply disables analytics, e-commerce platforms rely on cookies for critical functionality. Shopping carts, wishlists, recently viewed products, and checkout sessions all use cookies. A poorly implemented consent solution can destroy conversions, while an overly permissive one creates legal liability.

The Cookie Landscape on E-commerce Platforms

Before implementing any solution, you need to understand what cookies your platform sets:

Essential Cookies (Always Allowed):

  • Session identifiers (PHPSESSID, form_key)
  • Shopping cart data (cart_id, quote_id)
  • Authentication tokens
  • CSRF protection tokens
  • Currency and store preferences

Analytics Cookies (Require Consent):

  • Google Analytics (_ga, _gid, _gat)
  • Hotjar (_hj*)
  • Facebook Pixel (_fbp)
  • Platform analytics

Marketing Cookies (Require Consent):

  • Retargeting pixels
  • Ad network identifiers
  • Cross-site tracking cookies
  • Social media trackers

Functional Cookies (Gray Area):

  • Recently viewed products
  • Wishlist functionality
  • Product comparisons
  • Persistent login

The functional category is where most e-commerce implementations struggle. These enhance user experience but aren't strictly necessary for purchasing.

Magento 2 Cookie Consent Implementation

Magento 2's architecture is complex, with RequireJS managing JavaScript loading, UI components handling frontend interactions, and a sophisticated event system. Your cookie consent solution must integrate deeply with all these systems.

Understanding Magento 2's JavaScript Architecture

Magento 2 uses RequireJS as its JavaScript module loader. Every script on the page loads through RequireJS, which presents both a challenge and an opportunity for cookie consent:

// How Magento 2 loads scripts through RequireJS
require(['jquery', 'Magento_Customer/js/customer-data'], function($, customerData) {
    // This code runs after modules are loaded
    var cart = customerData.get('cart');
});

To implement cookie consent properly, you need to intercept RequireJS loading and conditionally block modules based on consent state.

Top Magento 2 Cookie Consent Extensions

1. Amasty Cookie Consent (Recommended)

Amasty's Cookie Consent is the most comprehensive solution for Magento 2, offering deep integration with the platform's architecture.

Key Features:

  • RequireJS integration for script blocking
  • Cookie groups with granular control
  • GeoIP-based banner display
  • Automatic cookie scanning
  • Google Consent Mode v2 support
  • IAB TCF 2.2 compatibility

Installation via Composer:

# Add Amasty repository (requires license)
composer config repositories.amasty composer https://composer.amasty.com/enterprise/

# Install the module
composer require amasty/module-gdpr-cookie-compliance

# Enable and deploy
php bin/magento module:enable Amasty_GdprCookie
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f
php bin/magento cache:flush

Configuration for Cookie Groups:

<!-- app/code/YourCompany/CookieCustomization/etc/cookie_groups.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Amasty_GdprCookie:etc/cookie_groups.xsd">
    <group name="essential" enabled="true" required="true">
        <label>Essential Cookies</label>
        <description>Required for the store to function. Cannot be disabled.</description>
        <cookies>
            <cookie name="PHPSESSID"/>
            <cookie name="form_key"/>
            <cookie name="mage-cache-storage"/>
            <cookie name="mage-cache-sessid"/>
            <cookie name="private_content_version"/>
        </cookies>
    </group>

    <group name="analytics" enabled="true" required="false">
        <label>Analytics Cookies</label>
        <description>Help us understand how visitors interact with our store.</description>
        <cookies>
            <cookie name="_ga"/>
            <cookie name="_gid"/>
            <cookie name="_gat"/>
        </cookies>
        <scripts>
            <script>Magento_GoogleAnalytics/js/google-analytics</script>
            <script>Magento_GoogleGtag/js/google-analytics</script>
        </scripts>
    </group>

    <group name="marketing" enabled="true" required="false">
        <label>Marketing Cookies</label>
        <description>Used to deliver personalized advertisements.</description>
        <cookies>
            <cookie name="_fbp"/>
            <cookie name="_fbc"/>
        </cookies>
        <scripts>
            <script>facebook-pixel</script>
            <script>google-ads</script>
        </scripts>
    </group>
</config>

2. MageWorx GDPR Cookie Compliance

MageWorx offers a solid alternative with excellent multi-store support:

Installation:

composer require mageworx/module-gdpr-cookie
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy -f

Multi-store Configuration:

<?php
// app/code/YourCompany/CookieConfig/Model/Config/MultiStoreResolver.php

namespace YourCompany\CookieConfig\Model\Config;

use Magento\Store\Model\StoreManagerInterface;

class MultiStoreResolver
{
    private StoreManagerInterface $storeManager;

    public function __construct(StoreManagerInterface $storeManager)
    {
        $this->storeManager = $storeManager;
    }

    public function getCookieConfigForCurrentStore(): array
    {
        $storeId = $this->storeManager->getStore()->getId();
        $storeCode = $this->storeManager->getStore()->getCode();

        // Define store-specific configurations
        $configs = [
            'eu_store' => [
                'show_banner' => true,
                'default_consent' => false,
                'tcf_enabled' => true,
                'gcm_enabled' => true,
            ],
            'us_store' => [
                'show_banner' => true,
                'default_consent' => true, // Opt-out model for US
                'tcf_enabled' => false,
                'gcm_enabled' => true,
            ],
            'uk_store' => [
                'show_banner' => true,
                'default_consent' => false,
                'tcf_enabled' => true,
                'gcm_enabled' => true,
            ],
        ];

        return $configs[$storeCode] ?? $configs['eu_store'];
    }
}

Custom Magento 2 Cookie Consent Implementation

For organizations requiring complete control, here's how to build a custom solution:

Module Structure:

app/code/YourCompany/CookieConsent/
├── registration.php
├── etc/
│   ├── module.xml
│   ├── di.xml
│   ├── frontend/
│   │   ├── routes.xml
│   │   └── requirejs-config.js
│   └── adminhtml/
│       └── system.xml
├── Block/
│   └── CookieBanner.php
├── Model/
│   ├── ConsentManager.php
│   └── CookieRegistry.php
├── Controller/
│   └── Consent/
│       ├── Save.php
│       └── Get.php
├── view/
│   └── frontend/
│       ├── layout/
│       │   └── default.xml
│       ├── templates/
│       │   └── cookie-banner.phtml
│       └── web/
│           ├── js/
│           │   ├── cookie-consent.js
│           │   └── script-blocker.js
│           └── css/
│               └── cookie-banner.css
└── Plugin/
    └── RequireJsBlocker.php

Core Consent Manager:

<?php
// app/code/YourCompany/CookieConsent/Model/ConsentManager.php

declare(strict_types=1);

namespace YourCompany\CookieConsent\Model;

use Magento\Framework\Stdlib\CookieManagerInterface;
use Magento\Framework\Stdlib\Cookie\CookieMetadataFactory;
use Magento\Framework\Session\SessionManagerInterface;
use Magento\Framework\Serialize\Serializer\Json;

class ConsentManager
{
    private const CONSENT_COOKIE_NAME = 'cookie_consent';
    private const CONSENT_COOKIE_DURATION = 31536000; // 1 year

    private CookieManagerInterface $cookieManager;
    private CookieMetadataFactory $cookieMetadataFactory;
    private SessionManagerInterface $sessionManager;
    private Json $json;

    public function __construct(
        CookieManagerInterface $cookieManager,
        CookieMetadataFactory $cookieMetadataFactory,
        SessionManagerInterface $sessionManager,
        Json $json
    ) {
        $this->cookieManager = $cookieManager;
        $this->cookieMetadataFactory = $cookieMetadataFactory;
        $this->sessionManager = $sessionManager;
        $this->json = $json;
    }

    public function saveConsent(array $consents): void
    {
        $consentData = [
            'essential' => true, // Always true
            'analytics' => $consents['analytics'] ?? false,
            'marketing' => $consents['marketing'] ?? false,
            'functional' => $consents['functional'] ?? false,
            'timestamp' => time(),
            'version' => $this->getConsentVersion(),
        ];

        $metadata = $this->cookieMetadataFactory
            ->createPublicCookieMetadata()
            ->setDuration(self::CONSENT_COOKIE_DURATION)
            ->setPath($this->sessionManager->getCookiePath())
            ->setDomain($this->sessionManager->getCookieDomain())
            ->setSecure(true)
            ->setHttpOnly(false); // Must be accessible to JS

        $this->cookieManager->setPublicCookie(
            self::CONSENT_COOKIE_NAME,
            $this->json->serialize($consentData),
            $metadata
        );

        // Remove cookies for denied categories
        $this->removeRejectedCookies($consents);
    }

    public function getConsent(): array
    {
        $consentCookie = $this->cookieManager->getCookie(self::CONSENT_COOKIE_NAME);

        if ($consentCookie) {
            try {
                $consent = $this->json->unserialize($consentCookie);

                // Check if consent version is current
                if (($consent['version'] ?? '') !== $this->getConsentVersion()) {
                    return $this->getDefaultConsent();
                }

                return $consent;
            } catch (\Exception $e) {
                return $this->getDefaultConsent();
            }
        }

        return $this->getDefaultConsent();
    }

    public function hasConsented(): bool
    {
        return $this->cookieManager->getCookie(self::CONSENT_COOKIE_NAME) !== null;
    }

    public function hasConsentFor(string $category): bool
    {
        $consent = $this->getConsent();
        return $consent[$category] ?? false;
    }

    private function removeRejectedCookies(array $consents): void
    {
        $cookieRegistry = [
            'analytics' => ['_ga', '_gid', '_gat', '_gat_*'],
            'marketing' => ['_fbp', '_fbc', 'fr', '_gcl_*'],
            'functional' => ['recently_viewed_*', 'wishlist_*'],
        ];

        foreach ($cookieRegistry as $category => $cookies) {
            if (!($consents[$category] ?? false)) {
                foreach ($cookies as $cookieName) {
                    if (strpos($cookieName, '*') !== false) {
                        // Pattern-based removal handled in JS
                        continue;
                    }
                    $this->deleteCookie($cookieName);
                }
            }
        }
    }

    private function deleteCookie(string $name): void
    {
        $metadata = $this->cookieMetadataFactory
            ->createPublicCookieMetadata()
            ->setPath($this->sessionManager->getCookiePath())
            ->setDomain($this->sessionManager->getCookieDomain());

        $this->cookieManager->deleteCookie($name, $metadata);
    }

    private function getDefaultConsent(): array
    {
        return [
            'essential' => true,
            'analytics' => false,
            'marketing' => false,
            'functional' => false,
            'timestamp' => null,
            'version' => $this->getConsentVersion(),
        ];
    }

    private function getConsentVersion(): string
    {
        return '2025.01.18'; // Update when cookie policy changes
    }
}

RequireJS Script Blocker:

// app/code/YourCompany/CookieConsent/view/frontend/web/js/script-blocker.js

define([
    'jquery',
    'mage/cookies'
], function($) {
    'use strict';

    return {
        blockedModules: {
            'analytics': [
                'Magento_GoogleAnalytics/js/google-analytics',
                'Magento_GoogleGtag/js/google-analytics',
                'googleTagManagerScript'
            ],
            'marketing': [
                'facebookPixel',
                'googleAds',
                'criteo'
            ]
        },

        getConsent: function() {
            var consentCookie = $.mage.cookies.get('cookie_consent');
            if (consentCookie) {
                try {
                    return JSON.parse(consentCookie);
                } catch (e) {
                    return null;
                }
            }
            return null;
        },

        isModuleAllowed: function(moduleName) {
            var consent = this.getConsent();
            if (!consent) {
                return false; // No consent = block non-essential
            }

            for (var category in this.blockedModules) {
                if (this.blockedModules[category].indexOf(moduleName) !== -1) {
                    return consent[category] === true;
                }
            }

            return true; // Module not in any blocked category
        },

        interceptRequireJS: function() {
            var self = this;
            var originalRequire = window.require;

            window.require = function(deps, callback, errback) {
                if (Array.isArray(deps)) {
                    deps = deps.filter(function(dep) {
                        var allowed = self.isModuleAllowed(dep);
                        if (!allowed) {
                            console.log('CookieConsent: Blocked module:', dep);
                        }
                        return allowed;
                    });
                }
                return originalRequire.call(this, deps, callback, errback);
            };

            // Copy properties from original require
            for (var prop in originalRequire) {
                if (originalRequire.hasOwnProperty(prop)) {
                    window.require[prop] = originalRequire[prop];
                }
            }
        },

        initialize: function() {
            this.interceptRequireJS();
        }
    };
});

Cookie Banner Template:

<?php
// app/code/YourCompany/CookieConsent/view/frontend/templates/cookie-banner.phtml
/** @var \YourCompany\CookieConsent\Block\CookieBanner $block */

$consentManager = $block->getConsentManager();
$showBanner = !$consentManager->hasConsented();
?>

<div id="cookie-consent-banner"
     class="cookie-consent-banner <?= $showBanner ? '' : 'hidden' ?>"
     data-mage-init='{"cookieConsent": {
         "saveUrl": "<?= $block->escapeUrl($block->getUrl('cookie/consent/save')) ?>",
         "categories": <?= /* @noEscape */ $block->getCategoriesJson() ?>
     }}'>

    <div class="cookie-consent-content">
        <div class="cookie-consent-header">
            <h3><?= $block->escapeHtml(__('We value your privacy')) ?></h3>
            <p><?= $block->escapeHtml(__('We use cookies to enhance your shopping experience, analyze site traffic, and personalize content. You can choose which cookies to accept below.')) ?></p>
        </div>

        <div class="cookie-consent-categories">
            <div class="cookie-category essential">
                <label>
                    <input type="checkbox" name="essential" checked disabled>
                    <span class="category-name"><?= $block->escapeHtml(__('Essential')) ?></span>
                    <span class="category-badge"><?= $block->escapeHtml(__('Required')) ?></span>
                </label>
                <p class="category-description"><?= $block->escapeHtml(__('Necessary for the website to function. Includes shopping cart, checkout, and security features.')) ?></p>
            </div>

            <div class="cookie-category analytics">
                <label>
                    <input type="checkbox" name="analytics" id="consent-analytics">
                    <span class="category-name"><?= $block->escapeHtml(__('Analytics')) ?></span>
                </label>
                <p class="category-description"><?= $block->escapeHtml(__('Help us understand how visitors interact with our store through anonymous data collection.')) ?></p>
            </div>

            <div class="cookie-category marketing">
                <label>
                    <input type="checkbox" name="marketing" id="consent-marketing">
                    <span class="category-name"><?= $block->escapeHtml(__('Marketing')) ?></span>
                </label>
                <p class="category-description"><?= $block->escapeHtml(__('Used to show you relevant advertisements on other websites based on your interests.')) ?></p>
            </div>

            <div class="cookie-category functional">
                <label>
                    <input type="checkbox" name="functional" id="consent-functional">
                    <span class="category-name"><?= $block->escapeHtml(__('Functional')) ?></span>
                </label>
                <p class="category-description"><?= $block->escapeHtml(__('Enable enhanced features like recently viewed products and wishlist persistence.')) ?></p>
            </div>
        </div>

        <div class="cookie-consent-actions">
            <button type="button" class="action secondary" data-action="reject-all">
                <?= $block->escapeHtml(__('Reject All')) ?>
            </button>
            <button type="button" class="action secondary" data-action="accept-selected">
                <?= $block->escapeHtml(__('Accept Selected')) ?>
            </button>
            <button type="button" class="action primary" data-action="accept-all">
                <?= $block->escapeHtml(__('Accept All')) ?>
            </button>
        </div>

        <div class="cookie-consent-footer">
            <a href="<?= $block->escapeUrl($block->getPrivacyPolicyUrl()) ?>"><?= $block->escapeHtml(__('Privacy Policy')) ?></a>
            <a href="<?= $block->escapeUrl($block->getCookiePolicyUrl()) ?>"><?= $block->escapeHtml(__('Cookie Policy')) ?></a>
        </div>
    </div>
</div>

Protecting Checkout from Cookie Blocking

The checkout process in Magento 2 must never be disrupted by cookie consent. Here's how to ensure checkout always works:

<?php
// app/code/YourCompany/CookieConsent/Model/CheckoutProtection.php

declare(strict_types=1);

namespace YourCompany\CookieConsent\Model;

class CheckoutProtection
{
    /**
     * List of cookies that must never be blocked during checkout
     */
    private const CHECKOUT_ESSENTIAL_COOKIES = [
        'PHPSESSID',
        'form_key',
        'mage-cache-storage',
        'mage-cache-storage-section-invalidation',
        'mage-messages',
        'private_content_version',
        'section_data_ids',
        'store',
        'mage-translation-storage',
        'mage-translation-file-version',
        'product_data_storage',
        'mage-cache-sessid',
        'persistent_shopping_cart',
    ];

    /**
     * JavaScript modules essential for checkout
     */
    private const CHECKOUT_ESSENTIAL_SCRIPTS = [
        'Magento_Checkout/js/view/minicart',
        'Magento_Checkout/js/sidebar',
        'Magento_Checkout/js/checkout-loader',
        'Magento_Checkout/js/region-updater',
        'Magento_Customer/js/customer-data',
        'Magento_Catalog/js/storage-manager',
        'Magento_Ui/js/core/app',
        'uiComponent',
        'Magento_Payment/js/view/payment',
        'Magento_Checkout/js/action/place-order',
    ];

    public function getProtectedCookies(): array
    {
        return self::CHECKOUT_ESSENTIAL_COOKIES;
    }

    public function getProtectedScripts(): array
    {
        return self::CHECKOUT_ESSENTIAL_SCRIPTS;
    }

    public function isProtectedCookie(string $cookieName): bool
    {
        return in_array($cookieName, self::CHECKOUT_ESSENTIAL_COOKIES, true);
    }

    public function isProtectedScript(string $scriptName): bool
    {
        foreach (self::CHECKOUT_ESSENTIAL_SCRIPTS as $protected) {
            if (strpos($scriptName, $protected) !== false) {
                return true;
            }
        }
        return false;
    }
}

PrestaShop Cookie Consent Implementation

PrestaShop has a different architecture than Magento, using Smarty templates by default (Twig in later versions), hooks for extensibility, and a simpler JavaScript setup. However, PrestaShop's vibrant module marketplace offers several robust solutions.

Understanding PrestaShop's Cookie Architecture

PrestaShop sets various cookies for functionality:

// PrestaShop Core Cookies
$essential_cookies = [
    'PrestaShop-*' => 'Session cookie with shop hash',
    'PHPSESSID' => 'PHP Session ID',
    'COOKIE_ACCEPTED' => 'Cookie consent state (if using built-in)',
];

// Common Third-Party Module Cookies
$module_cookies = [
    'viewed_products' => 'Recently viewed products',
    'compare_products' => 'Product comparison',
    'wishlist_*' => 'Wishlist functionality',
];

Top PrestaShop Cookie Consent Modules

1. PrestaShop Official GDPR Module

The official module is free and provides a good baseline:

Installation:

  1. Download from PrestaShop Addons
  2. Upload via Back Office → Modules → Upload Module
  3. Configure in Modules → Module Manager → GDPR

Configuration Code:

<?php
// modules/psgdpr/classes/GDPRConsent.php - Understanding the module

class GDPRConsent
{
    // Cookie categories defined by the module
    const CATEGORY_ESSENTIAL = 1;
    const CATEGORY_ANALYTICS = 2;
    const CATEGORY_MARKETING = 3;
    const CATEGORY_FUNCTIONAL = 4;

    public static function getConsentForModule($moduleName)
    {
        $context = Context::getContext();
        $customerId = $context->customer->id ?? null;

        if (!$customerId) {
            // Check cookie for guest consent
            return self::getGuestConsent($moduleName);
        }

        return Db::getInstance()->getValue(
            'SELECT consent
             FROM `' . _DB_PREFIX_ . 'psgdpr_consent_log`
             WHERE id_customer = ' . (int)$customerId . '
             AND module_name = "' . pSQL($moduleName) . '"
             ORDER BY date_add DESC'
        );
    }
}

2. PrestaHero Cookie Law Pro (Recommended)

PrestaHero's module offers the most comprehensive solution for PrestaShop:

Key Features:

  • Automatic cookie scanning
  • Script blocking with RegEx patterns
  • YouTube/Vimeo embed blocking
  • Google Consent Mode integration
  • IAB TCF 2.2 support
  • Multi-language support

Custom Integration Example:

<?php
// modules/your_module/your_module.php - Integrating with Cookie Law Pro

class YourModule extends Module
{
    public function __construct()
    {
        $this->name = 'your_module';
        // ... standard constructor
    }

    public function hookDisplayHeader($params)
    {
        // Check if Cookie Law Pro is installed and consent is given
        if (Module::isEnabled('ph_cookielaw')) {
            $cookieConsent = $this->getCookieLawConsent();

            if (!$cookieConsent['analytics']) {
                return ''; // Don't load analytics scripts
            }
        }

        return $this->display(__FILE__, 'views/templates/hook/header.tpl');
    }

    private function getCookieLawConsent()
    {
        $cookie = Context::getContext()->cookie;
        $consent = json_decode($cookie->ph_cookielaw_consent ?? '{}', true);

        return [
            'essential' => true,
            'analytics' => $consent['analytics'] ?? false,
            'marketing' => $consent['marketing'] ?? false,
            'functional' => $consent['functional'] ?? false,
        ];
    }
}

Custom PrestaShop Cookie Consent Module

For complete control, here's how to build a custom module:

Module Structure:

modules/customcookieconsent/
├── customcookieconsent.php
├── config.xml
├── controllers/
│   └── front/
│       └── consent.php
├── classes/
│   ├── ConsentManager.php
│   └── CookieScanner.php
├── views/
│   ├── templates/
│   │   └── hook/
│   │       ├── cookie_banner.tpl
│   │       └── cookie_settings.tpl
│   ├── js/
│   │   └── cookie-consent.js
│   └── css/
│       └── cookie-banner.css
└── translations/

Main Module File:

<?php
// modules/customcookieconsent/customcookieconsent.php

if (!defined('_PS_VERSION_')) {
    exit;
}

class CustomCookieConsent extends Module
{
    public function __construct()
    {
        $this->name = 'customcookieconsent';
        $this->tab = 'front_office_features';
        $this->version = '1.0.0';
        $this->author = 'Your Company';
        $this->need_instance = 0;
        $this->bootstrap = true;

        parent::__construct();

        $this->displayName = $this->l('Custom Cookie Consent');
        $this->description = $this->l('GDPR-compliant cookie consent management');
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
        $this->ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_];
    }

    public function install()
    {
        return parent::install()
            && $this->registerHook('displayHeader')
            && $this->registerHook('displayFooterBefore')
            && $this->registerHook('actionFrontControllerSetMedia')
            && $this->installDb();
    }

    private function installDb()
    {
        $sql = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'cookie_consent_log` (
            `id_consent` INT UNSIGNED NOT NULL AUTO_INCREMENT,
            `id_customer` INT UNSIGNED NULL,
            `id_guest` INT UNSIGNED NULL,
            `consent_data` TEXT NOT NULL,
            `ip_address` VARCHAR(45) NOT NULL,
            `user_agent` VARCHAR(255) NULL,
            `date_add` DATETIME NOT NULL,
            `date_upd` DATETIME NOT NULL,
            PRIMARY KEY (`id_consent`),
            INDEX `idx_customer` (`id_customer`),
            INDEX `idx_guest` (`id_guest`),
            INDEX `idx_date` (`date_add`)
        ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8mb4;';

        return Db::getInstance()->execute($sql);
    }

    public function hookActionFrontControllerSetMedia()
    {
        $this->context->controller->registerStylesheet(
            'cookie-consent-css',
            'modules/' . $this->name . '/views/css/cookie-banner.css',
            ['media' => 'all', 'priority' => 100]
        );

        $this->context->controller->registerJavascript(
            'cookie-consent-js',
            'modules/' . $this->name . '/views/js/cookie-consent.js',
            ['position' => 'head', 'priority' => 1]
        );
    }

    public function hookDisplayFooterBefore($params)
    {
        $consentManager = new ConsentManager($this->context);

        if ($consentManager->hasConsented()) {
            return ''; // Don't show banner if already consented
        }

        $this->context->smarty->assign([
            'cookie_categories' => $this->getCookieCategories(),
            'privacy_url' => $this->context->link->getCMSLink(
                Configuration::get('PS_CONDITIONS_CMS_ID')
            ),
            'consent_save_url' => $this->context->link->getModuleLink(
                $this->name,
                'consent',
                ['action' => 'save']
            ),
        ]);

        return $this->display(__FILE__, 'views/templates/hook/cookie_banner.tpl');
    }

    private function getCookieCategories()
    {
        return [
            'essential' => [
                'name' => $this->l('Essential'),
                'description' => $this->l('Required for the store to function'),
                'required' => true,
                'cookies' => ['PrestaShop-*', 'PHPSESSID'],
            ],
            'analytics' => [
                'name' => $this->l('Analytics'),
                'description' => $this->l('Help us understand visitor behavior'),
                'required' => false,
                'cookies' => ['_ga', '_gid', '_gat'],
            ],
            'marketing' => [
                'name' => $this->l('Marketing'),
                'description' => $this->l('Used for targeted advertising'),
                'required' => false,
                'cookies' => ['_fbp', '_fbc', 'fr'],
            ],
            'functional' => [
                'name' => $this->l('Functional'),
                'description' => $this->l('Enhanced features like wishlists'),
                'required' => false,
                'cookies' => ['viewed_products', 'wishlist_*'],
            ],
        ];
    }
}

Consent Manager Class:

<?php
// modules/customcookieconsent/classes/ConsentManager.php

class ConsentManager
{
    private const CONSENT_COOKIE_NAME = 'cc_consent';
    private const CONSENT_DURATION = 365; // days

    private $context;

    public function __construct(Context $context)
    {
        $this->context = $context;
    }

    public function saveConsent(array $categories): bool
    {
        $consentData = [
            'essential' => true,
            'analytics' => (bool)($categories['analytics'] ?? false),
            'marketing' => (bool)($categories['marketing'] ?? false),
            'functional' => (bool)($categories['functional'] ?? false),
            'timestamp' => time(),
            'version' => $this->getConsentVersion(),
        ];

        // Save to cookie
        $this->context->cookie->__set(
            self::CONSENT_COOKIE_NAME,
            json_encode($consentData)
        );

        // Log to database for audit
        $this->logConsent($consentData);

        // Delete cookies for rejected categories
        $this->cleanRejectedCookies($consentData);

        return true;
    }

    public function getConsent(): array
    {
        $cookieValue = $this->context->cookie->__get(self::CONSENT_COOKIE_NAME);

        if ($cookieValue) {
            $consent = json_decode($cookieValue, true);

            if (($consent['version'] ?? '') === $this->getConsentVersion()) {
                return $consent;
            }
        }

        return $this->getDefaultConsent();
    }

    public function hasConsented(): bool
    {
        $consent = $this->getConsent();
        return isset($consent['timestamp']);
    }

    public function hasConsentFor(string $category): bool
    {
        $consent = $this->getConsent();
        return $consent[$category] ?? false;
    }

    private function logConsent(array $consentData): void
    {
        $customerId = $this->context->customer->id ?? null;
        $guestId = $this->context->cookie->id_guest ?? null;

        Db::getInstance()->insert('cookie_consent_log', [
            'id_customer' => $customerId,
            'id_guest' => $guestId,
            'consent_data' => pSQL(json_encode($consentData)),
            'ip_address' => pSQL(Tools::getRemoteAddr()),
            'user_agent' => pSQL($_SERVER['HTTP_USER_AGENT'] ?? ''),
            'date_add' => date('Y-m-d H:i:s'),
            'date_upd' => date('Y-m-d H:i:s'),
        ]);
    }

    private function cleanRejectedCookies(array $consent): void
    {
        $cookieMap = [
            'analytics' => ['_ga', '_gid', '_gat'],
            'marketing' => ['_fbp', '_fbc', 'fr'],
        ];

        foreach ($cookieMap as $category => $cookies) {
            if (!$consent[$category]) {
                foreach ($cookies as $cookieName) {
                    if (isset($_COOKIE[$cookieName])) {
                        setcookie($cookieName, '', time() - 3600, '/');
                    }
                }
            }
        }
    }

    private function getDefaultConsent(): array
    {
        return [
            'essential' => true,
            'analytics' => false,
            'marketing' => false,
            'functional' => false,
            'timestamp' => null,
            'version' => $this->getConsentVersion(),
        ];
    }

    private function getConsentVersion(): string
    {
        return Configuration::get('COOKIE_CONSENT_VERSION') ?: '1.0';
    }
}

JavaScript for Script Blocking:

// modules/customcookieconsent/views/js/cookie-consent.js

(function() {
    'use strict';

    const CookieConsent = {
        config: {
            cookieName: 'cc_consent',
            blockedScripts: {
                analytics: [
                    'google-analytics.com',
                    'googletagmanager.com',
                    'analytics.js',
                    'gtag.js'
                ],
                marketing: [
                    'facebook.net',
                    'connect.facebook.net',
                    'fbevents.js',
                    'doubleclick.net'
                ]
            }
        },

        init: function() {
            this.interceptScriptLoading();
            this.blockInlineScripts();
            this.setupBannerListeners();
        },

        getConsent: function() {
            const cookie = this.getCookie(this.config.cookieName);
            if (cookie) {
                try {
                    return JSON.parse(cookie);
                } catch (e) {
                    return null;
                }
            }
            return null;
        },

        getCookie: function(name) {
            const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
            return match ? decodeURIComponent(match[2]) : null;
        },

        interceptScriptLoading: function() {
            const self = this;
            const originalCreateElement = document.createElement.bind(document);

            document.createElement = function(tagName) {
                const element = originalCreateElement(tagName);

                if (tagName.toLowerCase() === 'script') {
                    const originalSetAttribute = element.setAttribute.bind(element);

                    element.setAttribute = function(name, value) {
                        if (name === 'src' && self.shouldBlockScript(value)) {
                            console.log('CookieConsent: Blocked script:', value);
                            return;
                        }
                        return originalSetAttribute(name, value);
                    };

                    Object.defineProperty(element, 'src', {
                        set: function(value) {
                            if (self.shouldBlockScript(value)) {
                                console.log('CookieConsent: Blocked script src:', value);
                                return;
                            }
                            originalSetAttribute('src', value);
                        },
                        get: function() {
                            return element.getAttribute('src');
                        }
                    });
                }

                return element;
            };
        },

        shouldBlockScript: function(src) {
            const consent = this.getConsent();

            if (!consent) {
                // No consent given, block all non-essential
                return this.isBlockedScript(src, 'analytics') ||
                       this.isBlockedScript(src, 'marketing');
            }

            for (const category in this.config.blockedScripts) {
                if (!consent[category] && this.isBlockedScript(src, category)) {
                    return true;
                }
            }

            return false;
        },

        isBlockedScript: function(src, category) {
            const patterns = this.config.blockedScripts[category] || [];
            return patterns.some(pattern => src.includes(pattern));
        },

        blockInlineScripts: function() {
            // Find and neutralize inline scripts with data-category
            document.querySelectorAll('script[data-cookie-category]').forEach(script => {
                const category = script.getAttribute('data-cookie-category');
                const consent = this.getConsent();

                if (!consent || !consent[category]) {
                    script.type = 'text/plain'; // Prevents execution
                }
            });
        },

        setupBannerListeners: function() {
            const banner = document.getElementById('cookie-consent-banner');
            if (!banner) return;

            banner.addEventListener('click', (e) => {
                const action = e.target.dataset.action;

                switch (action) {
                    case 'accept-all':
                        this.saveConsent({ analytics: true, marketing: true, functional: true });
                        break;
                    case 'reject-all':
                        this.saveConsent({ analytics: false, marketing: false, functional: false });
                        break;
                    case 'accept-selected':
                        this.saveSelectedConsent();
                        break;
                }
            });
        },

        saveConsent: function(categories) {
            fetch(window.consentSaveUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(categories)
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    this.hideBanner();
                    if (categories.analytics || categories.marketing) {
                        // Reload to enable blocked scripts
                        window.location.reload();
                    }
                }
            });
        },

        saveSelectedConsent: function() {
            const categories = {
                analytics: document.getElementById('consent-analytics')?.checked || false,
                marketing: document.getElementById('consent-marketing')?.checked || false,
                functional: document.getElementById('consent-functional')?.checked || false
            };
            this.saveConsent(categories);
        },

        hideBanner: function() {
            const banner = document.getElementById('cookie-consent-banner');
            if (banner) {
                banner.classList.add('hidden');
            }
        }
    };

    // Initialize when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => CookieConsent.init());
    } else {
        CookieConsent.init();
    }

    // Expose for external access
    window.CookieConsent = CookieConsent;
})();

Multi-Store and Multi-Language Configuration

Both Magento and PrestaShop support multi-store setups. Cookie consent must be configured per store/language to comply with regional regulations.

Magento 2 Multi-Website Configuration

<?php
// app/code/YourCompany/CookieConsent/Model/Config/StoreConfigProvider.php

declare(strict_types=1);

namespace YourCompany\CookieConsent\Model\Config;

use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

class StoreConfigProvider
{
    private StoreManagerInterface $storeManager;
    private ScopeConfigInterface $scopeConfig;

    // GeoIP to Store mapping
    private const GEO_STORE_MAP = [
        'EU' => 'eu_regulations',
        'GB' => 'uk_regulations',
        'US_CA' => 'ccpa_regulations',
        'BR' => 'lgpd_regulations',
        'default' => 'default_regulations',
    ];

    public function __construct(
        StoreManagerInterface $storeManager,
        ScopeConfigInterface $scopeConfig
    ) {
        $this->storeManager = $storeManager;
        $this->scopeConfig = $scopeConfig;
    }

    public function getConsentConfig(): array
    {
        $storeId = $this->storeManager->getStore()->getId();
        $region = $this->detectRegion();

        $baseConfig = [
            'banner_enabled' => $this->scopeConfig->isSetFlag(
                'cookie_consent/general/enabled',
                ScopeInterface::SCOPE_STORE,
                $storeId
            ),
            'banner_position' => $this->scopeConfig->getValue(
                'cookie_consent/general/position',
                ScopeInterface::SCOPE_STORE,
                $storeId
            ),
            'consent_model' => $this->scopeConfig->getValue(
                'cookie_consent/general/consent_model',
                ScopeInterface::SCOPE_STORE,
                $storeId
            ),
        ];

        // Apply regional overrides
        return array_merge($baseConfig, $this->getRegionalConfig($region));
    }

    private function getRegionalConfig(string $region): array
    {
        $configs = [
            'eu_regulations' => [
                'consent_model' => 'opt-in',
                'show_reject_all' => true,
                'tcf_enabled' => true,
                'gcm_enabled' => true,
                'default_analytics' => false,
                'default_marketing' => false,
            ],
            'uk_regulations' => [
                'consent_model' => 'opt-in',
                'show_reject_all' => true,
                'tcf_enabled' => true,
                'gcm_enabled' => true,
                'default_analytics' => false,
                'default_marketing' => false,
            ],
            'ccpa_regulations' => [
                'consent_model' => 'opt-out',
                'show_reject_all' => true,
                'show_do_not_sell' => true,
                'tcf_enabled' => false,
                'gcm_enabled' => true,
                'default_analytics' => true,
                'default_marketing' => true,
            ],
            'lgpd_regulations' => [
                'consent_model' => 'opt-in',
                'show_reject_all' => true,
                'tcf_enabled' => false,
                'gcm_enabled' => true,
                'default_analytics' => false,
                'default_marketing' => false,
            ],
            'default_regulations' => [
                'consent_model' => 'opt-in',
                'show_reject_all' => true,
                'tcf_enabled' => false,
                'gcm_enabled' => true,
                'default_analytics' => false,
                'default_marketing' => false,
            ],
        ];

        return $configs[$region] ?? $configs['default_regulations'];
    }

    private function detectRegion(): string
    {
        // Implement GeoIP detection or use store configuration
        // This is a simplified example
        $storeCode = $this->storeManager->getStore()->getCode();

        $storeRegionMap = [
            'eu_de' => 'eu_regulations',
            'eu_fr' => 'eu_regulations',
            'uk' => 'uk_regulations',
            'us' => 'ccpa_regulations',
            'br' => 'lgpd_regulations',
        ];

        return $storeRegionMap[$storeCode] ?? 'default_regulations';
    }
}

PrestaShop Multi-Shop Configuration

<?php
// modules/customcookieconsent/classes/MultiShopConfig.php

class MultiShopConfig
{
    public static function getShopConfig($shopId = null)
    {
        if ($shopId === null) {
            $shopId = Context::getContext()->shop->id;
        }

        // Get shop-specific configuration
        $config = [
            'enabled' => Configuration::get('COOKIE_CONSENT_ENABLED', null, null, $shopId),
            'consent_model' => Configuration::get('COOKIE_CONSENT_MODEL', null, null, $shopId),
            'tcf_enabled' => Configuration::get('COOKIE_CONSENT_TCF', null, null, $shopId),
            'gcm_enabled' => Configuration::get('COOKIE_CONSENT_GCM', null, null, $shopId),
            'banner_text' => Configuration::get('COOKIE_CONSENT_TEXT', null, null, $shopId),
        ];

        // Get language-specific texts
        $languages = Language::getLanguages(true, $shopId);
        $config['texts'] = [];

        foreach ($languages as $lang) {
            $config['texts'][$lang['id_lang']] = [
                'banner_title' => Configuration::get(
                    'COOKIE_CONSENT_TITLE',
                    $lang['id_lang'],
                    null,
                    $shopId
                ),
                'banner_description' => Configuration::get(
                    'COOKIE_CONSENT_DESC',
                    $lang['id_lang'],
                    null,
                    $shopId
                ),
                'accept_button' => Configuration::get(
                    'COOKIE_CONSENT_ACCEPT',
                    $lang['id_lang'],
                    null,
                    $shopId
                ),
                'reject_button' => Configuration::get(
                    'COOKIE_CONSENT_REJECT',
                    $lang['id_lang'],
                    null,
                    $shopId
                ),
            ];
        }

        return $config;
    }

    public static function getRegionalRules($countryIso)
    {
        $rules = [
            // EU Countries
            'DE' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],
            'FR' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],
            'IT' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],
            'ES' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],
            'NL' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],

            // UK
            'GB' => ['model' => 'opt-in', 'tcf' => true, 'strict' => true],

            // US States
            'US' => ['model' => 'opt-out', 'tcf' => false, 'strict' => false],

            // Brazil
            'BR' => ['model' => 'opt-in', 'tcf' => false, 'strict' => true],

            // Default
            'default' => ['model' => 'opt-in', 'tcf' => false, 'strict' => false],
        ];

        return $rules[$countryIso] ?? $rules['default'];
    }
}

Testing Your Cookie Consent Implementation

Thorough testing is critical for e-commerce cookie consent. A bug could either expose you to legal risk or break your checkout.

Automated Testing Suite

<?php
// Test class for Magento 2

namespace YourCompany\CookieConsent\Test\Integration;

use Magento\TestFramework\TestCase\AbstractController;

class CookieConsentTest extends AbstractController
{
    /**
     * @magentoAppArea frontend
     */
    public function testBannerDisplaysForNewVisitor()
    {
        $this->dispatch('/');

        $body = $this->getResponse()->getBody();
        $this->assertStringContainsString('cookie-consent-banner', $body);
        $this->assertStringContainsString('Essential', $body);
        $this->assertStringContainsString('Analytics', $body);
    }

    /**
     * @magentoAppArea frontend
     */
    public function testCheckoutWorksWithoutConsent()
    {
        // Ensure no consent cookie exists
        $this->getRequest()->setCookies([]);

        // Navigate to checkout
        $this->dispatch('/checkout/');

        // Verify checkout page loads
        $body = $this->getResponse()->getBody();
        $this->assertStringContainsString('checkout-container', $body);
        $this->assertEquals(200, $this->getResponse()->getStatusCode());
    }

    /**
     * @magentoAppArea frontend
     */
    public function testAnalyticsBlockedWithoutConsent()
    {
        $this->dispatch('/');

        $body = $this->getResponse()->getBody();

        // Google Analytics should not be present without consent
        $this->assertStringNotContainsString('google-analytics.com/analytics.js', $body);
        $this->assertStringNotContainsString('gtag(', $body);
    }

    /**
     * @magentoAppArea frontend
     * @magentoConfigFixture current_store google/analytics/active 1
     */
    public function testAnalyticsLoadedWithConsent()
    {
        // Set consent cookie
        $consent = json_encode([
            'essential' => true,
            'analytics' => true,
            'marketing' => false,
            'timestamp' => time(),
        ]);

        $this->getRequest()->setCookies(['cookie_consent' => $consent]);
        $this->dispatch('/');

        $body = $this->getResponse()->getBody();

        // Now analytics should be present
        $this->assertStringContainsString('gtag', $body);
    }

    /**
     * @magentoAppArea frontend
     */
    public function testConsentSaveEndpoint()
    {
        $this->getRequest()->setMethod('POST');
        $this->getRequest()->setPostValue([
            'analytics' => '1',
            'marketing' => '0',
            'functional' => '1',
        ]);

        $this->dispatch('/cookie/consent/save');

        $response = json_decode($this->getResponse()->getBody(), true);
        $this->assertTrue($response['success']);
    }
}

Manual Testing Checklist

Test Case Expected Result Priority
New visitor sees banner Banner displays immediately Critical
Add to cart without consent Works correctly Critical
Complete checkout without consent Full flow works Critical
Accept all cookies Banner hides, analytics loads High
Reject all cookies Banner hides, cookies deleted High
Essential cookies still work Cart, session maintained Critical
Analytics blocked pre-consent No GA calls in network tab High
Marketing blocked pre-consent No FB pixel calls High
Consent persists across sessions Cookie stored for 1 year Medium
Multi-store consent isolation Separate consent per store Medium
Mobile banner usability Touch-friendly, not covering content High
Cookie settings accessible Link in footer works Medium

Performance Optimization

Cookie consent can impact page load times if not implemented carefully.

Magento 2 Performance Tips

// Defer banner initialization until after critical content
require(['jquery', 'domReady!'], function($) {
    // Wait for page to be interactive
    requestIdleCallback(function() {
        require(['cookieConsent'], function(CookieConsent) {
            CookieConsent.initialize();
        });
    });
});
<?php
// Cache consent state check

namespace YourCompany\CookieConsent\Model;

use Magento\Framework\App\CacheInterface;

class CachedConsentChecker
{
    private CacheInterface $cache;
    private ConsentManager $consentManager;

    private const CACHE_TAG = 'cookie_consent';
    private const CACHE_LIFETIME = 3600;

    public function hasConsentFor(string $category): bool
    {
        $cacheKey = 'consent_' . $category . '_' . $this->getVisitorId();

        $cached = $this->cache->load($cacheKey);
        if ($cached !== false) {
            return (bool) $cached;
        }

        $hasConsent = $this->consentManager->hasConsentFor($category);

        $this->cache->save(
            (string) (int) $hasConsent,
            $cacheKey,
            [self::CACHE_TAG],
            self::CACHE_LIFETIME
        );

        return $hasConsent;
    }

    private function getVisitorId(): string
    {
        // Generate based on session or cookie
        return md5($_COOKIE['PHPSESSID'] ?? 'anonymous');
    }
}

Common Issues and Solutions

Issue: Cart Empties After Consent

Cause: Session cookie was accidentally categorized as non-essential.

Solution:

// Ensure PHPSESSID is in essential category
private const ESSENTIAL_COOKIES = [
    'PHPSESSID',
    'form_key',
    // ... other essential cookies
];

public function shouldBlockCookie(string $name): bool
{
    if (in_array($name, self::ESSENTIAL_COOKIES)) {
        return false; // Never block essential
    }
    // ... rest of logic
}

Issue: Banner Keeps Reappearing

Cause: Consent cookie path or domain mismatch.

Solution:

// Ensure cookie path matches site structure
$metadata = $this->cookieMetadataFactory
    ->createPublicCookieMetadata()
    ->setPath('/') // Must be root
    ->setDomain('.' . $this->getDomain()) // Include subdomain dot
    ->setHttpOnly(false)
    ->setSecure(true);

Issue: Third-Party Scripts Still Loading

Cause: Scripts loaded before consent check runs.

Solution:

<!-- Load consent script first, blocking -->
<script src="cookie-consent.js"></script>
<!-- Then load other scripts -->
<script src="analytics.js"></script>

Key Takeaways

Implementing cookie consent on Magento 2 and PrestaShop requires understanding the unique architectures of each platform. The key principles are:

  1. Never block essential cookies - Cart, session, and security cookies must always work
  2. Intercept script loading - Use RequireJS hooks (Magento) or script interception (PrestaShop)
  3. Test checkout thoroughly - The purchase flow must work without any consent
  4. Configure per-store - Multi-store setups need regional compliance rules
  5. Monitor performance - Cookie consent shouldn't slow down your store

By following this guide and using either commercial plugins or the custom implementations provided, you can achieve GDPR, CCPA, and ePrivacy compliance while maintaining an excellent shopping experience for your customers. Remember that cookie consent is not a one-time setup—you must continuously monitor for new cookies from third-party integrations and update your configuration accordingly.

G

GetCookies-tiimi

Kirjoittanut ja ylläpitää GetCookies-tiimi, Getia AS, Oslo, Norja. Artikkeli on yleistä tietoa evästesuostumuksesta eikä oikeudellista neuvontaa.

Skannaa oma sivustosi ilmaiseksi

Ilmaispaketissa banneri toimii yhdellä sivustolla ja saat kolme evästeskannausta kuukaudessa. Ei korttia, ei myyntipuheluita.