Title: BuddyPilot Withdrawal (Easy Returns)
Author: BuddyPilot
Published: <strong>May 20, 2026</strong>
Last modified: June 2, 2026

---

Search plugins

![](https://ps.w.org/buddypilot-withdrawal/assets/banner-772x250.png?rev=3539945)

![](https://ps.w.org/buddypilot-withdrawal/assets/icon-256x256.png?rev=3539945)

# BuddyPilot Withdrawal (Easy Returns)

 By [BuddyPilot](https://profiles.wordpress.org/buddypilot/)

[Download](https://downloads.wordpress.org/plugin/buddypilot-withdrawal.1.0.13.zip)

 * [Details](https://vec.wordpress.org/plugins/buddypilot-withdrawal/#description)
 * [Reviews](https://vec.wordpress.org/plugins/buddypilot-withdrawal/#reviews)
 *  [Installation](https://vec.wordpress.org/plugins/buddypilot-withdrawal/#installation)
 * [Development](https://vec.wordpress.org/plugins/buddypilot-withdrawal/#developers)

 [Support](https://wordpress.org/support/plugin/buddypilot-withdrawal/)

## Description

**BuddyPilot Withdrawal for WooCommerce** implements the mandatory withdrawal function
required by **Article 11a of EU Directive 2023/2673** for online traders selling
to EU consumers.

From **19 June 2026**, every WooCommerce store with EU customers must provide a 
clearly visible, easily accessible withdrawal function (“withdraw from contract 
here” button) alongside the existing right of withdrawal informational requirements.

#### What the plugin does

**Customer-facing**

 * **Article 11a compliant “Withdraw from contract here” button** on the My Account
   order detail page
 * **Two-step withdrawal form**: item selection, declaration preview, one-click 
   confirm
 * **Variable product support**: each variant (size, colour, etc.) appears with 
   its full attribute labels as a separate line in the withdrawal form
 * **Guest checkout support** via direct verification flow (email + order number,
   no login required)
 * **My Account withdrawal history** list and declaration detail page
 * **Article 16(m) consent capture** at checkout for digital and virtual goods (
   configurable: required, optional, or hidden)
 * **Customer declaration email** serving as the durable-medium acknowledgement 
   required by Article 11a(3)
 * **Withdrawal resolved email** sent to the customer after the operator closes 
   the declaration; includes the EU ODR platform link (ec.europa.eu/consumers/odr)
   when any item is rejected
 * **Per-item resolution status** visible in the withdrawal form after a declaration
   is closed: each item shows an “accepted” or “rejected” badge with the rejection
   reason and ODR link, so the customer understands the outcome without contacting
   support

**Operator-facing**

 * **Order edit metabox** for registering declarations received via phone, mail 
   or in person
 * **Standalone “Add new declaration” page** for high-volume operator queues without
   leaving the declarations list
 * **Declarations list** with search, filtering and sorting in WooCommerce admin
 * **Declaration detail page** with full audit trail and resolution workflow: accept
   all, reject all, or partial resolution with per-item decisions and individual
   rejection reasons
 * **Admin notification email** sent immediately when a customer submits a withdrawal

**Legal and compliance**

 * **Custom order status** `wc-withdrawal` (optional, plugin-registered) with a 
   full change history logged as WooCommerce order notes
 * **Annex I(A) withdrawal information page** available to generate with one click
   from Settings
 * **Annex I(B) model withdrawal form page** available to generate with one click
   from Settings, published as a WordPress page and auto-linked from order emails
   and My Account
 * **Configurable refund period** (default 14 days, matches your contract terms)
 * **Configurable “delivered” statuses** that start the withdrawal clock
 * **Return cost attribution** (consumer pays or trader pays, per Annex I(A) requirements)
 * **GDPR personal data export and anonymisation** compatible with WordPress privacy
   tools
 * **HPOS compatibility** (High-Performance Order Storage) and legacy post-meta 
   mode
 * **WCAG 2.2 AA accessibility** compliance on all customer-facing forms
 * **Polish and English translations** included

#### Pro addon

Extended functionality is available via the [BuddyPilot Withdrawal Pro](https://buddypilot.io/plugins/withdrawal-pro/)
addon:

 * Operator-approved automatic refund built from declared items (one click from 
   the declaration screen)
 * Download-count verification: restores withdrawal right when a digital product
   was never actually downloaded, reducing unjustified refusal of refunds
 * WooCommerce Subscriptions support: cancels future renewals, prorates the current
   billing period per Article 9(2)(b)(iii)
 * WooCommerce Product Bundles support: automatic handling by bundle type; for per-
   item bundles with a configured discount, choose whether partial returns keep 
   the paid amount or forfeit the entire bundle discount
 * PDF export of the legal withdrawal declaration
 * Country-specific Annex I(B) form generation

## Installation

 1. Upload the plugin folder to `/wp-content/plugins/`, or install via **Plugins > 
    Add New**.
 2. Activate the plugin through the **Plugins** menu in WordPress.
 3. Go to **WooCommerce > Settings > Withdrawal** and configure:
 4.  * **General**: withdrawal period, grace period, delivered statuses
     * **Trader details**: name, address, return address, return cost responsibility
     * **Customer experience**: consent mode for digital goods, withdrawal information
       page
 5. In the **Customer experience** section click **“Create withdrawal information page”**
    to publish an EU-compliant Annex I(A) notice and Annex I(B) model form. The page
    URL is auto-linked from order emails and the My Account view-order screen.
 6. Customers eligible to withdraw will see a **“Withdraw from contract here”** call-
    to-action on their order page. The form works without JavaScript.

## FAQ

### Does the plugin work with guest checkout?

Yes. Create a dedicated withdrawal form page from WooCommerce > Settings > Withdrawal
> Customer (one click). Place the `[buddypilot_withdrawal_guest_form]` shortcode
on that page. Guest customers enter their order number and billing email; a secure
magic-link is sent to the billing address. Clicking the link opens the full interactive
withdrawal form without requiring an account.

### Does the plugin process refunds automatically?

The free version records the withdrawal declaration and changes the order status
to `wc-withdrawal`. The operator processes the refund using native WooCommerce tools.
One-click automatic refund built from declared items is available in the Pro addon.

### What does the plugin NOT do?

The plugin records withdrawal declarations as legal artifacts and notifies both 
the customer and operator. It does not automatically refund money, verify return
shipments, enforce refund deadlines, or handle accounting and VAT. The shop operator
is responsible for processing the refund within the legally required period (14 
days by default, configurable under WooCommerce > Settings > Withdrawal > General).

### Does the plugin support variable products?

Yes, with no extra configuration needed. Variable products (different sizes, colours,
etc.) are fully supported in the free version. Each variant appears in the withdrawal
form as a separate line with its full attribute labels (e.g. T-shirt, Size: L, Colour:
Black), so the customer specifies exactly what they are returning and the operator
sees the same detail on the declarations list and declaration detail page.

### Does the plugin work with WooCommerce Subscriptions or Product Bundles?

Subscription, bundle and composite product handling (cancel renewals, proration,
bundle refund modes) is part of the Pro addon. The free version treats them as standard
products.

### Does the plugin cover Article 16(m) consent for digital goods?

Yes. The plugin adds an explicit consent checkbox at checkout when the cart contains
downloadable or virtual products. The operator can choose between “required before
purchase”, “optional” or “hidden” (trader waives the exclusion). Consent is recorded
on the order and displayed in the withdrawal form.

### When does the withdrawal clock start?

The clock starts from the exact moment the order status changes to one of the “delivered”
statuses configured under WooCommerce > Settings > Withdrawal > General. The plugin
records the precise timestamp of that status change. As long as the deadline has
not passed, the customer sees how many days remain in the withdrawal form; once 
the deadline passes, the withdrawal button is no longer shown. Any built-in or custom
WooCommerce order status can be mapped as a “delivered” trigger (e.g. “Completed”,
a custom “Shipped” status, etc.). Leaving the list empty means the clock never starts,
which is useful during testing or when you want withdrawal to be available indefinitely.

### Is the plugin HPOS-compatible?

Yes. The plugin is fully compatible with WooCommerce High-Performance Order Storage(
HPOS) and falls back to legacy post-meta mode automatically.

## Reviews

![](https://secure.gravatar.com/avatar/f1558ed5b04101f0163d13a56026afd2f947f7fe6b7a3d0f513d811d88233812?
s=60&d=retro&r=g)

### 󠀁[Polecam!](https://wordpress.org/support/topic/polecam-66/)󠁿

 [woky](https://profiles.wordpress.org/woky/) May 29, 2026 1 reply

Bardzo dobra wtyczka. Rozwiązuje problem, który wiele sklepów będzie miało już niebawem.
Dobry kontakt z autorem. Będę ją polecał moim klientom.

 [ Read all 1 review ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/)

## Contributors & Developers

“BuddyPilot Withdrawal (Easy Returns)” is open source software. The following people
have contributed to this plugin.

Contributors

 *   [ BuddyPilot ](https://profiles.wordpress.org/buddypilot/)
 *   [ Dominik Kawula ](https://profiles.wordpress.org/domkawula/)

“BuddyPilot Withdrawal (Easy Returns)” has been translated into 5 locales. Thank
you to [the translators](https://translate.wordpress.org/projects/wp-plugins/buddypilot-withdrawal/contributors)
for their contributions.

[Translate “BuddyPilot Withdrawal (Easy Returns)” into your language.](https://translate.wordpress.org/projects/wp-plugins/buddypilot-withdrawal)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/buddypilot-withdrawal/),
check out the [SVN repository](https://plugins.svn.wordpress.org/buddypilot-withdrawal/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/buddypilot-withdrawal/)
by [RSS](https://plugins.trac.wordpress.org/log/buddypilot-withdrawal/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.0.13

 * Feature: Operators can now close a declaration as “Reject all items” or “Partial
   resolution” directly from the declaration detail page. Previously only full acceptance
   was available from that screen.
 * Feature: Partial resolution mode allows a per-item accept/reject decision with
   a mandatory rejection reason for each rejected item, so the customer receives
   an itemised outcome rather than a single blanket decision.
 * Feature: Rejection and partial resolution emails now include the EU ODR (Online
   Dispute Resolution) platform link (ec.europa.eu/consumers/odr) as required for
   cross-border dispute notifications under Article 11a.
 * Feature: The customer-facing withdrawal form now shows per-item resolution badges
   for fully-resolved declarations: accepted items display a confirmation badge,
   rejected items display the rejection reason and an ODR platform link. Previously
   the form showed a generic “order not eligible” message.
 * Improved: Reopening a declaration now resets the resolution type and operator
   note to null, so the operator starts with a clean slate when correcting an erroneous
   decision.
 * Security: Financial snapshot fields in the declaration record (item total, subtotal,
   tax) are immutable; they cannot be overwritten by POST manipulation during the
   resolution step.
 * Security: Resolution type is validated against a strict server-side whitelist(
   accepted / rejected / partial); arbitrary values are rejected before reaching
   the database.
 * Security: Already-closed declarations reject stale resolution attempts with an
   explicit error, preventing accidental overwrites in concurrent operator sessions.

#### 1.0.12

 * Fixed: Article 16(m) consent checkbox was not displayed at checkout on stores
   using WooCommerce 8.6 or later with a classic shortcode-based checkout (e.g. 
   Blocksy theme). The plugin now correctly detects whether the block checkout is
   in use before routing consent collection through the Additional Checkout Fields
   API.
 * Improved: Guest withdrawal session is now time-limited to 15 minutes after clicking
   the magic link, reducing the window in which a shared or inactive browser session
   could be misused.
 * Improved: Magic link redemption endpoint now applies rate limiting per order 
   per IP, consistent with the email verification endpoint.
 * Improved: Declaration detail and status toggle in the admin panel now verify 
   that the current user has access to the linked order, providing correct access
   control on multi-vendor setups.
 * Improved: Operator-customised consent label text is sanitised when saved, so 
   stored option value is always clean HTML.
 * Improved: Plugin no longer collects or stores the browser user-agent string with
   withdrawal declarations. The field served no active purpose and its removal reduces
   the personal data footprint. Existing installations will have the column dropped
   automatically on first load after updating (no data loss; the legal declaration
   record is preserved in full).

#### 1.0.11

 * Fixed: Plugin email class registration is now isolated per class. A constructor
   failure in one class no longer propagates into WooCommerce’s email dispatch and
   no longer blocks all WooCommerce transactional emails (new order, customer processing,
   etc.).

#### 1.0.10

 * Fixed: Attempting to bulk-change orders in withdrawal status to Completed or 
   Processing now triggers an admin notice informing the operator how many orders
   were skipped because the status change is not permitted for them, and that those
   orders must be handled individually.

#### 1.0.9

 * Feature: Guest verification form now accepts custom order numbers from third-
   party renumbering plugins (e.g. WebToffee Advanced Order Numbers, Custom Order
   Numbers for WooCommerce by Tyche Softwares). Guests can enter the formatted number
   shown in their confirmation email instead of the internal numeric order ID.
 * Feature: Order number input on the guest verification form no longer forces a
   numeric-only keyboard on mobile devices, allowing guests to type prefixed order
   numbers such as ORD-2024-1042.
 * Security: Input length on the guest verification form is now capped before order
   resolution, preventing crafted requests from triggering full-table database scans
   on stores with custom order number plugins installed.

#### 1.0.8

 * Feature: Withdrawal information page is now created as native WordPress blocks,
   allowing the operator to freely edit the legal wording directly in the block 
   editor.
 * Feature: New [buddypilot_withdrawal_info_block] shortcode renders the dynamic
   sections (trader contact details, return address, CTA button) independently so
   they always reflect current settings, even after manual edits to the surrounding
   text.
 * Feature: New “Overwrite information page content” button in WooCommerce > Settings
   > Withdrawal > Customer refreshes the page with the latest model notice wording
   at any time.
 * Fixed: Orders imported via the WooCommerce REST API (e.g. BaseLinker) were being
   re-imported in a loop because the plugin was writing unnecessary consent meta
   to every new order. The plugin no longer writes any meta to API-created orders
   that contain no digital items.

#### 1.0.7

 * Improved: Delivery tracking now uses a dedicated database table instead of order
   meta, preventing third-party order-sync integrations (e.g. BaseLinker) from re-
   importing orders on every delivery status update.
 * Improved: Confirmation screen now reads the configured withdrawal period dynamically
   instead of using a hardcoded value.
 * Improved: Rate limiting is now handled with a single atomic database operation.
 * Improved: Reduced database queries on the withdrawals list page.
 * Fixed: Several coding standards issues and minor security hardening.

#### 1.0.6

 * Feature: guest (non-logged-in) customers can now submit a withdrawal declaration
   directly from a dedicated form page. After entering their order number and billing
   email, a secure one-time magic-link is sent to the billing address; clicking 
   it opens the full interactive withdrawal form with no account required.
 * Feature: new `[buddypilot_withdrawal_guest_form]` shortcode for the guest withdrawal
   form page. The page and shortcode can be created with a single click from WooCommerce
   > Settings > Withdrawal > Customer.
 * Feature: `[buddypilot_withdrawal_information]` shortcode now shows an optional“
   Submit withdrawal form online” call-to-action button linking to the guest form
   page when the form page is configured. Existing information pages are not modified.
 * Feature: new “Withdrawal form access link (guest)” transactional email containing
   the magic-link; configurable under WooCommerce > Settings > Emails.

#### 1.0.5

 * Plugin renamed to BuddyPilot Withdrawal (Easy Returns) for better discoverability.
 * Added full translations for all 24 EU official languages: bg, cs, da, de (de_DE,
   de_AT, de_CH), el, es, et, fi, fr (fr_FR, fr_BE, fr_CH), ga, hr, hu, it, lt, 
   lv, mt, nl (nl_NL, nl_BE), pl, pt, ro, sk, sl, sv.
 * Translation quality: all 24 EU language files audited across three rounds; legal
   terminology verified against official national gazettes (EU Directive 2011/83/
   EU Annex I), diacritical marks restored, formal register enforced throughout,
   plural forms corrected, and internal consistency improved.

#### 1.0.4

 * Feature: fractional product quantities are now detected and reported in the withdrawal
   declaration. Full handling of fractional quantities (correct proration, refund
   calculation) is available in BuddyPilot Withdrawal Pro.
 * Feature: added “Upgrade to Pro” action link in the Plugins list, shown only when
   the Pro version is not active.

#### 1.0.3

 * Tested and confirmed compatible with WordPress 7.0.
 * Security: nonce is now verified before consuming a rate-limit slot, preventing
   CSRF probes from exhausting a user’s hourly budget.
 * Security: anonymous (guest) rate-limit buckets are now scoped per hashed IP instead
   of a shared `user_id=0` bucket.
 * Security: `sealed_map` and GET presealed IDs are validated against the order 
   before use, preventing crafted requests from attaching sealed confirmations to
   ordinary items.
 * Security: `apply_filters('buddypilot_withdrawal_declaration_items_payload')` 
   output is re-validated: entries with `item_id=0`, negative quantities or non-
   array values are stripped.
 * Security: `PermissionChecker` now explicitly blocks `user_id=0` regardless of
   order `customer_id`.
 * Security: `OperatorRegistration` checks `EXCLUDED_STATUSES` at handler entry 
   and flushes the permission cache after order save.
 * Security: `DeclarationRepository` validates `filing_date` format and rejects 
   zero-date strings.
 * Security: `RateLimit::tally()` returns `PHP_INT_MAX` when the rate-limit table
   is missing, preventing the limiter from silently becoming a no-op.
 * Fix: `round(qty, 10)` in `get_declared_quantities()` prevents IEEE-754 epsilon
   accumulation on fractional product quantities.
 * Fix: removed spurious `user_register` hook from Privacy Manager; only `woocommerce_created_customer`
   is used.

#### 1.0.2

 * Code: renamed all short `bpw_` / `bpw-` identifiers to the full `buddypilot_withdrawal_`/`
   buddypilot-withdrawal-` prefix throughout (affects AJAX/admin-post action names,
   script handles, localized JS object, metabox ID, WooCommerce field type, nonce
   field names, lock keys and GET parameters). Resolves WordPress.org unique-prefix
   review requirement.

#### 1.0.1

 * Security: all admin and AJAX request handlers now verify user permissions before
   nonce verification, consistent with WordPress security guidelines.
 * Security: the AJAX path in the operator registration handler now uses `check_ajax_referer()`
   instead of `check_admin_referer()`, returning a proper AJAX error response on
   nonce failure.
 * Security: added `current_user_can()` gate to the admin notice handler for info-
   page creation status.
 * Security: currency symbol and separator values from localized price format data
   are now escaped on the JavaScript side before insertion into the DOM.
 * Code: moved inline JavaScript from the operator registration template to an enqueued
   file (`assets/js/admin/operator-registration.js`); strings passed via `wp_localize_script()`.
 * Code: removed `load_plugin_textdomain()` call; WordPress 4.6 and later loads 
   translations automatically for plugins hosted on WordPress.org.

#### 1.0.0

 * First stable release. Full implementation of the Article 11a withdrawal function:
   two-step form, guest verification, operator manual registration, declarations
   admin list, configurable period and delivered statuses, Article 16(m) consent
   capture, Annex I(A)/(B) page generator, customer and admin emails, My Account
   withdrawal history, HPOS support, GDPR hooks, WCAG 2.2 AA compliance, Polish 
   and English translations.

## Meta

 *  Version **1.0.13**
 *  Last updated **4 days ago**
 *  Active installations **10+**
 *  WordPress version ** 5.8 or higher **
 *  Tested up to **7.0**
 *  PHP version ** 7.4 or higher **
 *  Languages
 * [Croatian](https://hr.wordpress.org/plugins/buddypilot-withdrawal/), [Czech](https://cs.wordpress.org/plugins/buddypilot-withdrawal/),
   [Dutch](https://nl.wordpress.org/plugins/buddypilot-withdrawal/), [Dutch (Belgium)](https://nl-be.wordpress.org/plugins/buddypilot-withdrawal/),
   [English (US)](https://wordpress.org/plugins/buddypilot-withdrawal/), and [Polish](https://pl.wordpress.org/plugins/buddypilot-withdrawal/).
 *  [Translate into your language](https://translate.wordpress.org/projects/wp-plugins/buddypilot-withdrawal)
 * Tags
 * [compliance](https://vec.wordpress.org/plugins/tags/compliance/)[eu](https://vec.wordpress.org/plugins/tags/eu/)
   [right of withdrawal](https://vec.wordpress.org/plugins/tags/right-of-withdrawal/)
   [withdrawal](https://vec.wordpress.org/plugins/tags/withdrawal/)[woocommerce](https://vec.wordpress.org/plugins/tags/woocommerce/)
 *  [Advanced View](https://vec.wordpress.org/plugins/buddypilot-withdrawal/advanced/)

## Ratings

 5 out of 5 stars.

 *  [  1 5-star review     ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/?filter=5)
 *  [  0 4-star reviews     ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/?filter=4)
 *  [  0 3-star reviews     ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/?filter=3)
 *  [  0 2-star reviews     ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/?filter=2)
 *  [  0 1-star reviews     ](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/?filter=1)

[Your review](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/buddypilot-withdrawal/reviews/)

## Contributors

 *   [ BuddyPilot ](https://profiles.wordpress.org/buddypilot/)
 *   [ Dominik Kawula ](https://profiles.wordpress.org/domkawula/)

## Support

Issues resolved in last two months:

     1 out of 1

 [View support forum](https://wordpress.org/support/plugin/buddypilot-withdrawal/)