EU Withdrawal Compliance

Сүрөттөө

From June 19, 2026, EU Directive 2023/2673 obliges every online retailer in the European Union to offer a digital withdrawal function that is at least as easy to use as the purchase flow itself. Most plugins in the directory stop at “a button”. This one ships the complete toolkit every EU store needs to comply — and a few things competitors don’t offer at any price.

Only this plugin in the directory ships, all of it, for free

  • Two-step confirmation function (Art. 11a(3)) — the EU-wide baseline of the online withdrawal right. The public form leads to a review screen with a read-only summary and a dedicated “Confirm withdrawal” button; the request is registered only when that button is pressed, preventing the unintended exercise of the right. Held server-side between the two steps in a single-use token, so it works even without JavaScript.
  • Complete durable-medium acknowledgement of receipt (Art. 11a(4)): the confirmation email reproduces the full content of the declaration (name, order, order date, scope, affected products) and the exact date and time of submission, carrying a verifiable SHA-256 receipt hash as tamper-evident proof — recomputable from the stored fields if a dispute arises.
  • Annex I.B model withdrawal form (Directive 2011/83/EU) generated dynamically from your shop data, rendered as a collapsible block below the public form, with a printable view on the same URL. Meets the pre-contractual information obligation of Art. 6(1)(h).
  • Double-consent checkboxes at the WooCommerce checkout for the two consents the directive expects to apply specific exceptions:
    • Mandatory consent for digital content (Art. 16(m)) — blocks the place-order step until accepted.
    • Optional consent for services started within the 14-day window (Art. 14(4)(a)) — enables pro-rated billing if the customer later withdraws.
      Every consent is persisted on the order with the exact text shown, accepted/declined state, timestamp, IP and user agent — durable proof in case of dispute.
  • Single “Withdrawal status” dropdown per product and per category with four explicit options (Standard, Digital content, Service started early, Other Article 16 exception). Drives both the Article 16 exclusion flag and the matching checkout consent in one place, with full subcategory inheritance.
  • Configurable public notice on excluded products, rendered between price and add-to-cart, with separate title+body for digital content and other Article 16 exceptions.
  • Article 16 exclusions with category inheritance — competing plugins gate this behind a paid Pro tier; here it’s free.
  • Native GDPR integration: suggested Privacy Policy snippet + personal-data exporter + eraser, all keyed on the customer email — no second GDPR plugin to install.
  • Standalone mode: the form, shortcode, request log, email notifications, SHA-256 receipt hash, Annex I.B model and GDPR integration all run without WooCommerce. The plugin always lives in its own top-level Withdrawals menu (with a Settings submenu), with or without WooCommerce — same path on every install.

Public-facing pieces

  • Public withdrawal page automatically created on activation with a neutral, translation-ready template and the form embedded via shortcode (with a “review with a legal advisor” disclaimer).
  • [ayudawp_withdrawal_form] shortcode for embedding the form anywhere on the site.
  • [ayudawp_withdrawal_link] shortcode for a permanent link to the withdrawal page from any widget area, footer or template part — helps meet the “clearly identifiable” requirement of Article 11a of Directive 2023/2673 without forcing a specific footer layout.
  • [ayudawp_withdrawal_excluded_notice] shortcode to place the excluded-product notice with page builders (Divi, Elementor, Bricks, ShopLentor) that render their own product template and skip the standard WooCommerce hooks.
  • HTML5-semantic form with HTML5 validation, honeypot anti-spam, escaped output, sanitized input and CSRF nonces.
  • Privacy-policy acceptance checkbox before submit, linked to the WordPress-configured Privacy Policy page.
  • Frontend and backend links generated by the plugin carry rel="noopener nofollow" to keep the site’s link equity contained.

WooCommerce-specific pieces (auto-activated when WooCommerce is detected)

  • My Account Right of withdrawal endpoint with a per-order “Withdraw” button shown while the order is in an eligible status, deep-linked to the form with the order pre-filled.
  • Withdrawal notice injected into transactional emails (processing, completed, customer invoice) with a direct link to the form pre-filled with the order number. Eligible order statuses configurable; admin emails never receive the notice.
  • Automatic verification of the order/email pair when WooCommerce is active: the request is matched to a real order and gated by the configured eligible statuses. The 14-day deadline is surfaced as an advisory flag for the admin, not an automatic rejection (the period legally runs from delivery, which the shop verifies).
  • Configurable advisory deadline: choose order date vs. WooCommerce completion date as the basis, plus optional grace days, for the deadline flag surfaced to the admin — all from the settings UI, no code.
  • Order-number compatibility with Sequential Order Numbers (free and Pro) and Custom Order Numbers for WooCommerce (both Tyche and WPFactory) out of the box, plus a filter to add any other numbering plugin or resolver.
  • “Withdrawal” column on the WooCommerce orders screen (legacy and HPOS) showing the status of any linked request, toggleable from Screen Options.
  • Private order notes added at every lifecycle step (request received, accepted, rejected, completed) including any admin comment.
  • HPOS-compatible from day one, declared via FeaturesUtil::declare_compatibility().

