Description
From 19 June 2026, EU Directive 2023/2673 requires online stores across the European Union to
provide a digital withdrawal function: a way to cancel a distance contract online that is at
least as easy to use as the purchase flow itself. A single “cancel” button is not enough. The law
requires a clearly labelled, continuously available function, a two-step confirmation, and an
acknowledgement on a durable medium whose timestamp fixes the legal moment of communication.
ErreD EU Order Withdrawal for WooCommerce implements that function end to end — not just the
button, but the declaration flow, the two-step confirmation, the durable-medium receipt, the
eligibility rules and the merchant review tools needed to actually comply.
It ships the Italian transposition out of the box (art. 54-bis of the Codice del Consumo,
introduced by D.Lgs. 209/2025) with the legally-fixed label «recedere dal contratto qui», and it
is fully translatable for other EU markets.
The plugin does not create a right of withdrawal: it provides the online channel to exercise an
existing one, honouring the legal exceptions (e.g. art. 59 in Italy). It is built security-first,
is WooCommerce High-Performance Order Storage (HPOS) native, and works fully offline (no external
service calls).
For consumers
- A clearly labelled, continuously available withdrawal function on the My Account orders screen.
- A two-step declaration and confirmation flow (“conferma recesso”), server-rendered so it works
even with JavaScript disabled. - An acknowledgement on a durable medium (email plus a stored PDF receipt) whose timestamp fixes
the moment of communication — the legal start date (dies a quo) for refund deadlines. - Reachable by guest-checkout customers through a per-order signed link in their order emails and on
the order-received page, with no order enumeration.
For merchants
- A React admin screen to review requests, filter by status, search by order, name or email, view
the audit timeline, view/regenerate the durable receipt and mark requests refunded or rejected. - A menu badge counting the requests awaiting action.
- A conservative, configurable eligibility engine (withdrawal window, start trigger, per-product
and per-category exclusions) that fails closed when configuration is missing. - Optional WooCommerce Subscriptions support: a confirmed withdrawal cancels the subscription
(status transition only — subscription data is never deleted). - An append-only audit trail and tamper-evident receipts (SHA-256 of the receipt payload).
Built to standard
- Security-first: capability + nonce/REST permission checks and input sanitisation / output
escaping on every privileged path; signed, rate-limited guest access. - Accessibility: WCAG 2.2 AA, verified with automated axe checks.
- Fully translatable; ships a complete Italian (it_IT) translation.
This plugin encodes legal and security intent; it is not legal advice. The mapping of the art. 59
exceptions to your catalogue and the durable-medium content must be validated by a qualified legal
professional before relying on them.
Source code and build process
This plugin ships its complete, human-readable source. The compiled assets in build/ are
generated from the React/JS/CSS source in assets/ with @wordpress/scripts.
- JS/CSS source:
assets/admin/andassets/frontend/ - Generated bundles:
build/admin/andbuild/frontend/ - Rebuild the bundles:
composer install && npm ci && npm run build - Runtime PHP dependencies (Dompdf) are managed with Composer.
Development repository: https://github.com/erred74/ErreD-EU-Order-Withdrawal-for-WooCommerce
Screenshots





