Consent Banner
GDPR/ePrivacy consent banner for WordPress with category-level controls, always-on essentials, and bilingual EN/BG support. https://katsar0v.github.io/consent-banner/
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/katsar0v/consent-banner/archive/refs/heads/main.zipConsent Banner
GDPR/ePrivacy consent management for WordPress with category-level controls, essential cookies always on, and EN/BG/DE support.
Overview
Consent Banner adds a configurable consent banner to WordPress with Accept all, Reject all, and Customize flows. Categories are managed in admin, essential cookies stay enabled by design, and consent can be revisited from a shortcode or theme trigger.
Version 0.5.0 adds validated runtime modes and opt-in, vendor-neutral browser and server commerce contracts with consent-derived destinations, positive-list redaction, and no built-in remote transport.
Highlights
| Area | What it gives you |
|---|---|
| Consent UX | Accept all, reject all, and per-category customization in a modal. |
| Category model | Essential category is enforced as required; custom categories can be added in admin. |
| Admin settings | Categories, EN/BG/DE texts, lifetime, position, theme, uninstall behavior, and version bumping. |
| REST API | Public consent submission/config endpoint + admin settings endpoint. |
| WP-CLI | JSON settings import/export for deployments and backups. |
| Integrations | JS API (window.kdconsent) + PHP helper (kdconsent_has_consent) + WP hooks/filters. |
| Internationalization | English, Bulgarian, and German text packs (site locale based). |
| Audit option | Optional pseudonymous receipts without IP, user agent, or PII. |
| Service control | Purpose-gated URL allowlists with cookie/storage teardown on revocation. |
| Commerce events | Optional, transport-free WooCommerce/Bricks browser event contract with positive-list redaction. |
Requirements
| Requirement | Version |
|---|---|
| WordPress | 6.8 or newer |
| PHP | 8.1 or newer |
Installation
Place the plugin directory in WordPress:
wp-content/plugins/consent-banner
Install dependencies when installing from source:
composer install --no-dev
Activate from WP-CLI inside the Docker php container:
docker exec -w /var/www/html php wp plugin activate consent-banner --allow-root
First-Time Setup
- Open
Settings -> Consent Bannerin wp-admin. - Confirm categories and keep
essentialrequired. - Review EN/BG/DE texts.
- Set banner behavior (position, theme, lifetime).
- Save. Optionally bump consent version to force re-consent.
Frontend Behavior
- A deferred loader checks the consent cookie first; full config, CSS, and UI load only when consent is missing, stale, or preferences are opened.
Accept all: enables all categories.Reject all: enables only required categories.Customize: opens modal with category toggles (essential locked on).[kdconsent_preferences]shortcode renders a button to reopen preferences.- Any element with class
.kdconsent-open-preferencesreopens preferences. - Effective categories, consent texts, consent lifetime, and service definitions share one fingerprint. A change to any of them invalidates prior consent exactly once; style, position, animation, delay, and automatic footer display changes do not.
Automatic footer preferences control
Settings → Consent Banner → Appearance → Display behavior includes
Automatically show Cookie settings in the footer (autoFooterPreferences).
It defaults to enabled, including when upgrading settings that omit the key.
The plugin renders one .kdconsent-footer-preferences wrapper through wp_footer.
Disabling the checkbox removes the whole wrapper server-side, leaving no empty row.
Clear any full-page cache after changing it. Export/import and REST PATCH preserve
this setting; changing only this setting does not increment the consent version.
Provide a replacement in your own footer or another accessible location before
disabling automatic display. Use [kdconsent_preferences], for example in a
Bricks Shortcode element, or a native link/button with kdconsent-open-preferences:
<button type="button" class="kdconsent-open-preferences">Cookie settings</button>
The trigger class adds behavior only and leaves manually styled links/buttons intact,
including after the dialog stylesheet loads. Shortcode and automatic buttons use
the separate kdconsent-preferences-button class for their default appearance.
Custom JavaScript can also call window.kdconsent.openPreferences() after the
loader is ready. These manual controls remain independent of the checkbox and
work for returning visitors whose banner UI has not loaded yet.
Theme migration: Equanis's reported row came from
Equanis\Consent\LiveTracking::preferences_link, registered on wp_footer,
not from the 0.5.0 plugin. Remove that legacy automatic callback when adopting
the plugin renderer, otherwise it remains independent and can cause duplication.
Keep wp_footer and consent runtime scripts enabled. Theme-specific styling can
target .kdconsent-footer-preferences .kdconsent-open-preferences.
Runtime mode
The frontend defaults to live. Integrations can select the local, non-loading debug transport with a filter:
add_filter( 'kdconsent_runtime_mode', static fn(): string => 'debug' );
debug logs Consent Mode commands and planned service activations in the browser console. live writes Consent Mode commands to the data layer and may load consented, allowlisted service scripts. Any value other than live or debug falls back to live.
Optional browser commerce events
The generic commerce module is disabled by default and has no remote transport. Enable it from an integration or theme after plugins load:
add_filter( 'kdconsent_commerce_enabled', '__return_true' );
It emits page_view, view_item_list, select_item, view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, and search. Every kdconsent:commerce DOM event uses the same redacted envelope exposed by window.kdconsentCommerce; in debug mode that envelope is also logged with [kdconsent-commerce]. No search term, contact data, address, network identifier, click identifier, or attribution cookie value is exposed.
planned_destinations contains only sanitized IDs from kdconsent_services whose declared purpose is currently granted. The module never hardcodes a vendor or sends an HTTP request.
When WooCommerce is active, product/variation IDs, SKU, product name, post-discount net unit price, quantity, currency, and cart value are provided. Woo Order Attribution remains off in cacheable HTML, follows marketing consent in the browser, and removes known sbjs_* first-party state after denial or revocation. The WooCommerce and Bricks integrations are optional and guarded by runtime checks.
Classic cart and mini-cart removal links are enriched at render/fragment time with exact data-kdconsent-commerce-* item fields from the current cart line. Confirmed AJAX adds use the matching returned fragment's effective net unit price while retaining the quantity added by that action, rather than the cart line's accumulated quantity. Per-cart lookup data is localized only on the non-cacheable cart and checkout pages, and internal cart keys and parent/variation lookup fields never enter public event payloads. Woo Blocks confirmations that only expose preserveCartData are emitted only from the latest exact, unexpired product intent (Woo product-button/remove-link classes, data-wc-context, or data-kdconsent-commerce-action). A confirmation without exact event data or a recent captured intent is ignored rather than assigned to a guessed product.
Bricks WooCommerce lists receive semantic roles and data-kdconsent-commerce-* attributes. Integrations can supply a site-specific list identity without coupling it to the plugin:
add_filter(
'kdconsent_commerce_bricks_list_context',
static function ( array $context, object $element, array $settings, string $attribute_key ): array {
$context['list_id'] = 'featured-products';
$context['list_name'] = 'Featured products';
$context['list_group'] = 'featured';
return $context;
},
10,
4
);
Optional server commerce events
The same kdconsent_commerce_enabled filter opts into a WooCommerce server dispatcher for purchase and refund. WooCommerce and Action Scheduler remain optional dependencies: without WooCommerce the callbacks are inert, and without a working scheduler no delivery state is written.
Classic checkout and Store API checkout capture the normalized consent snapshot on the order. By default, only paid first orders created through checkout or store-api qualify. Subscription renewals are rejected when WooCommerce Subscriptions is available. A site integration can add stricter exclusions without replacing these defaults:
add_filter(
'kdconsent_commerce_order_qualifies',
static function ( bool $qualifies, WC_Order $order ): bool {
return $qualifies && '' === (string) $order->get_meta( '_my_import_id', true );
},
10,
2
);
Purchase IDs are always purchase:<order_id> and refund IDs are refund:<order_id>:<refund_id>. The occurrence time comes from WooCommerce's paid/refund creation timestamp rather than job execution time. Merchandise value is the exact sum of post-discount line totals without tax or shipping; unit price is the corresponding line total divided by quantity. Purchase paid_value is the paid order total. Refund value contains only actual refund lines, so a shipping-only refund has merchandise value 0; refund paid_value is the actual refund amount. When WooCommerce records a non-zero product refund line without a quantity, the canonical event preserves that affected product as one unit priced at the refunded line amount.
Every event is reduced to the positive-list contract before kdconsent_commerce_event runs, and its result is redacted again. Canonical IDs, timestamps, consent, value math, items, and privacy booleans cannot be replaced by a filter. A filter can suppress planned destinations, but the remaining set is intersected with the currently registered, consented services. Contact values, addresses, IP addresses, user agents, attribution cookies, and click IDs are never exposed. Only has_email, has_phone, and a click-ID boolean derived from actual gclid, wbraid, or gbraid order metadata can be present.
The plugin bundles no live transport. A live transport listens to the public delivery action and must explicitly acknowledge success through its DeliveryConfirmation; registration or a callback return value never counts as delivery:
use KatsarovDesign\ConsentBanner\Commerce\DeliveryConfirmation;
add_action(
'kdconsent_commerce_deliver_purchase',
static function ( array $event, DeliveryConfirmation $confirmation ): void {
if ( my_transport_send_redacted_event( $event ) ) {
$confirmation->confirm();
}
},
10,
2
);
Refund transports use kdconsent_commerce_deliver_refund with the same two arguments. Unconfirmed or failed attempts stay pending and receive capped, unique backoff retries through Action Scheduler. A later paid/refund source hook can requeue work that remains pending after the cap. Transport exceptions are rethrown so Action Scheduler records the failed attempt; failed, timed-out, and unexpectedly terminated plugin jobs are also reconciled into the retry path.
In debug runtime mode, the dispatcher performs no HTTP request or live transport action. It writes only the twice-redacted event as [kdconsent-commerce] <json> to the PHP error log and marks the job debug-delivered after a successful write. Repeated callbacks for delivered or debug-delivered entities are no-ops.
REST API
Namespace:
/wp-json/kdconsent/v1
| Methods | Endpoint | Purpose |
|---|---|---|
GET |
/config |
Public runtime config (texts, categories, behavior, current consent). |
POST |
/consent |
Public consent submission endpoint (rate-limited). |
GET |
/settings |
Admin-only settings read. |
PUT, PATCH |
/settings |
Admin-only settings update. |
Admin endpoints require X-WP-Nonce and manage_options capability.
WP-CLI
Export the same JSON payload available in the admin GUI:
docker exec -w /var/www/html php wp consent-banner export /tmp/consent-banner-settings.json --allow-root
Overwrite an existing export:
docker exec -w /var/www/html php wp consent-banner export /tmp/consent-banner-settings.json --force --allow-root
Print the export to stdout:
docker exec -w /var/www/html php wp consent-banner export - --allow-root
Import and merge settings. By default this bumps the consent version, matching the GUI:
docker exec -w /var/www/html php wp consent-banner import /tmp/consent-banner-settings.json --allow-root
Replace all settings instead of merging:
docker exec -w /var/www/html php wp consent-banner import /tmp/consent-banner-settings.json --replace --allow-root
Validate an import without changing settings or the consent version:
docker exec -w /var/www/html php wp consent-banner import /tmp/consent-banner-settings.json --dry-run --allow-root
Import without asking users for consent again:
docker exec -w /var/www/html php wp consent-banner import /tmp/consent-banner-settings.json --no-bump-version --allow-root
Hooks and Helpers
| Type | Name | Purpose |
|---|---|---|
| Filter | kdconsent_default_categories |
Override install-time category defaults. |
| Filter | kdconsent_categories |
Adjust runtime categories before use/render. |
| Filter | kdconsent_services |
Register declarative, purpose-gated service definitions. |
| Filter | kdconsent_runtime_mode |
Select validated live or debug frontend transport. |
| Filter | kdconsent_commerce_enabled |
Opt into browser and WooCommerce server commerce modules; defaults to false. |
| Filter | kdconsent_commerce_bricks_list_context |
Supply sanitized list_id, list_name, and list_group metadata; arguments are context, element, settings, and attribute key. |
| Filter | kdconsent_commerce_order_qualifies |
Add site-specific order exclusions after the built-in checkout/Store API and renewal rules. |
| Filter | kdconsent_commerce_event |
Inspect the already-redacted server envelope or suppress planned destinations; canonical event and commerce fields remain enforced. |
| Action | kdconsent_consent_recorded |
Runs when a consent decision is persisted. |
| Action | kdconsent_commerce_deliver_purchase |
Receive a redacted purchase and explicitly confirm successful live transport. |
| Action | kdconsent_commerce_deliver_refund |
Receive a redacted refund and explicitly confirm successful live transport. |
| PHP helper | kdconsent_has_consent( string $category ): bool |
Check category consent in PHP templates/plugin logic. |
Data Storage
| Key | Purpose |
|---|---|
kdconsent_settings |
Main plugin settings payload. |
kdconsent_consent_version |
Consent schema/version for re-prompting users. |
kdconsent_consent_definitions_hash |
Fingerprint of effective category and service definitions. |
kdconsent_db_version |
Plugin DB version state. |
kdconsent_remove_on_uninstall |
Opt-in uninstall cleanup flag. |
kdconsent_consent cookie |
Signed client consent payload (v, t, c). |
{prefix}kdconsent_consent_log |
Optional hashed consent proof entries. |
_kdconsent_commerce_consent_snapshot |
Normalized purpose booleans captured on a WooCommerce order. |
_kdconsent_commerce_purchase_state |
Purchase scheduling/delivery state on the order. |
_kdconsent_commerce_refund_state |
Refund scheduling/delivery state on the refund. |
Server jobs use the kdconsent-commerce Action Scheduler group. The internal process hooks are kdconsent_commerce_process_purchase and kdconsent_commerce_process_refund; integrations should use the public delivery actions above rather than those queue callbacks.
Development
docker exec -w /var/www/html/wp-content/plugins/consent-banner php composer lint:syntax
docker exec -w /var/www/html/wp-content/plugins/consent-banner php composer lint:phpcs
Architecture
consent-banner.php Plugin bootstrap
includes/Plugin.php Hook wiring
includes/Installer.php Defaults, table creation, upgrades
includes/Admin/ Admin menu, settings page, admin assets
includes/Frontend/ Banner mount, frontend assets, shortcode
includes/Commerce/ Optional browser and server commerce event modules
includes/Rest/ REST routes and consent/settings controller
includes/Repository/ Settings and optional consent log persistence
includes/Service/ Consent and localization logic
includes/Domain/ Consent/category value objects
assets/css/ Admin and banner styles
assets/js/ Admin and banner interactions
views/settings.php Admin settings template
languages/ Translation files
uninstall.php Opt-in cleanup during uninstall
Uninstall Behavior
By default, plugin data is preserved on uninstall. If Remove plugin data on uninstall is enabled, uninstall removes plugin options and consent log table.
License
Consent Banner is licensed under GPL-2.0-or-later.
Copyright (C) 2026 Katsarov Design.