Admin tooling

  • Full request log as a private custom post type with status lifecycle (pending accepted rejected completed), customer details, scope (full / partial), IP, user agent and UTC submission timestamp for legal traceability.
  • CSV export of the request log for accounting and consumer-protection audits: an “Export to CSV” bulk action on the listing plus a filtered export by status and date range under Withdrawals Export withdrawals. Columns include submission and resolution timestamps, scope, status, acknowledgement-delivery flag, receipt hash and excluded items, with cells escaped against CSV/formula injection.
  • Audit trail per request: resolution timestamp recorded on every status change and a flag for whether the acknowledgement email was accepted for delivery (with its timestamp), both shown in the request detail and the CSV as burden-of-proof evidence.
  • Bulk actions to mark several requests as accepted, rejected or completed at once, with email notification on transition.
  • Status metabox with required comment when rejecting, optional comment when completing — comment forwarded to the customer email.
  • Acknowledgement email to the customer on confirmation: the Art. 11a(4) durable-medium receipt with the full declaration content, the date and time of submission and the SHA-256 hash as proof, plus a follow-up email on every status transition.
  • Notification email to the shop admin with reply-to set to the customer, sanitized against header injection.
  • Captured checkout consents surfaced in the request detail metabox: exact text, accepted/declined state, timestamp, IP, user agent — durable proof on file.
  • Consistent admin menu: always a top-level Withdrawals menu with a Settings submenu, regardless of whether WooCommerce is active. Same path on every install.
  • Legal disclaimer block in the settings page making it explicit that the plugin provides optional technical tools and does not guarantee legal compliance.
  • Mandatory / Recommended / Optional tags on every setting description so the merchant can scan the form quickly.

Built for production

  • Conditional asset loading: CSS only loads on the withdrawal page, single-product pages that actually show the excluded notice, and plugin admin screens.
  • Translation-ready, bundled es_ES translation, follows WordPress Coding Standards, fully escaped output and sanitized input, capability checks and nonces on every admin action.
  • 11 documented filters and 4 actions for developers and agencies to extend the plugin without forking.
  • PHP 7.4+, WordPress 6.0+, WooCommerce 7.0+ (optional).

Why this plugin?

The EU directive becomes enforceable in every member state on June 19, 2026, so the WordPress.org directory is filling up with “withdrawal button” plugins. This one stands out for verifiable reasons:

  • Fully free, no paid tier. No premium add-on, no feature locked behind an upsell, no “Pro” version on the horizon. Everything documented on this page is what you get on install.
  • Two-step “confirm withdrawal” function built in, the EU-wide baseline of Article 11a(3): the public form shows a read-only review screen and registers the request only after explicit confirmation, fully server-side and working without JavaScript — where many “button-only” plugins register the request on the very first click.
  • The only plugin in the directory that issues a SHA-256 receipt hash as durable-medium proof of every withdrawal request, recoverable from the stored fields if a dispute later arises.
  • The only plugin in the directory that ships the Annex I.B model withdrawal form dynamically generated from the shop data, with a printable view — meeting the Art. 6(1)(h) information obligation that the new directive does not replace.
  • The only plugin in the directory that injects the two consent checkboxes (Art. 16(m) digital content and Art. 14(4)(a) service started early) at the WooCommerce checkout, with durable proof persisted on the order.
  • The only plugin in the directory that ships Article 16 product/category exclusions with full subcategory inheritance — competing plugins gate this behind their own paid Pro tier.
  • The only plugin in the directory that integrates natively with the WordPress GDPR tools (Privacy Policy snippet + personal-data exporter and eraser) — no second GDPR plugin to install.
  • Standalone or with WooCommerce. Works without WooCommerce as a self-contained tool (form, shortcode, log, emails, SHA-256, GDPR, Annex I.B) and lights up store-specific features automatically when WooCommerce is detected.
  • Compatible by default with Sequential Order Numbers (free and Pro) and Custom Order Numbers for WooCommerce (Tyche and WPFactory), with a filter for any other numbering scheme.
  • Configurable from the settings UI, without writing code: deadline basis (order date vs. completion date) and grace days plus an optional strict deadline mode, eligible order statuses, withdrawal page, notification email (now accepting several recipients), email sender and editable status-email texts, consent text per type, excluded-notice text per type, editable form intro, optional B2B consumer self-declaration, Annex I.B trader address/phone/email, and which roles may manage requests.
  • Developer-friendly: 11 documented filters + 4 actions so agencies can extend it without forking.
  • Maintained by a Spanish WordPress trainer with 20+ years on the platform: bundled es_ES translation, prompt replies on the WordPress.org support forum and an active roadmap of free improvements (classic widget, Gutenberg block, dashboard widget, custom WC order status and more — all free).