Blocks
This plugin provides 1 block.
- ErreD EU Order Withdrawal for WooCommerce Renders the withdrawal (recesso) flow and the «recedere dal contratto qui» entry control.
Installation
- Upload the plugin to the
/wp-content/plugins/directory, or install it through the WordPress plugins screen. - Activate the plugin through the ‘Plugins’ screen in WordPress.
- WooCommerce 8.2 or newer (with High-Performance Order Storage) is required.
- Visit WooCommerce Recesso digitale: settings to configure the withdrawal window, the window start trigger and your product/category exclusions.
FAQ
-
Does this plugin decide whether an order can be withdrawn?
-
No. The plugin provides the channel and records the request; the merchant accepts or rejects each
one. The ordinary 14-day period is shown as advisory information (it does not hide the function),
and the merchant can pre-exclude specific products or categories (art. 59). Rejecting a request
requires a reason, which is recorded and emailed to the consumer. The mapping of the art. 59
exceptions to your catalogue must still be validated by a legal professional. -
Does it work for guest-checkout orders?
-
Yes. Guests receive a per-order, single-purpose signed link (HMAC, verified in constant time and
rate-limited). A bare order id or order key is never sufficient to submit a withdrawal, and
responses are uniform to prevent order enumeration. -
Does the withdrawal flow require JavaScript?
-
No. The two-step flow is server-rendered and works with JavaScript disabled; JavaScript only
enhances the admin experience. -
What is the “durable medium”?
-
A withdrawal acknowledgement sent by email together with a stored PDF receipt, kept in a protected
location and downloadable only through a capability- or token-checked endpoint. The receipt records
the content of the request and the exact date and time of transmission. -
Does it support WooCommerce Subscriptions?
-
Optionally. If WooCommerce Subscriptions is active, a confirmed withdrawal cancels the related
subscription by transitioning its status — it never deletes subscription data. With Subscriptions
absent, the plugin is unaffected. -
Does the plugin make external network calls?
-
No. It functions fully offline and loads no third-party scripts, fonts or assets.
-
What happens to my data on uninstall?
-
Nothing is removed unless you opt in via the “Delete all data on uninstall” setting. When enabled,
the plugin removes its tables, options and the flow page on uninstall.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“ErreD EU Order Withdrawal for WooCommerce” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “ErreD EU Order Withdrawal for WooCommerce” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
0.5.4
- Admin: the WooCommerce menu entries and the requests-page heading now default to English
(“Order Withdrawal”, “Order Withdrawal: settings”); Italian sites keep the previous
«Recesso digitale» labels via the bundled it_IT translation.
0.5.3
- Uninstall: only the withdrawal page auto-created by the plugin is removed. The page is now
identified by a created-by-plugin marker (and must still host the withdrawal shortcode), so a
pre-existing page the merchant selected in settings is never deleted.
0.5.2
- The withdrawal function is now a single «recedere dal contratto qui» button shown below the order
details, available to both guest-checkout customers (order-received page) and logged-in members
(My Account order view); the previous duplicate button is gone and the link no longer reports
“this withdrawal link is not valid or has expired”. - Confirmation step: removed the (unnecessary) product thumbnail and gave the «conferma recesso»
button a proper, accessible button style that no longer depends on the theme. - Admin: unconfirmed (abandoned) declarations no longer appear in the requests list or its counts,
and a consumer who closed the page before confirming can start a fresh request. - Admin: the menu badge counts all open requests awaiting action (confirmed and acknowledged) and
uses the standard WooCommerce/WordPress menu-counter styling.
0.5.1
- Packaging: the distribution now bundles the full, human-readable JS/CSS source (
assets/) and
the build manifests (package.json,package-lock.json) alongside the compiledbuild/
assets, and the readme documents how to rebuild them and links the public development repository. - Dependency: the bundled Dompdf library is updated from 3.0.0 to 3.1.5.
- Hardening: the admin receipt download link now carries a nonce that the download endpoint
verifies for the admin path (the consumer link continues to use its signed, rate-limited token). - Hardening: saving a product’s withdrawal status now verifies the WooCommerce editor nonce and
theedit_productcapability explicitly.
0.5.0
- New “Withdrawal” column on the WooCommerce orders screen (HPOS and the legacy list) showing each
order’s current withdrawal decision as a colour-coded badge. - The request detail panel now sets the decision through a single “Set status” dropdown (Pending,
Accepted, Rejected, Completed) with a “Save status” button next to “Regenerate receipt”. The reason
field appears only when rejecting (and stays required); the orders column updates to match. - Hardening: the contract reference stored for a withdrawal is now always derived server-side from
the order number, ignoring any client-supplied value on the REST create endpoint. - Hardening: the receipt download endpoint now validates the stored path against an exact directory
boundary, preventing a similarly named sibling directory from being treated as the protected store. - Packaging: reproducible production builds (installed from composer.lock) and a direct-access guard
added to the generated PHP asset files in the distribution.
0.4.0
- Product/category “Withdrawal status” now offers the specific art. 59 / Directive classifications
(standard, digital content art. 16(m), service started early art. 14(4)(a), dated accommodation /
transport art. 16(l), other art. 16 exception) instead of a plain allow/exclude, and each maps to
the right eligibility outcome. Statuses saved by earlier versions keep working. - A one-time welcome notice after activation links straight to the settings and the auto-created
withdrawal page. - Reorganised the settings screen into clear sections (General, Withdrawal deadline, Article 16
exclusions, Checkout consents, Model withdrawal form, Excluded products notice, Withdrawal link
visibility, Data), added a notification email and a withdrawal-page selector, a “show model form”
toggle and an optional trader phone number. - The Annex I.B model withdrawal form was redesigned to match the statutory layout (header block with
the trader’s details, fillable lines, source attribution and an optional printable view).
0.3.1
- Fix: an in-place update now reliably adds the v4 optional columns — the installed schema version is
only advanced once the columns actually exist, so the requests admin list and the withdrawal form no
longer break until a manual re-activation. - Hardening: the withdrawal declaration never white-screens — a transient persistence failure degrades
to a friendly message instead of a fatal error (the legally-required function stays usable).
0.3.0
- Art. 59 configuration: per-product and per-category “Right of withdrawal” status (allow / exclude /
inherit) in the product and category editors, plus an opt-in “excluded from withdrawal” notice on
the product page. - Checkout consents: optional, configurable digital-content (art. 16(m)) and service-start
(art. 14(4)(a)) consents, recorded on the order with timestamps and an order note. - Annex I.B model withdrawal form (printable), populated with configurable trader contact details,
shown below the public form and via the [recesso_digitale_modulo] shortcode. - Order notes mirror the withdrawal lifecycle; new customer emails on accept/refund/complete and an
admin notification (multiple recipients) when a withdrawal is confirmed. - Admin: Accept / Mark completed / Mark refunded actions, an at-a-glance stats summary and a CSV
export of requests. - GDPR: personal-data exporter and eraser (anonymises personal data while retaining the legal
acknowledgement) and a suggested privacy-policy snippet. - Optional refund IBAN and reason fields on the declaration (carried into the durable receipt),
honeypot anti-spam, an optional persistent footer link, an optional strict deadline-enforcement
mode with grace days, and a wpml-config.xml for WPML/Polylang.
0.2.0
- Redesigned the consumer withdrawal page: products are chosen with checkboxes, each shown with its
thumbnail, and a per-product quantity selector appears only when more than one unit was purchased. - The durable-medium acknowledgement email now lists every selected product with its quantity, along
with the confirmation email and the declaration timestamp, matching the PDF receipt. - The two-step confirmation («conferma recesso») and the completion screen now itemise exactly which
products and quantities are being withdrawn. - Internal: item resolution unified in one shared component used by the receipt, email and on-screen
summaries; the tamper-evident receipt hash is unchanged.
0.1.0
- Initial development release.
- Security-first, HPOS-native withdrawal channel: continuously available function, two-step
server-rendered declaration/confirmation flow, signed rate-limited guest access. - Durable medium: acknowledgement email plus a protected, tamper-evident PDF receipt with a
write-once legal timestamp (dies a quo), generated asynchronously via Action Scheduler. - Conservative, configurable, fail-closed eligibility engine (window, start trigger, art. 59
exclusions) with filters for integrators. - React admin: list, status filter, free-text search, audit timeline, receipt view/regenerate,
process actions, and a menu badge for requests awaiting action. - Optional WooCommerce Subscriptions adapter (withdrawal cancels the subscription).
- Merchant-decides model: the 14-day period is advisory (shown to the merchant), the withdrawal
function stays available, and the merchant accepts or rejects each request. Rejection requires a
reason, recorded in the audit trail and emailed to the consumer. - Accessibility to WCAG 2.2 AA (axe-verified) and a complete it_IT translation.
