WooCommerce 11.1 ships on 1 September 2026, and it brings WooCommerce order withdrawal into core: a built-in withdrawal button, free, no plugin. Before you switch it on and assume the EU problem is solved, it is worth knowing what the feature actually does — because I read the source, and the 14-day clock is measured from a date the law does not use.
Since 19 June 2026, Directive (EU) 2023/2673 has required online stores selling to EU consumers to offer a plain digital way to withdraw from a contract. Until now that meant buying one of half a dozen plugins, and comparing them was its own small research project — the same exercise as working out what WooCommerce plugins really cost. Core changes that: the feature is free, bundled, and presented in the release as finished.
It is also disabled by default, it does not cancel anything, and one of its merchant-facing messages contradicts the code that triggers it. None of that is in the release notes. Here is what I found in OrderWithdrawalFormProcessor.php, the file that does the actual work.
The short answer
If you sell to EU consumers: switch it on, because a withdrawal function is legally required and this one is free — but do not treat it as compliance. It notifies you and nothing more, and its eligibility warning is calculated from the wrong date, so it will tell you that valid requests are too late. Everything below is the detail behind those two sentences.
What WooCommerce order withdrawal actually is
The short version, before the details:
- It is off by default. You enable it at WooCommerce → Settings → Advanced → Features. Toggling it queues a rewrite-rules flush, so the new URL starts working on its own.
- It adds one page:
/my-account/withdraw-order/, titled “Withdraw from contract”. You can rename the endpoint under Page Setup — it is stored in the optionwoocommerce_myaccount_order_withdrawal_endpoint. - It works without a login. A guest who never created an account can still file a request.
- It changes nothing about the order. No status change, no cancellation, no refund, no stock movement. It sends two emails and, when it can identify the order, writes an order note.
That last point is the one to internalise. This is a notification feature, not a returns workflow. WooCommerce built the legally-required front door; everything behind the door is still manual.
Matching is done on order number plus billing email. If both line up with a real order, the request is linked to it and an order note is added. If they do not match — a typo, a different email at checkout — the request still goes through and still emails you, just with nothing attached. You match it by hand.
The 14-day clock starts on the wrong day: order date, not delivery
This is the part worth your attention.
The processor defines the window as a constant, WITHDRAWAL_WINDOW_IN_DAYS = 14, and then decides whether a request is late using the order’s creation date:
$date_created = $order->get_date_created( 'edit' );
return ( time() - self::WITHDRAWAL_WINDOW_IN_SECONDS ) > $date_created->getTimestamp();
When that returns true, WooCommerce appends this sentence to your order note and to your notification email, verbatim:
“This order is older than 14 days. Only orders within 14 days of delivery are eligible for withdrawal.”
Read those two things together. The message says delivery. The code measures order creation. They are not the same date, and for physical goods they are never the same date.
The law is unambiguous about which one counts. Article 9(2) of Directive 2011/83/EU starts the withdrawal period, for sales contracts, on “the day on which the consumer or a third party other than the carrier and indicated by the consumer acquires physical possession of the goods.” Delivery day. Not checkout day.
So take an ordinary order: placed on the 1st, delivered on the 10th. The customer’s statutory window runs to the 24th. On the 18th they file a withdrawal request — comfortably within their rights. WooCommerce hands you that request stamped with a sentence telling you the order is too old and not eligible.
Why that warning is the dangerous part
One clarification that matters, because it is easy to overstate this: the customer is never blocked. The window check does not reject the submission, does not hide the form and produces no customer-facing error. The request goes through normally.
The warning appears in exactly two places, both of them yours: the order note, and the body of the merchant notification email. It is a message from WooCommerce to you.
Which is precisely why it is a problem. A busy merchant reads “not eligible for withdrawal” in an official-looking note from their own store software and declines the request. The software was measuring the wrong date; the refusal is the merchant’s, and so is the liability. A feature built for compliance can, in this one path, nudge you into non-compliance.
The practical fix is unglamorous but reliable: treat the warning as “check this one manually”, never as a verdict. Before responding to any flagged request, look up when the parcel actually arrived — tracking, courier confirmation, delivery notification — and count fourteen days from there.
It is better protected than “no login required” suggests
An unauthenticated form that emails you and writes to your orders sounds like an obvious abuse target. I expected to find that hole. It is not there, and the assumption is worth correcting because it will otherwise stop people from enabling a feature they may be legally obliged to offer.
The endpoint carries a real nonce — wp_verify_nonce against the action woocommerce_order_withdrawal — checked before any submitted value is used. On top of that, submissions run through WC_Rate_Limiter keyed on both the visitor’s IP and the submitted email address, with a delay of MINUTE_IN_SECONDS / 2: thirty seconds. Fail the send and the rate limit is released again, so a genuine customer is not punished for your mail server’s bad day.
This is ordinary, careful WordPress engineering. “No login required” here means guest-friendly, not unguarded.
Three more behaviours the release notes do not mention
One request per order, permanently
When a request is matched to an order, WooCommerce writes a meta flag to that order. Any later submission for the same order is refused with “A withdrawal request has already been submitted for this order.”
There is no expiry on that flag and no UI to clear it. If the first request was a mistake, or was filed by someone who mistyped their way onto the wrong order number, the real customer meets a wall and has to email you like it is 2009. Worth knowing before it happens rather than during.
If email fails, nothing is recorded at all
The order of operations here has consequences. submit_order_withdrawal() sends the customer acknowledgement and the merchant notification first. Only if both succeed does it mark the order, add the order note and create the inbox note.
If the send fails, the function shows the customer an error, releases the rate limit and returns. The order meta is never written. The order note is never added. The inbox note never appears. On a store with a broken SMTP configuration, a customer can file a withdrawal request that leaves no trace anywhere in WooCommerce — and the only party who knows it happened is the customer, who now has a screenshot and a statutory right.
If you enable this feature, verify your transactional email actually sends before you rely on it. This is the same class of trap as the one in WooCommerce 11.0’s built-in abandoned cart email, where the scheduler quietly skips the default block checkout: the feature is real, the edge case is where stores actually live.
No filters, no actions
The processor contains zero apply_filters and zero do_action calls. That is not an oversight you can work around — it is the whole extensibility story.
You cannot change the 14-day constant. You cannot make the window count from a delivery date you already store. You cannot suppress or reword the misleading warning, adjust the matching rules, or hook your helpdesk into the submission. Until WooCommerce adds extension points, the only routes are forking core or going back to a third-party plugin.
A pre-flight checklist before you enable it
None of the above is a reason to leave WooCommerce order withdrawal switched off — if you sell into the EU, you need a withdrawal function either way, and core’s is free. It is a reason to enable it deliberately. Five checks, in order, each of which takes a few minutes:
- Send yourself a test transactional email first. If your SMTP is broken, withdrawal requests will vanish silently. This is the single highest-consequence check on the list.
- Enable the feature, then load
/my-account/withdraw-order/while logged out. That is how your customers will see it. If it 404s, the rewrite flush has not run — save your permalinks once. - File a real test request against a real old order — one placed more than fourteen days ago — and read the merchant email you receive. You want to recognise the “older than 14 days” sentence in advance, in your own inbox, rather than the first time it lands on a live customer.
- Decide who reads these emails and write down the delivery-date rule. Whoever handles them needs one instruction: count fourteen days from delivery, not from the order, and treat WooCommerce’s warning as a prompt to check.
- Check whether you already run a withdrawal plugin. Two withdrawal buttons on one store is a worse customer experience than either alone, and core’s version does less than most of the paid ones.
So does WooCommerce order withdrawal make you compliant?
On its own, no — and WooCommerce says so itself. Its engineering post states the feature “may help your store meet these requirements, but it does not guarantee compliance.”
That is the honest framing. What core now gives you is the accessible digital withdrawal form the directive asks for, at a URL you can link from your terms, your order emails and your footer. What it does not give you is the part that carries the legal risk: recognising a valid request, judging it against the right date, refunding within the statutory period and keeping a record that you did.
Which is worth saying plainly, because “WooCommerce handles withdrawals now” is going to be repeated a lot over the next few weeks, and it is only a third true. The form is handled. The obligation is not.
Everything above came from reading one file before upgrading. That is a reasonable habit for any release that touches money or law: the release notes tell you what a feature is for, and the source tells you what it does. When those two disagree — as they do here, in a single sentence about delivery dates — it is the source that will be running on your store.
If the volume of these requests is what worries you, that is a different problem from compliance, and a more familiar one: withdrawal requests arrive as free-text messages from people who are often confused, sometimes angry and usually in a hurry. Someone has to read each one, find the order, work out the date and reply. That is support work, and it is the kind of work an AI agent on a WooCommerce store can take the first pass at — drafting the reply, pulling the order, flagging the ones a human must decide. It cannot decide them for you, and it is not legal advice. Neither is this article.
Run the five checks above on your own store before 1 September. They are more useful than any vendor’s opinion, mine included.
A note on shelf life: everything here describes the code as it stands for the 11.1 release. If WooCommerce switches the eligibility check to a delivery date, or adds filters to the processor, the two main limitations above disappear — and this article will be updated to say so. It is worth re-checking on any release that touches orders.
Sources
- Introducing Order Withdrawal in WooCommerce 11.1 — WooCommerce Developer Blog, 27 August 2026 (feature overview and the compliance disclaimer).
- WooCommerce 11.1: What’s coming for developers — WooCommerce Developer Blog, 18 August 2026 (release date and scope).
OrderWithdrawalFormProcessor.phpandOrderWithdrawalController.php— WooCommerce source,trunk, read 30 August 2026. All behaviour described above was verified here.- Directive 2011/83/EU, Article 9(2) — when the 14-day withdrawal period begins.
- Directive (EU) 2023/2673 — the withdrawal-function requirement in force since 19 June 2026.