Roadmap

Planned for upcoming free versions:

  • WooCommerce Checkout block support for the Art. 16(m) and Art. 14(4)(a) consent checkboxes, using woocommerce_register_additional_checkout_field() so the consents work on stores that have already migrated from the classic [woocommerce_checkout] shortcode to the block.
  • Classic widget to surface the withdrawal link in themes with widget areas.
  • Gutenberg block for the withdrawal link, fully supported in block themes (align, color, typography).
  • Dashboard widget with counters, pending requests and monthly basic stats.
  • Custom WooCommerce order status “Withdrawal requested” with automatic transition on acceptance.
  • Urgency indicators in the request list (days remaining, expired).
  • PDF download of the request with the SHA-256 receipt hash printed on it, reusing the same standalone-print infrastructure as the Annex I.B view.
  • Signed token in the email link so guest customers can check status without logging in.
  • Rate limiting on the public form to prevent abuse.
  • Optional IBAN field to speed up manual refunds.
  • HTML email templates that inherit the WooCommerce email theme.
  • Optional modal display mode for the shortcode.
  • Visible audit log on each request detail screen (status transitions, admin comments, email-delivery timestamps).
  • Optional opt-in auto-injection of the withdrawal link in wp_footer.

Privacy

This plugin stores the following personal data for each withdrawal request, exclusively to fulfil the legal traceability of consumer rights and to allow the shop to handle the request:

  • Customer name and email address (required to contact the consumer about the request).
  • Order reference and order date (required to validate the request against the purchase).
  • IP address and User-Agent string (required to evidence when and how the request was submitted, in line with the directive’s “durable medium” requirement).
  • Submission timestamp (UTC) and SHA-256 receipt hash (required to recompute and verify the integrity of the original submission if disputed).

Data is stored as a private custom post type entry (ayudawp_withdrawal) accessible only to administrators. The plugin does not transmit any data to third-party services; all communication happens between the shop and the customer via standard WordPress emails.

You should add a section to your site’s privacy policy describing this storage. The plugin contributes a suggested Privacy Policy snippet that you can paste from Settings Privacy Policy Guide. Withdrawal data is also exposed to the native WordPress Tools Export Personal Data and Tools Erase Personal Data screens (filtered by customer email).

Support

Need private support or custom development?

Do you need one-on-one help, priority troubleshooting, or a custom feature, integration, or tweak built specifically for your site? I offer private support and custom development. Just contact me and tell me what you need.

Need help or have suggestions?

Love the plugin? Please leave us a 5-star review and help spread the word!

About AyudaWP.com

We are specialists in WordPress security, SEO, AI and performance optimization plugins. We create tools that solve real problems for WordPress site owners while maintaining the highest coding standards and accessibility requirements.

Скриншоттор

Орнотуу

  1. Upload the plugin folder to /wp-content/plugins/.
  2. Activate the plugin from the Plugins screen.
  3. The plugin creates a “Right of withdrawal” page automatically with a sample legal template. Review and edit it from Pages.
  4. Go to Withdrawals Settings to configure the notification email address and the page that hosts the form.
  5. Add the URL of the withdrawal page to your footer or to the legal links section so it is visible from any page on your site.

FAQ.KG

Will the form check the 14-day deadline?

It does not auto-reject based on it. The 14-day withdrawal period legally runs from the moment the goods are delivered (or, for digital content, from the start of the download) — a date the shop cannot detect automatically — so rejecting a request on the order or completion date would risk turning away customers who are still within their legal window. Instead, the plugin flags requests that look past the approximate window in the admin notification email and lets you verify the real delivery date (for example from the carrier tracking) and decide. Eligibility for the form, the My Account button and the email notice is governed by the configured order statuses. You can still tune the advisory calculation (order date vs. completion date, plus grace days) under Withdrawals Settings, and the ayudawp_euw_grace_days filter keeps working; the ayudawp_euw_skip_deadline_check filter no longer has any effect, since there is no longer a deadline gate to skip. If your shop’s start date is reliable (services and digital content, where the period runs from the contract date, or shop pickup and predictable carriers), you can switch the Deadline enforcement setting from Advisory to Strict, which hides the button and blocks new requests once the deadline plus grace days has passed; Advisory remains the default and the safe choice for goods with unpredictable delivery.

