completed status is not proof that a buyer has received the goods. A system that confidently hides the function fourteen days after the wrong event can be worse than one that admits the delivery date is unknown.A new button exposes an old data problem
The right to withdraw from many distance contracts is not new. The interface requirement is.
Directive (EU) 2023/2673 added Article 11a to the Consumer Rights Directive. Italy transposed it as Article 54-bis of the Consumer Code. For distance contracts concluded through an online interface, the trader must let the consumer withdraw through an online function.
The function must be continuously available during the applicable withdrawal period, prominently displayed and easy to access. It must use the words “withdraw from contract here” or an equally unambiguous formulation. After the consumer supplies or confirms their name, the contract to withdraw from and an electronic destination for the receipt, a second function must let them confirm the declaration. That function must say “confirm withdrawal” or something equally clear.
Once confirmed, the trader must send an acknowledgement on a durable medium without undue delay. The acknowledgement must include the content of the declaration and the date and time it was submitted.
That sounds like a form, an email and a timestamp. In WooCommerce, it is also a data-modelling problem.
The store must decide:
- whether the contract carries a right of withdrawal;
- when the applicable period began;
- whether that period is still open;
- which products or quantities the declaration concerns;
- what evidence is preserved after submission.
The button is the easy part.
Withdrawal, return and refund are three different events
Ecommerce software often uses withdrawal, return, cancellation and refund as if they described one workflow. They do not.
A withdrawal is the consumer's declaration that they are exercising a legal right. A return is the later physical movement of goods, where goods have to be sent back. A refund is the financial consequence handled after the declaration and subject to its own rules.
Article 54-bis is unusually clear about the first event: the right is exercised in time if the consumer transmits the online withdrawal declaration before the deadline. It does not become effective only when the merchant clicks Approve.
This makes common interface language awkward:
A merchant may still need to determine whether an exception applies, whether the buyer acted as a consumer or whether the stated order exists. The back office may need internal states such as pending review, accepted or rejected. But the customer-facing receipt should not imply that a valid declaration is merely a discretionary request.
The distinction matters operationally. A system can acknowledge the declaration immediately, preserve its original content and separately record the merchant's later assessment. Combining those events makes the evidence less clear for both parties.
It also creates the wrong product incentives. A returns workflow is designed to collect reasons, photographs and approval data. A withdrawal function must allow the consumer to act without giving a reason. Optional feedback can be useful; making a reason mandatory adds friction to a right that Article 9 of Directive 2011/83/EU says can be exercised without one.
Fourteen days after what?
For a typical sale of physical goods, the withdrawal period does not run from the click on Place order. It runs from the day the consumer, or a third party designated by the consumer other than the carrier, acquires physical possession.
The same rule has important branches:
- if multiple goods in one order arrive separately, the period runs from possession of the last good;
- if one good is delivered in multiple lots or pieces, it runs from possession of the last lot or piece;
- for regular delivery of goods over a defined period, it runs from possession of the first good;
- for services and digital content not supplied on a tangible medium, it generally runs from conclusion of the contract.
WooCommerce has an order creation date. It may have a payment date and a completion date. None is inherently the date on which the consumer acquired physical possession.
completed is especially tempting. Some stores set an order to completed when it is packed, others when it leaves the warehouse, others after a carrier callback, and some never use the status for physical orders at all. Treating it as delivery is a business-specific convention, not a WooCommerce guarantee.
This leaves a plugin with three broad strategies:
- Guess early. Start from order creation. This can close the period before the goods have even arrived.
- Use a configurable proxy. Start from completion or another order status. This can work when the merchant's workflow makes that status a reliable delivery event.
- Represent uncertainty. Keep the function available until an actual or configured delivery event is known.
The third option looks less automated. For physical goods with incomplete fulfilment data, it is also the only one that does not manufacture a delivery date.
What we inspected
On 5 July 2026, we downloaded the latest stable packages for five free plugins from the WordPress.org directory and inspected their PHP source:
- BuddyPilot Withdrawal 1.0.19
- WebToffee EU Order Withdrawal Button 1.0.6
- APG Withdrawal for WooCommerce 0.6.1
- Revoker for WooCommerce 1.0.26
- EU Withdrawal Compliance 2.0.1
This was a source audit, not a legal certification and not a full browser compatibility test. We examined the default deadline model, fallback behaviour, guest path, confirmation step and acknowledgement data visible in the code. Theme conflicts, mail deliverability, translations and behaviour under every WooCommerce extension require a separate runtime test.
The packages change quickly. The version numbers and audit date are part of the finding.
The deadline models are materially different
| Plugin | Default period model observed in source | Behaviour when delivery is not known | Main consequence |
|---|---|---|---|
| BuddyPilot Withdrawal 1.0.19 | Physical goods use per-item delivery metadata, an order-level delivery event, then WooCommerce completion | If no usable delivery event exists, the deadline remains unknown and the function stays available | Most explicit representation of delivery uncertainty in this sample |
| WebToffee 1.0.6 | Starts from order creation by default; merchant can select one or more status events | Default produces a hard fourteen-day deadline from order creation | Requires deliberate configuration for physical goods |
| APG Withdrawal 0.6.1 | Starts from order completion by default, falling back to order creation | Applies the calculated deadline as an eligibility gate | Safe only if the chosen WooCommerce date reliably represents the legal start event |
| Revoker 1.0.26 | Uses custom _delivery_date metadata when present; otherwise completion by default, then creation |
Applies a hard deadline from the selected or fallback date | Supports a real delivery field, but its fallbacks can still start early |
| EU Withdrawal Compliance 2.0.1 | Calculates an approximate date from creation or completion | Default mode is advisory rather than blocking; an optional strict mode enforces it | Conservative by default, but strict mode inherits the quality of the selected proxy |
This is not a simple ranking. Each model embodies a different tradeoff.
WebToffee lets the merchant choose status events and records when an order first reaches them. That can be accurate in a store whose carrier integration creates a real Delivered status. Its out-of-box setting, however, is order creation. The plugin directory's own FAQ also describes the standard period as fourteen days from purchase, which is not the general rule for physical goods.
APG defaults to completion and exposes creation as the alternative. Revoker similarly defaults to completion, supports a dedicated _delivery_date, and falls back to creation. These systems can be configured around a reliable operational process. Without one, a precise deadline is calculated from a proxy.
EU Withdrawal Compliance makes the inverse choice. Its calculated date is advisory by default, so a potentially late declaration is flagged for merchant review rather than blocked. A strict mode is available for cases where the selected date is trustworthy, such as some service, digital-content or shop-pickup flows.
BuddyPilot distinguishes physical items from services and digital items, can store delivery at item level and chooses the latest applicable start across an order. If a physical item has no usable delivery event, its calculator returns no deadline and its permission check keeps the right open. That is the most developed temporal model in this sample. Its accuracy still depends on the store recording a truthful delivery event; using WooCommerce completion as a fallback remains a proxy.
The broader lesson is more useful than the individual verdicts:
Mixed and split orders break order-level clocks
Consider one order containing a book and a chair. The book arrives on 1 July. The chair arrives on 8 July.
An order-level clock based on payment, creation or the first fulfilment event may expire on 15 July. Under the last-good rule, the period for the order may instead run from 8 July.
Now consider a configurable product with a table base delivered separately from its top, or an order partially fulfilled by two warehouses. One date_completed value cannot describe both possession events unless the store deliberately sets it after the final delivery.
This is why item-level delivery data is not gratuitous complexity. It corresponds to a branch already present in consumer law. Stores do not necessarily need a new timestamp on every line item, but software should not pretend that one order date answers every fulfilment shape.
A useful implementation should document what its clock means:
- Is it the contract date?
- The fulfilment date?
- A configured delivery status?
- A carrier-confirmed delivery date?
- The latest delivery across eligible line items?
- Only an advisory estimate?
If neither the merchant nor the plugin can answer, hiding the function on that basis is difficult to justify.
Digital does not automatically mean excluded
Another attractive shortcut is to exclude every WooCommerce product marked virtual or downloadable. The legal test is narrower.
For paid digital content not supplied on a tangible medium, the exception applies when performance has begun after the consumer gave prior express consent and acknowledged that they would lose the right of withdrawal. The trader must also preserve the relevant confirmation. A product flag alone does not prove those events happened.
The same principle applies to other exceptions. A personalised product, a perishable product and a sealed hygiene product are not interchangeable categories. Some exceptions depend on what the product is; others depend on what occurred after delivery. “Excluded” is therefore not merely a catalog attribute. In several cases it is a catalog attribute plus evidence of an event or consent.
The plugins in this sample expose increasingly detailed controls for product types, category exclusions, digital-content consent and sealed packaging. That is useful, but it creates a second class of dangerous defaults: a merchant can configure a legally meaningful exception without collecting the evidence that makes it applicable.
A receipt is more than “we got it”
The new function has two separate confirmation requirements.
First, the interface needs a confirmation action labelled “confirm withdrawal” or an equally unambiguous formulation. A generic Submit button is weaker because it does not say what legal action the consumer is taking.
Second, after submission, the trader must send an acknowledgement on a durable medium. Email is the obvious implementation, but the body matters. Article 54-bis requires the content of the declaration and its transmission date and time.
A useful receipt therefore preserves at least:
- the customer's name;
- the order or contract reference;
- the scope of the declaration;
- affected products and quantities where relevant;
- the electronic address chosen for the acknowledgement;
- the exact submission date and time;
- a stable reference for the recorded declaration.
The inspected plugins generally implement a two-step flow and customer email. Several store request records, timestamps, item snapshots and verification hashes. Those are sensible evidentiary features, although a cryptographic hash is not itself required by Article 54-bis and does not prove that an email reached its destination.
The wording remains just as important as the fields. A receipt should say the declaration was received, not that the consumer has applied for permission to make one.
The guest path is part of the requirement
A Withdraw action inside My Account → Orders is convenient. It is not a complete public path if the store permits guest checkout.
All five inspected plugins describe or implement some form of guest-facing flow, typically using order number and billing email, sometimes followed by a one-time link. The operational questions are:
- Is the public function created automatically or must the merchant publish a shortcode page?
- Is it linked prominently from the site, or only mentioned in documentation?
- Can a guest find it without reconstructing the account they never created?
- Does verification resist order enumeration?
- Does the email link work in the customer's language?
“Guest support” in a feature list is not the same as a guest being able to discover the function from the online interface. Placement is part of the rule.
A practical test any merchant can run
Do not stop after activating a plugin and seeing a new button in an administrator screenshot. Test the system from outside the store.
1. Discovery
Open a private browser window. Starting from the homepage, find the withdrawal function without using search or documentation. Repeat on mobile.
2. Guest checkout
Place a guest order. Confirm that the function remains usable without creating an account and does not disclose whether arbitrary order numbers exist.
3. The delivery gap
Create an order, leave it unfulfilled for more than fourteen days, then mark it delivered. The function should not disappear before the legal period has had a chance to begin.
4. Split delivery
Create an order with two physical items delivered on different dates. Verify which date the plugin uses and whether that matches the store's policy and fulfilment evidence.
5. Confirmation semantics
Read the exact labels. The first function must unambiguously lead to withdrawal; the second must unambiguously confirm it. Check that a reason is not required.
6. Durable acknowledgement
Submit a declaration and inspect the customer email. It should reproduce the declaration, not merely link back to a mutable admin page. Check the date, time, timezone and selected items.
7. Digital-content evidence
Place an order for paid digital content with immediate access. Confirm that any loss of the right depends on express consent and acknowledgement captured at checkout, not just the product's downloadable flag.
8. Failure paths
Disable JavaScript, delay scheduled tasks and test a failed email send. The merchant needs to know which parts of the legal flow depend on the browser, the queue and the mail transport.
What a safer WooCommerce implementation looks like
A robust implementation does not need to automate every legal decision. It needs to preserve the consumer's action without inventing facts.
For physical goods, that means connecting the withdrawal clock to a real delivery event where possible. If delivery data is absent, a consumer-protective implementation can leave the function available and flag the declaration for review. For services and digital content, it means selecting the correct contract event and preserving any consent on which an exception depends.
The customer-facing state machine can remain simple:
- Available — the function is visible and accessible.
- Review — the consumer sees the declaration they are about to make.
- Confirmed — the declaration is recorded and an immutable acknowledgement is generated.
- Processing — return instructions, eligibility assessment and refund operations happen separately.
That separation makes the system easier to explain, test and audit. It also prevents an internal returns workflow from rewriting the legal meaning of the consumer's click.
The real compliance dependency is operational data
The EU rule is described as a button because that is what consumers see. For merchants, its reliability depends on less visible data: contract type, delivery, consent, item scope and evidence.
WooCommerce can store all of those facts, but it does not create them merely because an order exists. A plugin can provide fields, statuses and workflows. The merchant still has to connect them to what actually happens in checkout and fulfilment.
The most important question is therefore not:
It is:
If the answer is “fourteen days after the order was created,” the interface may look compliant while shortening the consumer's legal window. If the answer is “after a delivery event we can identify and retain,” the button is backed by an operational fact.
Compliance is not the presence of a control. It is the ability to preserve a consumer's legal option when the store's data is incomplete. Confidently hiding the function based on the wrong timestamp is not automation. It is a guess with consequences.
This article provides a technical reading of public legislation and open-source software. It is not legal advice. Applicability and exceptions depend on the contract, catalog, customer and national implementation; merchants should obtain advice for their specific circumstances.