PS PrestaShop Beginner

Age Verification for PrestaShop – Blocking Modal for CBD, Alcohol, Vape & Healthcare Pros

Complete documentation for the dfagegate module: installation, mode configuration (standard CBD/alcohol/vape/firearms and medical), multilingual customization, GDPR compliance and diagnostics.

Updated Module version 1.0.3

The dfagegate module adds a blocking age-verification modal to your PrestaShop 8 or 9 store. It covers two distinct markets: standard mode for age-restricted products (CBD, alcohol, vape, firearms, lighters, 18+ products), and medical mode for medical devices restricted to healthcare professionals (in France, within the meaning of article L5122-9 of the Public Health Code).

Compatibility — PrestaShop 1.7.7+, 8.x and 9.0. PHP 7.4 minimum, 8.1+ recommended. Multistore and multilingual (FR/EN/ES/DE pre-filled at installation).

Installation

Installation follows the standard PrestaShop workflow. After purchasing on DataFirefly, you receive a ZIP file dfagegate-X.Y.Z.zip.

  1. Log in to your PrestaShop back office
  2. Go to Modules → Module Catalog
  3. Click Upload a module at the top right
  4. Select the ZIP dfagegate-X.Y.Z.zip
  5. Once uploaded, click Install

Via FTP

  1. Unzip the archive locally
  2. Upload the dfagegate/ folder into your PrestaShop modules/ directory
  3. Go to Modules → Module Catalog
  4. Search for “DataFirefly Age Gate” and click Install

Important — After installation, the module is disabled by default. This is intentional: it lets you configure texts and mode before blocking your store. Go to the module configuration and enable the toggle in the General tab once everything is set up.

Initial setup

Access the configuration: Modules → Installed modules → DataFirefly Age Gate → Configure.

Configuration is organized into 6 tabs:

  • General — activation, mode, verification type, minimum age
  • Content — multilingual texts (title, message, buttons, legal notices)
  • Design — logo, colors, backdrop blur
  • Behavior — cookie, redirect, bypass rules
  • Medical mode — professions and license number (only applies in medical mode)
  • Logs & GDPR — logging and diagnostics

General tab

  • Enable module — Yes/No toggle, controls the global display of the modal
  • ModeStandard (CBD, alcohol, vape, firearms) or Medical (healthcare professionals)
  • Verification typeYes/no button, Date of birth, or Profession declaration
  • Minimum age — 18 by default, 21 for some markets (US for alcohol, for example)

Which verification type should you choose? The yes/no button suits most cases (CBD, mainstream alcohol, vape): fast, low friction, sufficient deterrent for most regulatory audits. Date of birth is stricter and recommended for firearms or nicotine liquids. Profession declaration is reserved for medical mode.

Content tab

Each language enabled in your PrestaShop has its own text block. Defaults are pre-filled in FR, EN, ES and DE. For each language you can configure:

  • Title — displayed large at the top of the modal (default: “Age Verification”)
  • Message — main explanatory text, line breaks are preserved
  • Confirm button — positive button label (default: “I am 18 or older”)
  • Refuse button — negative button label
  • Legal notice — text at the bottom of the modal (e.g. “Please drink responsibly.”)
  • Denial message — screen shown when the user refuses, before redirect

HTML — Content is sanitized on save (plain text only). Line breaks are converted to break tags at render time via an automatic nl2br filter.