How do I mark products that are excluded from the right of withdrawal (Article 16)?

The plugin uses a single Withdrawal status dropdown set per category and per product.

  1. By category: go to Products Categories, edit the category and pick a “Withdrawal status” — for example Other Article 16 exception for perishable or custom-made goods, or Digital content (Art. 16(m)) for sealed digital content. Every product in that category (and its descendant categories) will inherit the status automatically.
  2. By product: edit the product, scroll to the General tab and pick the “Withdrawal status” there. By default it is set to — Inherit from category; pick any other option (including Standard) to override the inheritance for that product only.

The four possible statuses are:

  • Standard — withdrawal applies normally (default).
  • Digital content (Art. 16(m)) — product is excluded from the withdrawal right, and a mandatory consent checkbox is shown at the WooCommerce checkout for any cart containing the product.
  • Service started early (Art. 14(4)(a)) — withdrawal still applies, and an optional consent checkbox is shown at checkout so the trader can charge a pro-rated amount if the customer withdraws after the service has started.
  • Other Article 16 exception — product is excluded (perishable, custom-made, hygiene-sealed, sealed audio/video/software media unsealed after delivery, etc.), no checkout consent needed.

When a withdrawal request lands on an order containing excluded items, the plugin flags it in the admin notification email and on the request detail screen. The request is never auto-rejected, because a partial withdrawal over the non-excluded items in the same order can still be valid. The admin reviews and decides.

If the Excluded products notice is enabled (default: yes), a configurable notice will also appear on the single product page between price and add-to-cart button so the consumer reads it before purchasing.

Upgrading from earlier versions: any category that was previously listed in the retired “Excluded categories” picker is migrated automatically to the new per-category dropdown (with status Other Article 16 exception) on the next admin request. No configuration is lost.

The excluded-product notice does not appear with my page builder (Divi, Elementor, Bricks…). What can I do?

Page builders such as Divi, Elementor, Bricks or ShopLentor render their own single-product template and skip the standard WooCommerce hook (woocommerce_single_product_summary) where the plugin injects the excluded-product notice, so it does not appear automatically. Drop the [ayudawp_withdrawal_excluded_notice] shortcode into your product layout (most builders have a “Shortcode” element) and the notice will print for the current product whenever that product is flagged as excluded. With no attributes it resolves the product being viewed; pass id="123" to target a specific product.

How do the checkout consent checkboxes work (Art. 16(m) and Art. 14(4)(a))?

The plugin can inject two consent checkboxes at the WooCommerce checkout when the cart contains products flagged for them:

  • Type A (mandatory, Art. 16(m)): digital content. The customer must accept it to complete the order; without acceptance recorded, the customer keeps the 14-day withdrawal right even after accessing the content.
  • Type B (optional, Art. 14(4)(a)): services started within the 14-day window. If accepted, the trader may charge a pro-rated amount when the customer withdraws after the service has started. Without it, an early withdrawal forces a full refund.

Each flag is set per product (General tab) or per category (Edit Category screen), with full subcategory inheritance — the same hierarchy used by the Article 16 exclusions module. The exact text shown to the customer, plus accepted/declined state, timestamp, IP and user agent, is persisted on the order so the trader has durable proof if the customer later contests the request. The metabox of each withdrawal request also surfaces these consents for the linked order.

The two checkboxes can be enabled/disabled globally from Withdrawals Settings Checkout consent, and their text is fully editable.

I sell to businesses (B2B). Can I exclude them from the right of withdrawal?

The right of withdrawal protects consumers (natural persons acting outside their trade or profession), not business buyers, but the plugin never decides that for you. Enable Consumer self-declaration under Withdrawals Settings Public withdrawal form and the form shows a required checkbox where the buyer declares they purchased as a consumer; a business that cannot declare it self-excludes, and the declaration is stored with the request as proof. It is off by default. Use the consumer_check="yes" shortcode attribute to force it on a specific landing page, or the ayudawp_euw_show_consumer_check filter for custom logic (VIES validation, a customer-type field, etc.).

Does the plugin include the Annex I.B model withdrawal form required by Directive 2011/83/EU?

