{"id":318295,"date":"2026-05-28T13:53:50","date_gmt":"2026-05-28T13:53:50","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/eucomply-withdrawal-button\/"},"modified":"2026-06-01T05:22:09","modified_gmt":"2026-06-01T05:22:09","slug":"purchase-contract-withdrawal-button-for-woocommerce","status":"publish","type":"plugin","link":"https:\/\/vec.wordpress.org\/plugins\/purchase-contract-withdrawal-button-for-woocommerce\/","author":17808888,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.3.1","stable_tag":"1.3.1","tested":"7.0","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Purchase Contract Withdrawal Button for WooCommerce","header_author":"Jakub Ka\u0161p\u00e1rek","header_description":"Adds a \"Withdraw from purchase contract\" button for logged-in customers and an optional shortcode-based form for guests. Centralised admin overview of withdrawal requests, CSV export, configurable cooling-off period and reference date, automated emails. Designed to comply with EU Directive 2023\/2673 \u2014 required in the Czech Republic from 19 June 2026 (\u00a7 1830a Civil Code).","assets_banners_color":"7b5c9e","last_updated":"2026-06-01 05:22:09","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/kasparek-net\/purchase-contract-withdrawal-button-for-woocommerce","header_author_uri":"https:\/\/github.com\/kasparek-net","rating":0,"author_block_rating":0,"active_installs":0,"downloads":133,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.2.3":{"tag":"1.2.3","author":"jakubkasparek","date":"2026-05-28 13:53:23"},"1.3.0":{"tag":"1.3.0","author":"jakubkasparek","date":"2026-05-29 08:23:55"},"1.3.1":{"tag":"1.3.1","author":"jakubkasparek","date":"2026-06-01 05:22:09"}},"upgrade_notice":{"1.3.1":"<p>Halves the deployed package size by shipping only compiled translations.<\/p>","1.3.0":"<p>Adds bundled translations for all 24 official EU languages, intended for stores serving the EU single market before the 19 June 2026 enforcement date of EU Directive 2023\/2673.<\/p>","1.2.3":"<p>The &quot;Custom button CSS&quot; setting has been removed per WordPress.org guidelines. Override styles in your theme stylesheet instead.<\/p>","1.2.2":"<p>Code quality improvements addressing Plugin Check feedback. No functional changes.<\/p>","1.2.1":"<p>Declares WooCommerce as a required plugin dependency (WP 6.5+).<\/p>","1.2.0":"<p>Adds a guest shortcode for non-logged-in customers, a dedicated Withdrawals admin screen with CSV export, a date-of-delivery meta box, and order actions for resolving and submitting on behalf.<\/p>","1.1.0":"<p>Adds configurable button position and a Custom CSS field in settings.<\/p>","1.0.1":"<p>Naming and security cleanup per WP.org review.<\/p>","1.0.0":"<p>First release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3552277,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3552277,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3552277,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3552277,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.2.3","1.3.0","1.3.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3552277,"resolution":"1","location":"assets","locale":"","width":1440,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3552277,"resolution":"2","location":"assets","locale":"","width":1440,"height":900},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3552277,"resolution":"3","location":"assets","locale":"","width":1440,"height":900},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3552277,"resolution":"4","location":"assets","locale":"","width":1440,"height":900},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3552277,"resolution":"5","location":"assets","locale":"","width":1440,"height":1100}},"screenshots":{"1":"Withdrawals admin overview \u2014 filter by pending\/resolved, search, date range, bulk actions, CSV export, per-row Resolve.","2":"Order edit screen with the \"Withdrawal cooling-off\" meta box \u2014 set the date of delivery, see the cooling-off deadline and submission details.","3":"Order actions dropdown \u2014 submit a withdrawal on behalf of the customer, or mark an existing one as resolved.","4":"Guest lookup form rendered by the [pcwb_withdrawal_form] shortcode on a public page \u2014 order number + billing email.","5":"Guest withdrawal form after successful lookup \u2014 pre-filled order summary, optional refund account and reason, explicit confirmation."}},"plugin_section":[],"plugin_tags":[395,131785,9682,245590,286],"plugin_category":[45],"plugin_contributors":[245695],"plugin_business_model":[],"class_list":["post-318295","plugin","type-plugin","status-publish","hentry","plugin_tags-eu","plugin_tags-gdpr","plugin_tags-refund","plugin_tags-withdrawal","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_contributors-jakubkasparek","plugin_committers-jakubkasparek"],"banners":{"banner":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/banner-772x250.png?rev=3552277","banner_2x":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/banner-1544x500.png?rev=3552277","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/icon-128x128.png?rev=3552277","icon_2x":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/icon-256x256.png?rev=3552277","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/screenshot-1.png?rev=3552277","caption":"Withdrawals admin overview \u2014 filter by pending\/resolved, search, date range, bulk actions, CSV export, per-row Resolve."},{"src":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/screenshot-2.png?rev=3552277","caption":"Order edit screen with the \"Withdrawal cooling-off\" meta box \u2014 set the date of delivery, see the cooling-off deadline and submission details."},{"src":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/screenshot-3.png?rev=3552277","caption":"Order actions dropdown \u2014 submit a withdrawal on behalf of the customer, or mark an existing one as resolved."},{"src":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/screenshot-4.png?rev=3552277","caption":"Guest lookup form rendered by the [pcwb_withdrawal_form] shortcode on a public page \u2014 order number + billing email."},{"src":"https:\/\/ps.w.org\/purchase-contract-withdrawal-button-for-woocommerce\/assets\/screenshot-5.png?rev=3552277","caption":"Guest withdrawal form after successful lookup \u2014 pre-filled order summary, optional refund account and reason, explicit confirmation."}],"raw_content":"<!--section=description-->\n<p>Purchase Contract Withdrawal Button for WooCommerce adds a clearly-labeled \"Withdraw from purchase contract\" button to each customer's order detail page in WooCommerce My Account. It implements the two-step submission process required by EU consumer law: the customer clicks the button, reviews their order details, optionally provides a refund bank account and reason, and then explicitly confirms.<\/p>\n\n<p>After submission:<\/p>\n\n<ul>\n<li>The order is moved to a configurable status (default: On hold)<\/li>\n<li>A note is added to the order with the customer's reason and refund account<\/li>\n<li>The customer receives a confirmation email<\/li>\n<li>The store administrator receives a notification email with all submission details<\/li>\n<\/ul>\n\n<p>Both emails are registered as standard WooCommerce email classes and can be customized in <strong>WooCommerce \u2192 Settings \u2192 Emails<\/strong>.<\/p>\n\n<h4>Legal context<\/h4>\n\n<p>This plugin is designed to help merchants comply with EU Directive 2023\/2673, which requires online merchants to provide consumers with a direct online function to withdraw from a contract \u2014 the same way they could enter into it. In the Czech Republic, this obligation enters into force on <strong>19 June 2026<\/strong> under \u00a7 1830a of the Civil Code (the so-called \"button amendment 2.0\"). Similar transposition is required across all EU member states.<\/p>\n\n<p>The plugin is not legal advice. Merchants remain responsible for ensuring their full implementation (including terms and conditions, refund processing, and goods return) complies with applicable law.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li><strong>Two-step submission<\/strong> \u2014 button click \u2192 form with details \u2192 explicit confirmation<\/li>\n<li><strong>Guest shortcode<\/strong> \u2014 optional <code>[pcwb_withdrawal_form]<\/code> for non-logged-in customers (order number + billing email lookup, rate-limited)<\/li>\n<li><strong>Configurable cooling-off period<\/strong> \u2014 default 14 days, adjustable per store<\/li>\n<li><strong>Date of delivery meta box<\/strong> \u2014 admin can record when the goods were received; the cooling-off period then runs from that date (legally correct under EU law)<\/li>\n<li><strong>Configurable eligible statuses<\/strong> \u2014 choose which order statuses show the button<\/li>\n<li><strong>Configurable post-submission status<\/strong> \u2014 typically On hold or Processing<\/li>\n<li><strong>WooCommerce email integration<\/strong> \u2014 customer + admin emails as native WC_Email classes<\/li>\n<li><strong>Admin overview<\/strong> \u2014 dedicated \"Withdrawals\" screen under WooCommerce: filter by pending\/resolved, search, CSV export, bulk \"Mark as resolved\"<\/li>\n<li><strong>Order actions<\/strong> \u2014 admins can submit a withdrawal on behalf of the customer (e.g. phone request) and mark requests resolved from the order edit screen<\/li>\n<li><strong>Translatable<\/strong> \u2014 full text domain, .pot included, all 24 official EU languages bundled (Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Irish, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Slovak, Slovenian, Spanish, Swedish)<\/li>\n<li><strong>Theme-overridable templates<\/strong> \u2014 copy <code>templates\/withdrawal-form.php<\/code> or <code>templates\/guest-lookup.php<\/code> into your theme to customize<\/li>\n<li><strong>HPOS-compatible<\/strong> \u2014 works with WooCommerce's High-Performance Order Storage<\/li>\n<\/ul>\n\n<h4>Filters and actions<\/h4>\n\n<ul>\n<li><code>pcwb_eligible_statuses<\/code> \u2014 array of statuses where the button is shown<\/li>\n<li><code>pcwb_period_days<\/code> \u2014 cooling-off period override<\/li>\n<li><code>pcwb_period_reference_date<\/code> ($date, $order) \u2014 override the cooling-off reference date<\/li>\n<li><code>pcwb_new_status<\/code> \u2014 order status applied after submission<\/li>\n<li><code>pcwb_admin_recipient<\/code> \u2014 admin email recipient override<\/li>\n<li><code>pcwb_after_submit<\/code> ($order, $reason, $account, $source) \u2014 fires after a successful submission (source = customer|guest|admin)<\/li>\n<li><code>pcwb_after_resolve<\/code> ($order, $resolved_by_user_id) \u2014 fires when an admin marks a withdrawal resolved<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/<\/code>, or install via Plugins \u2192 Add New.<\/li>\n<li>Activate the plugin through the Plugins menu.<\/li>\n<li>Go to <strong>WooCommerce \u2192 Withdrawal Button<\/strong> to configure the cooling-off period and eligible statuses.<\/li>\n<li>(Optional) Customize the customer and admin email templates in <strong>WooCommerce \u2192 Settings \u2192 Emails<\/strong>.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20does%20the%20button%20appear%3F\"><h3>Where does the button appear?<\/h3><\/dt>\n<dd><p>In the customer's My Account \u2192 Orders \u2192 individual order detail page, below the order table. It only appears when the order matches an eligible status and is within the configured cooling-off period.<\/p><\/dd>\n<dt id=\"can%20the%20customer%20submit%20multiple%20withdrawals%3F\"><h3>Can the customer submit multiple withdrawals?<\/h3><\/dt>\n<dd><p>No. Once a withdrawal is submitted, the button is replaced with a notice showing the submission date and a contact email.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20issue%20refunds%20automatically%3F\"><h3>Does the plugin issue refunds automatically?<\/h3><\/dt>\n<dd><p>No. The plugin records the customer's intent to withdraw \u2014 it does not move money. Refunds are processed manually (or via your payment gateway) after the goods are received. The plugin notifies the admin via email so the merchant can follow up.<\/p><\/dd>\n<dt id=\"can%20guest%20orders%20use%20the%20button%3F\"><h3>Can guest orders use the button?<\/h3><\/dt>\n<dd><p>No. The button requires the customer to be logged in (their account must be linked to the order). This matches the typical \"logged-in My Account\" flow.<\/p><\/dd>\n<dt id=\"how%20do%20i%20customize%20the%20email%20content%3F\"><h3>How do I customize the email content?<\/h3><\/dt>\n<dd><p>Go to <strong>WooCommerce \u2192 Settings \u2192 Emails<\/strong>, then select either \"Withdrawal confirmation (customer)\" or \"Withdrawal notification (admin)\". You can edit subject, heading, and additional content. For deeper customization, copy template files from <code>templates\/emails\/<\/code> into your theme.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20gdpr-compliant%3F\"><h3>Is this plugin GDPR-compliant?<\/h3><\/dt>\n<dd><p>The plugin only stores data the customer has explicitly submitted (reason, refund bank account) as order metadata, alongside the existing order record. It does not transmit data to third parties.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.3.1<\/h4>\n\n<ul>\n<li>Lightweight build \u2014 the deployed package now ships only compiled <code>.mo<\/code> translation files. The matching <code>.po<\/code> source files remain in the GitHub repository for translators.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>New: bundled translations for all 24 official EU languages \u2014 Bulgarian, Croatian, Danish, Dutch, Estonian, Finnish, French, German, Greek, Hungarian, Irish, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Slovenian, Spanish, Swedish (in addition to existing Czech and Slovak). Native-quality review via translate.wordpress.org is welcome and will override the bundled files automatically.<\/li>\n<\/ul>\n\n<h4>1.2.3<\/h4>\n\n<ul>\n<li>Removed: \"Custom button CSS\" textarea setting and the <code>pcwb_custom_css<\/code> option. WordPress.org guidelines do not allow plugins to accept arbitrary CSS input; the bundled stylesheet remains, and themes can override styles in their own files.<\/li>\n<li>Replaced inline arrow-function <code>sanitize_callback<\/code> for <code>pcwb_guest_enabled<\/code> with a named class method.<\/li>\n<\/ul>\n\n<h4>1.2.2<\/h4>\n\n<ul>\n<li>Code quality: addressed Plugin Check warnings \u2014 annotated CSV-streaming filesystem calls, admin list-table filter parameters, and intentional WooCommerce email hooks with explanatory <code>phpcs:ignore<\/code> comments.<\/li>\n<li>Internal: renamed template-scoped <code>$completed<\/code> variable to <code>$pcwb_completed<\/code> in admin email templates.<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Added <code>Requires Plugins: woocommerce<\/code> header to declare WooCommerce as a required dependency (WP 6.5+).<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>New: optional <code>[pcwb_withdrawal_form]<\/code> shortcode for non-logged-in customers (order number + email lookup with rate limiting, short-lived submission token).<\/li>\n<li>New: dedicated \"Withdrawals\" admin screen under WooCommerce \u2014 list, filter (pending\/resolved\/all), date range, search, bulk \"Mark as resolved\", and CSV export.<\/li>\n<li>New: \"Withdrawal cooling-off\" order meta box \u2014 enter the date the goods were delivered to make the cooling-off period start from the legally correct moment.<\/li>\n<li>New: order actions \u2014 \"Submit withdrawal on behalf of customer\" and \"Mark withdrawal as resolved\".<\/li>\n<li>New filter: <code>pcwb_period_reference_date<\/code> to override the reference date programmatically.<\/li>\n<li>New action: <code>pcwb_after_resolve<\/code> ($order, $resolved_by) fires when a withdrawal is marked resolved.<\/li>\n<li>Internal: <code>PCWB_Frontend::do_submit()<\/code> and <code>::resolve()<\/code> are now reusable across customer, guest and admin flows.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>New: configurable button position (after order table, before order table, top of view-order page, or orders list row action).<\/li>\n<li>New: Custom CSS field in settings \u2014 inject styles for the withdrawal button and form without touching theme files.<\/li>\n<li>New filter: <code>pcwb_button_positions<\/code> to register additional hooks.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Renamed plugin and slug to comply with WP.org plugin naming guidelines.<\/li>\n<li>Fixed: replaced <code>esc_url_raw<\/code> with <code>esc_url<\/code> for displayed URL in plain text admin notification.<\/li>\n<li>Updated function\/class prefix to <code>pcwb_<\/code> \/ <code>PCWB_<\/code> for collision safety.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial release.<\/li>\n<\/ul>","raw_excerpt":"Adds a withdrawal button to WooCommerce My Account orders. Two-step submission with nonce, configurable cooling-off, automated emails.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/318295","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=318295"}],"author":[{"embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/jakubkasparek"}],"wp:attachment":[{"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=318295"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=318295"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=318295"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=318295"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=318295"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/vec.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=318295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}