BuddyPilot Withdrawal (Easy Returns)

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

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 status management
  • 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 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:
    • 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
  4. 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.
  5. 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 — 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

There are no reviews for this plugin.

Contributors & Developers

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

Contributors

“BuddyPilot Withdrawal (Easy Returns)” has been translated into 3 locales. Thank you to the translators for their contributions.

Translate “BuddyPilot Withdrawal (Easy Returns)” into your language.

Interested in development?

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

Changelog

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.