Yes. The plugin renders the Annex I.B model form dynamically from the shop name, address (from WooCommerce when available) and notification email, with an optional trader phone configurable from settings. It appears as a collapsible block right below the public withdrawal form, with a printable view available from the same page. Providing this model is a pre-contractual information obligation under Art. 6(1)(h) of Directive 2011/83/EU — the online function added by Directive 2023/2673 complements but does not replace it.

What is the receipt verification code in the customer email?

It is a SHA-256 hash computed from the request data (post ID, customer name, email, order reference, scope, order date and submission timestamp). The customer keeps the email as a tamper-evident proof on a durable medium. If a dispute later arises, you can recompute the hash from the stored fields with the ayudawp_euw_compute_receipt_hash() helper and confirm the original submission was not altered.

Where are withdrawal requests stored?

Each request is saved as a private custom post type entry called ayudawp_withdrawal. You can manage them under the top-level Withdrawals menu in your admin area. They are not publicly accessible from the frontend.

Can I choose who can manage withdrawal requests?

Yes, from Withdrawals Settings Permissions. Because each request stores personal data (name, email, IP), you pick which user roles, besides the administrator, may view and manage them. The administrator always has access and cannot be unticked. On sites updating from an earlier version, the roles that could already see requests (typically Editor, and Shop manager on WooCommerce) keep their access so nothing breaks; you then untick any you want to remove. New installs start administrator-only.

Does it support HPOS (High-Performance Order Storage)?

Yes. The plugin declares HPOS compatibility on load.

Does the plugin work without WooCommerce?

Yes. The form, shortcode, withdrawal request log, email notifications, SHA-256 receipt hash and native GDPR integration all run as a standalone tool, with their own top-level Withdrawals menu in the admin and a Settings submenu. The plugin layers extra features on top automatically when WooCommerce is active: order/email validation gated by eligible order statuses (with an advisory deadline flag for the admin), “My Account” withdrawal endpoint, withdrawal notice injected into transactional emails, “Withdrawal” column in the orders screen, private order notes on every status change, and Article 16 exclusions by product/category. Activating WooCommerce later lights those features up; deactivating it leaves the standalone features intact.

Does it work with plugins that change the WooCommerce order number (Sequential Order Numbers, Custom Order Numbers, etc.)?

Yes. The form accepts both the internal WooCommerce order ID and the displayed order number. The resolver checks a list of known meta keys: the standard _order_number and _order_number_formatted (WooCommerce Sequential Order Numbers, free and Pro), plus _alg_wc_full_custom_order_number and _alg_wc_custom_order_number (Tyche / WPFactory “Custom Order Numbers for WooCommerce”). Add other numbering plugins with the ayudawp_euw_order_number_meta_keys filter; for schemes computed on the fly (e.g. YITH Sequential Order Number, custom integrations), short-circuit the lookup with the ayudawp_euw_pre_resolve_wc_order filter.

Will the notice appear on every WooCommerce email?

No. By default the notice is only added to the customer-facing emails relevant to the withdrawal window: order processing, completed and customer invoice (the manually triggered one). Admin emails never receive the notice. The notice is also gated by the configured list of eligible order statuses (default: Processing and Completed) so the manual invoice email only carries it when the order is in one of those statuses. You can change the email list with the ayudawp_euw_email_ids filter and the status list under Withdrawals Settings Eligible order statuses or with the ayudawp_euw_allowed_statuses filter.

Can I make the withdrawal notice in the emails more discreet?

Yes. Return an empty array from the ayudawp_euw_email_ids filter to remove the notice block (heading, text and button) from every order email:

add_filter( 'ayudawp_euw_email_ids', '__return_empty_array' );

This only affects the emails: the My Account button, the public form and the site footer link keep working, so the withdrawal function stays accessible as Article 11a requires. You can then add your own, more discreet wording with a plain link using WooCommerce’s per-email Additional content field (WooCommerce Settings Emails, then open each email such as Processing order or Completed order). Because it is set per email, your link appears only where you add it. Point it to your withdrawal page, and if you want to keep the order number pre-filled, add the {order_number} placeholder to the link, for example .../withdrawal/?order_id={order_number}. Do keep some reference in the order confirmation email: it is the contract confirmation on a durable medium, so the withdrawal information should stay reachable from it, just not necessarily as a prominent button.

In which languages is the plugin available?

All strings are translation-ready. Translations are managed through the official WordPress.org platform at translate.wordpress.org, so any locale with enough translated strings is delivered automatically to your site when the WordPress site language matches. Contributions to existing or new locales are welcome there.