Design tab

  • Logo — PNG, JPG, SVG or WEBP, 2 MB max, displayed at the top of the modal
  • Background color — main card color (white by default)
  • Primary color — title and main button (black #111111 by default)
  • Text color — message body
  • Overlay color — veil behind the modal, accepts CSS rgba() and hex formats (default: rgba(15,15,20,0.85))
  • Backdrop blur — background blur (modern effect, works in all current browsers)

Behavior tab

  • Cookie duration — in days (90 by default). Set 0 for a session cookie (deleted when the browser closes)
  • Redirect URL on refusal — leave empty to only show the denial message, or set an external URL (Google, supplier page, etc.)
  • Bypass IPs — list of IPs (one per line) for which the modal never shows. Ideal for you and your team during tests
  • Bypass URLs — excluded partial paths. Pre-filled with /legal, /contact, and other legal pages
  • Bypass logged-in customers — if enabled, already-authenticated customers never see the modal (useful if your store is restricted to already-validated accounts)

Medical mode tab

This tab only applies when mode is set to Medical and verification type to Profession declaration.

  • Profession list — one per line. Defaults: Doctor, Pharmacist, Nurse, Physiotherapist, Dentist, Veterinarian, Other healthcare professional. Fully customizable
  • Mandatory license number (RPPS/ADELI) — if enabled, a field appears in the modal. Regex validation accepting 9 to 11 digits. The number is not stored, it is only validated server-side

Legal compliance — Medical mode materializes a sworn declaration in the spirit of French article L5122-9 of the Public Health Code, which restricts advertising of certain medical devices to authorized healthcare professionals. This module does not replace a legal review of your catalog by a specialized lawyer. Consult your counsel to validate your configuration and check the equivalent rules in your jurisdiction.

Logs & GDPR tab

  • Log refusals — records each refusal in the ps_dfagegate_log table with SHA-256 hashed IP (never plain), date, reason

This tab also shows a GDPR summary:

  • Cookie setdfagegate_ok
  • Category — strictly necessary (legal access compliance)
  • Data — value “1”, configurable duration, SameSite=Lax, Secure on HTTPS

Configuring for your market

Mainstream CBD store

  • Mode: Standard
  • Verification type: Yes/no button
  • Minimum age: 18
  • Cookie duration: 90 days (good balance between compliance and UX)
  • Redirect URL: empty (denial message only)

Premium spirits / alcohol store

  • Mode: Standard
  • Verification type: Date of birth (stricter control for premium markets)
  • Minimum age: 18 (or 21 for US markets)
  • Redirect URL: any official information page

Vape / nicotine e-liquid store

  • Mode: Standard
  • Verification type: Date of birth (strongly recommended for nicotine)
  • Minimum age: 18
  • Log refusals: Yes (useful in case of a regulatory audit)

Firearms store

  • Mode: Standard
  • Verification type: Date of birth (mandatory)
  • Minimum age: 18
  • Bypass URLs: add /regulations, /hunting-license
  • Log refusals: Yes

Medical equipment store (professional devices)

  • Mode: Medical
  • Verification type: Profession declaration
  • Profession list: Doctor, Pharmacist, Physiotherapist, Osteopath (adapt to your catalog)
  • Mandatory license number: Yes
  • Bypass logged-in customers: Yes (if you already validate the profession at registration)

How date-of-birth verification works

Unlike a simple button, date-of-birth verification performs the calculation server-side, not in the browser:

  1. The visitor enters day, month and year in the modal
  2. JavaScript sends these values to the AJAX controller DfagegateAjaxModuleFrontController
  3. PHP validates the date with checkdate() then computes the age via DateTimeImmutable::diff()
  4. If the age is under the configured threshold, the server returns a JSON response with success=false, denied=true and the error message
  5. The modal shows the denial message and redirects after 2 seconds
  6. If the age meets the threshold, the dfagegate_ok cookie is set and the modal closes

Why server-side? A purely JavaScript check can be bypassed via DevTools in under 10 seconds. Server-side calculation guarantees that an underage user cannot access the site, even with technical knowledge. This is essential to withstand a regulatory audit.

Note that the date of birth is never stored — it is used for a single calculation and then forgotten. Only the binary validation (accepted / refused) is retained via the cookie.

How medical mode works

  1. The modal shows a dropdown of professions (configurable) and optionally a license number field
  2. A mandatory sworn declaration checkbox is present
  3. The AJAX controller validates that a profession is selected
  4. If the license number is mandatory, the server validates the format via a regular expression accepting 9 to 11 consecutive digits
  5. No storage: neither the profession nor the number is kept in the database — the dfagegate_ok cookie only materializes the successful passage

Design choice — The regulation requires materializing the declaration, not necessarily a real-time check against a national registry. Our approach is minimum viable compliance: we ask for the declaration, formally validate it, log the refusal if enabled, but collect no unnecessary personal data. If you need real-time registry verification, that is a separate custom development.

Multistore

The module is 100% multistore compatible. All settings (mode, verification type, multilingual texts, colors, bypass rules) are stored per shop context via id_shop_group and id_shop. This means a single PrestaShop can host:

  • A CBD FR store in standard mode at 18 with French texts
  • A vape UK store in standard mode at 18 with English texts
  • A medical equipment DE store in medical mode with mandatory license number and German texts

To configure a specific sub-store:

  1. In the context selector at the top of the back office, pick the target sub-store
  2. Open the module configuration
  3. Edit the values — they will be persisted for that store only

Hooks are registered on all stores at installation time via Shop::getCompleteListOfShopsID(), avoiding the classic pitfall of a module that only runs on the current store.

Compatibility with custom themes

The module uses the standard displayBeforeBodyClosingTag hook to inject the modal just before the closing body tag. This hook is supposed to be universal on PrestaShop 1.7.5+.

Unfortunately, some custom themes do not call this hook in their layout. For this case, dfagegate ships with a JavaScript injection fallback:

  1. PHP pre-renders the full modal HTML and passes it to JS via Media::addJsDef
  2. On DOMContentLoaded, the script checks whether the element with ID dfagegate-modal exists in the DOM
  3. If yes, all good — the hook worked
  4. If not, the script injects the modal itself via insertAdjacentHTML('beforeend', ...)
  5. A console.info message confirms the fallback activated: “[dfagegate] modal injected via JS fallback (theme does not trigger displayBeforeBodyClosingTag).”

Practical result — The module works on any PrestaShop 1.7.5+ theme, including incomplete custom themes, without touching the layout. You can deploy it without coordinating with your theme agency.

Diagnostics and debugging

Once enabled, dfagegate adds an HTML comment in the head tag of every front page, in the form “dfagegate v1.0.3 enabled=1 should_display=1”.

This comment is your first diagnostic checkpoint. View the source of a front page (Ctrl+U or Cmd+U) and search for “dfagegate”.

Comment Interpretation
No comment at all The displayHeader hook is not registered — check the module is installed and active
enabled=0 The “Enable module” toggle is off in the General tab
enabled=1 should_display=0 A bypass is active: your IP is whitelisted, the current URL matches an excluded path, or you are a logged-in customer with the bypass enabled
enabled=1 should_display=1 Server-side, everything is fine. If the modal does not appear, check the browser console for a fallback or error message

The modal still does not appear?

  1. Is the diagnostic comment present with should_display=1? If not, fix the config
  2. Open the store in private browsing. The dfagegate_ok cookie may have been set in a previous session
  3. Check your IP against the bypasses in the Behavior tab
  4. Open the browser console (F12). Look for [dfagegate] messages
  5. Check PrestaShop logs in Advanced Parameters → Logs, filter on “dfagegate”
  6. Clear the PrestaShop cache after every configuration change: Advanced Parameters → Performance → Clear cache
  1. Open DevTools (F12)
  2. Application tab (Chrome) or Storage (Firefox)
  3. Cookies section → your domain
  4. Delete the dfagegate_ok row
  5. Reload the page

GDPR compliance

  • Type — strictly necessary to comply with a legal access obligation
  • Legal basis — exempt from prior consent (in France, under article 82 of the Data Protection Act per CNIL guidance; similar exemptions exist under the ePrivacy framework)
  • Value — binary (1 = confirmed)
  • Duration — configurable (90 days by default), or session if set to 0
  • AttributesSameSite=Lax, automatic Secure on HTTPS, Path=/

In practice — You do not need to add this cookie to your consent banner. It falls into the same category as the PrestaShop session cookie or the CSRF cookie: necessary for the legal operation of the site, therefore exempt.

Refusal logs

If you enable Log refusals, every refusal is recorded in the ps_dfagegate_log table with:

  • id_shop — sub-store concerned
  • reason — refusal reason (user_refused or dob_under_age)
  • age — declared age if applicable
  • profession — declared profession if applicable
  • ip_hashSHA-256 of the IP, never the plain IP
  • date_add — timestamp

SHA-256 hashing makes the IP non-reversible while still allowing deduplication of attempts (the same IP always produces the same hash). This is the compromise recommended by data-protection authorities for access statistics.

Verification data

  • The date of birth transits via AJAX for the calculation but is never stored
  • The license number is validated server-side then forgotten, never stored
  • Only the binary validation is retained, via the cookie

Technical structure and integration

Hooks used

  • displayHeader — injects the diagnostic comment
  • actionFrontControllerSetMedia — registers CSS and JS, passes the config and the pre-rendered modal HTML to JS
  • displayBeforeBodyClosingTag — renders the modal server-side (JS fallback if absent from the theme)

AJAX endpoints

The AJAX controller responds at /module/dfagegate/ajax and accepts two actions:

  • action=confirm — with parameters depending on the verification type (nothing for yes/no, day/month/year for DOB, profession/license for medical)
  • action=refuse — logs the refusal and returns the redirect URL

Responses are JSON. A confirmation returns success=true. An underage refusal returns success=false, denied=true and the appropriate error message. A validation error returns success=false with the message. A user refusal returns success=true and redirect_url containing the configured redirect URL.

Database schema

A single table is created: ps_dfagegate_log. It contains the columns id_log (auto-increment primary key), id_shop, reason (varchar 64), age (nullable), profession (varchar 128 nullable), ip_hash (char 64 for the SHA-256) and date_add (datetime). Two secondary indexes optimize reporting queries: idx_shop_date on (id_shop, date_add) and idx_reason on reason.

Uninstallation

Disable without removing

Modules → Installed modules → DataFirefly Age Gate → Disable. Configuration and the log table are kept. You can re-enable at any time without reconfiguring.

Full uninstall

Modules → Installed modules → DataFirefly Age Gate → Uninstall. This action:

  • Drops the ps_dfagegate_log table
  • Removes all configuration entries (18 scalar keys + 6 multilingual keys)
  • Unregisters the hooks
  • Removes the module from the system

Warning — Uninstallation is irreversible. If you want to keep the refusal log history (e.g. for a data-protection audit), export the table first.

Support and updates

Every license includes:

  • 12 months of updates — PrestaShop compatibility, fixes, improvements
  • Email technical support — response within 24 business hours, FR/EN
  • 30-day refund, no questions asked
  • Unencrypted source code — you are free to adapt the module to your specific needs

For any technical question or bug report, contact us from your DataFirefly account. Please include your PrestaShop version, PHP version, module version (visible at the top of the configuration screen), and if possible the diagnostic comment present in the head tag of your store.

Was this page helpful?

Still stuck? Contact support