Does the plugin pass GDPR requirements?

The plugin asks for explicit privacy policy acceptance before submission and stores the visitor IP and user agent only for the purpose of legal traceability of the request. See the Privacy section above for the full list of stored fields. The plugin also integrates natively with the WordPress GDPR tools: a suggested Privacy Policy snippet appears in Settings Privacy Policy Guide, and withdrawal data is exposed to Tools Export Personal Data and Tools Erase Personal Data so admins can fulfil access and erasure requests without leaving the WordPress admin.

What happens if the customer deletes their WordPress user account?

The withdrawal log is independent of the WordPress user table — it lives as a private custom post type indexed by the customer email. Deleting the user account does not delete the log automatically; the customer must request erasure through Tools Erase Personal Data (where the plugin registers an eraser that removes every withdrawal request matching the customer email) or you can delete the corresponding ayudawp_withdrawal entries manually if your retention policy requires it.

Can I customise the emails?

Yes. From Withdrawals Settings Withdrawal emails you can set the sender (“From name” and “From address”) for the plugin’s emails and edit the body of the accepted, rejected and completed status emails; left empty, each text falls back to the bundled default. The admin notification can be tailored with the ayudawp_euw_admin_email_lines filter, and all strings remain translatable through the standard WordPress text-domain. The emails are sent in plain text; HTML templates that inherit the WooCommerce email theme are planned for a later release.

Which hooks does the plugin expose for developers?

Filters:

  • ayudawp_euw_grace_days — extra days added to the 14-day deadline. The default is the value stored in settings; the filter receives that value, so returning $days + N adds on top of it.
  • ayudawp_euw_skip_deadline_check — retained for back-compat but no longer has any effect: the deadline is advisory and never gates a submission, so there is nothing to skip.
  • ayudawp_euw_email_ids — array of WooCommerce email IDs where the withdrawal notice is injected.
  • ayudawp_euw_allowed_statuses — array of order statuses (without the wc- prefix) for which the withdrawal button and email notice are offered. Receives the value stored in settings and the current WC_Order (when available).
  • ayudawp_euw_allow_unverified_order — return true to accept submissions whose order number cannot be matched against a real WooCommerce order. Useful for sites that also handle non-WC purchases.
  • ayudawp_euw_pre_resolve_wc_order — short-circuit the order resolver. Return a WC_Order instance to accept, false to reject, or null (default) to fall through to the built-in strategies. Useful for plugins that store the displayed order number outside the standard _order_number post meta (e.g. YITH Sequential Order Number, custom ERP integrations).
  • ayudawp_euw_resolve_wc_order — late filter that receives the resolved WC_Order (or false) and the raw reference, for auditing or last-chance overrides.
  • ayudawp_euw_order_number_meta_keys — the list of order post-meta keys checked when matching a typed order number, in priority order. Add your numbering plugin’s meta key.
  • ayudawp_euw_admin_email_lines — the array of lines of the admin notification email, just before they are joined, with the request context (post ID, name, email, order, scope, details) as the second argument. Add, remove or reorder lines.
  • ayudawp_euw_validation_result — the order/email validation result before the request proceeds. Return valid => false with an error code to reject a submission, for example from a captcha integration.
  • ayudawp_euw_show_consumer_check — whether the optional “bought as a consumer” checkbox is shown, with the current field values, so an integrator can decide per order (VIES validation, a customer-type field, etc.).

Actions:

  • ayudawp_euw_after_submission — fires after a withdrawal request has been processed. Arguments: CPT ID, submission data array.
  • ayudawp_euw_after_status_change — fires after a status change (individual or bulk). Arguments: CPT ID, new status, optional admin comment.
  • ayudawp_euw_after_form — fires inside the public form wrapper, right after the </form> tag, so modules can inject content below the form without coupling. No arguments. Used internally by the Annex I.B model form.
  • ayudawp_euw_form_before_submit — fires inside the form, right before the submit button, so an integrator can render a captcha/anti-spam widget or an extra field whose value is posted with the form. Arguments: current pre-fill values, lock flag.

Is this plugin enough to comply with EU Directive 2023/2673?

The plugin covers the functional requirements that Directive 2023/2673 imposes EU-wide from 19 June 2026: a discoverable digital withdrawal function, eligibility by order status with an advisory deadline flag, Article 16 exclusions with subcategory inheritance, durable-medium proof via the SHA-256 receipt hash, the model form from Annex I.B of Directive 2011/83/EU and the double-consent checkboxes at checkout that enable the Art. 16(m) and Art. 14(4)(a) exceptions. On top of that it adds operational tools that the directive does not mandate but that make handling requests practical: per-status email injection, status lifecycle with bulk actions, native GDPR integration, public notice on excluded products and full traceability. Member States can layer extra national requirements on top of the EU baseline — the two-step confirmation flow with an intermediate review page expected by the strictest member states (Germany, for example) is built in; a future release will adapt to any further requirement in the final Spanish Real Decreto once it is published. Legal compliance ultimately depends on your business model, catalog and jurisdiction; the plugin provides the technical building blocks, not legal advice — consult a consumer-law specialist for your specific case.

Сын-пикирлер

Июнь 25, 2026-ж. 1 reply
Este plugin merece mucho más que cinco estrellas. Resulta admirable y muy de agradecer que su autor dedique tiempo y esfuerzo en una herramienta gratuita, muy fácil de configurar y que cumple de maravilla con la legislación. No dudes en instalarlo, que funciona perfectamente y te va a ahorrar posibles problemas. Un millón de gracias, Fernando.
Июнь 18, 2026-ж. 1 reply
Exactly what every EU store needed for the June 19 deadline: two-step confirmation, SHA-256 receipt proof, Annex I.B form, Article 16 exclusions with category inheritance, all free with no paid tier.Tested it live on a real order and everything worked flawlessly.On top of that, Fernando replied to my forum questions within minutes, multiple times in the same day, even adding a suggestion to the roadmap on the spot.Rare to find this level of quality and support in a free plugin. Highly recommended.
Июнь 16, 2026-ж. 1 reply
Excelente plugin, funciona perfectamente y cumple la ley europea de desestimiento
Июнь 15, 2026-ж. 1 reply
Como todo lo que aparecía sobre el cumplimiento de la directiva sobre desistimiento estaba relacionado con Woocommerce, era reacio a instalarlo en mi web, que no lo usa. Pero leí y escuché a Fernando que su plugin también era operativo sin Woocommerce, así que me decidí a probarlo. He de decir que funciona perfectamente, es muy sencillo de configurar y cumple de sobra con la legislación aplicable, con una trazabilidad perfecta para cada solicitud. Por lo tanto, muy satisfecho por haberme decidido a instalarlo. Y muchas gracias a Fernando por haberlo creado y ofrecerlo gratis. Alguna propuesta, aunque entiendo que podría socavar el objetivo de cumplimiento normativo que tiene el plugin. Es un tema opinable. 1.- Los textos que encabezan el plugin podrían ser redundantes con las explicaciones que podemos poner en la página creada, justo lo que se escribe antes del shortcode. Una opción podría ser hacerlos editables en los ajustes, y así el usuario ajusta los textos en un sitio o en otro según su necesidad. 2.- También podrían ser editables los textos que se generan cuando se acepta y se completa la solicitud. A modo de ejemplo, si se devuelve el dinero por bizum, el cliente habría cobrado inmediatamente, no tras unos días. Pero como ha dicho Fernando, el principal objetivo era cumplir la ley antes del 19 de junio, y eso lo hace perfectamente. Lo demás, para cuando se pueda. Porque si algo caracteriza a Fernando es que va implementando mejoras en sus plugins a gran velocidad y con una dedicación encomiable.
Read all 12 reviews

Contributors & Developers

“EU Withdrawal Compliance” is open source software. The following people have contributed to this plugin.

Мүчөлөрү

“EU Withdrawal Compliance” has been translated into 2 locales. Thank you to the translators for their contributions.

Translate “EU Withdrawal Compliance” into your language.

Interested in development?

Browse the code, check out the SVN repository, or subscribe to the development log by RSS.

Өзгөртүүлөр

2.0.1

  • Improved: On Polylang and WPML sites, the links to the withdrawal page now resolve to the page translation in the visitor’s language: the printable Annex I.B form, the button in WooCommerce emails, the excluded-product “See the full withdrawal policy” link and the [ayudawp_withdrawal_link] shortcode. Single-language sites are unaffected.
  • Fix: The mandatory digital-content consent checkbox (Art. 16(m)) is now validated before WooCommerce PayPal Payments (and other express-checkout smart buttons) open their payment window, so the “you must accept the consent” message appears up front instead of only after returning from PayPal. The payment was never completed without the checkbox; this only corrects the confusing timing.

2.0.0

  • New: Configurable email sender. Two new fields in Settings (“From name” and “From address”) set the sender for the plugin’s own emails (acknowledgement, admin notification and status updates), which until now went out with the WordPress default (wordpress@yourdomain). The override is applied around each message only, the same way WooCommerce sets the sender of its own emails, so it never changes the site’s global sender. Left empty, the behaviour is unchanged.
  • New: Editable status-change email texts. The body of the accepted, rejected and completed emails can now be edited from Settings, so the wording matches how your shop really works (for example an instant Bizum refund instead of “a few business days”). Left empty, each falls back to the bundled default; the per-request comment field still works for one-off notes.
  • New: Editable (and hideable) intro text on the public form. The introductory paragraph above the form can be edited from Settings, or hidden entirely when the page already explains the withdrawal above the shortcode. The fixed legal note below it stays unchanged, since it must read identically across the form, the confirmation screen and the acknowledgement email.
  • New: Optional strict deadline mode. A setting under Withdrawals Settings Withdrawal deadline switches from the default “advisory” behaviour (never blocks, only flags late requests for review) to “strict”, which hides the My Account button and rejects new requests once the deadline plus grace days has passed. Advisory stays the default and the safe choice for goods with unpredictable delivery; strict is exact for services, digital content and shop pickup, where the period runs from a date the shop knows. A customer contesting a rejected request is never blocked by it: strict mode stops opening new requests, not handling one already open.
  • New: Optional “bought as a consumer” self-declaration on the form, for shops that also sell to businesses (B2B), where the right of withdrawal does not apply. Off by default; when enabled, the form shows a required checkbox declaring the purchase was made as a consumer (a natural person acting outside their trade or profession), stored with the request as durable proof. A shortcode attribute (consumer_check=”yes”) and the ayudawp_euw_show_consumer_check filter allow per-page or per-order control.
  • New: Permissions section to choose which user roles, besides the administrator, can view and manage withdrawal requests. Until now any Editor (and, on WooCommerce sites, the Shop manager) could see requests, which hold personal data; the request log now uses its own capability, granted only to the administrator and to the roles you tick. On update, the roles that could already see requests keep their access so nothing breaks, and you tighten it from there; new installs start administrator-only.
  • New: Dedicated trader contact email for the Annex I.B model form, set in Settings (Model withdrawal form section), separate from the notification address, so the public model form can show a clean contact mailbox while internal notices go elsewhere. Left empty it falls back to the notification address and then the site admin email.
  • New: [ayudawp_withdrawal_excluded_notice] shortcode to place the excluded-product notice with page builders (Divi, Elementor, Bricks, ShopLentor, etc.) that render their own product template and skip the standard WooCommerce hook where the notice normally appears. Drop it into the product layout and it prints the notice for the current product when that product is excluded.
  • New: Developer hooks. ayudawp_euw_form_before_submit (action) to render a captcha/anti-spam widget or an extra field inside the form, rejected through the ayudawp_euw_validation_result filter; ayudawp_euw_admin_email_lines to customise the admin notification; and ayudawp_euw_order_number_meta_keys to add custom order-number meta keys. The form shortcode also accepts more pre-fill attributes (name, date, scope, details).
  • Improved: The notification email field now accepts several recipients separated by commas, so the notice of each new request can reach more than one mailbox (shop manager and accounting, for example). Invalid addresses are dropped; if none are valid it falls back to the site admin email.
  • Improved: When the public form is submitted with something missing, each missing or invalid field is now highlighted in red with a short note next to it, instead of a single generic message with no visual cue, so the customer sees exactly what to fix. Accessible (aria-invalid, role=”alert”) and still without JavaScript.
  • Improved: After each step (confirmation, success message, validation errors) the form now scrolls into view instead of jumping to the top of the page, so the relevant content is where the customer is looking.
  • Fix: Order-number compatibility with Tyche “Custom Order Numbers for WooCommerce”. The resolver matched the order only against the _order_number meta, which Tyche does not use, so its customers got “we could not match this email with the order number provided” even with the right details. It now also checks Tyche’s _alg_wc_full_custom_order_number and _alg_wc_custom_order_number, with the new ayudawp_euw_order_number_meta_keys filter to add other numbering plugins.
  • Fix: The category “Withdrawal status” dropdown no longer offers a redundant “Standard” option that appeared not to save (in a category it meant the same as the empty option, and the save handler cleared it the same way). The per-product dropdown keeps it, where it is a real override of the category inheritance.
  • Fix: The Annex I.B model form no longer prints “Phone: [Phone number, if any]” when no trader phone is configured. The phone line is optional and is now omitted when empty, and the example shows as a placeholder in the settings field instead.

For older changelog entries, please check the changelog.txt file