Order Withdrawal (EU Compliance)
EU law gives consumers the right to withdraw from distance contracts within a statutory period — 14 days by default. Under EU Directive 2023/2673, which entered into force on 19 June 2026, online stores must provide consumers with a simple, direct mechanism to exercise that right. This plugin adds a Withdraw from Contract button to the customer's order page in J2Commerce, records each request in a dedicated dashboard, and sends legally-required confirmation emails automatically.
If your store sells to customers in the EU or EEA, this plugin is not optional — it is a legal requirement.
Prerequisites
- J2Commerce installed and activated on Joomla 6
- Your Joomla site email (outgoing mail) configured and working
- Administrator access to the J2Commerce back end
Installation
This plugin is a separate add-on available from the J2Commerce Extensions Store. It is not included with the core J2Commerce component.
- Purchase and download the
app_orderwithdrawal.zippackage from the J2Commerce website. - Go to System -> Install -> Extensions.
- Upload the
app_orderwithdrawal.zippackage file. - The plugin installs and enables automatically. A database table (
#__j2commerce_apporderwithdrawals) is created on install.
After installation, go to J2Commerce -> Apps and locate Order Withdrawal (EU Compliance). Click the plugin name to open its settings.
Configuration
The plugin settings are split across four tabs.
Basic Settings
| Field | Description | Default | Recommended |
|---|---|---|---|
| Enable Withdrawal Function | Master on/off switch. When disabled, no withdrawal button appears anywhere on the storefront. | Yes | Yes |
| Default Withdrawal Period | Number of days from the order date during which a customer may withdraw. EU standard is 14 days. | 14 | 14 (or higher if your policy allows) |
| Eligible Order Statuses | Select which order statuses allow a withdrawal request. Orders in any other status will not show the button. | (none selected — all statuses eligible) | Select the statuses that represent confirmed/paid orders, such as Confirmed or Processing |
| Require Two-Step Confirmation | When enabled, the customer must complete a two-screen confirmation flow before the request is submitted. This protects against accidental withdrawals. | Yes | Yes |
| Allow Partial Withdrawals | When enabled, customers may withdraw individual items from a multi-item order rather than the entire order. | No | Your business preference |
| Show Dashboard Quick Icon | Displays a pending withdrawal count badge on the J2Commerce dashboard quick-icon panel. | Yes | Yes |
| Debug Mode | Writes detailed log entries to administrator/logs/app_orderwithdrawal.php and the browser console. Disable in production. | No | No (only enable when troubleshooting) |
Display Settings
| Field | Description | Default |
|---|---|---|
| Button Position | Where the withdrawal button appears: Order Details Page Only, Order History Page Only, or Both Pages. | Order Details Page Only |
| Show Days Remaining | Displays a countdown badge (e.g., "12 days remaining") next to the withdrawal button. | Yes |
| Button Theme Color | Hex color for the withdrawal button and accent elements. Accepts any valid hex color. | #dc3545 (Bootstrap red) |
| Custom CSS | Paste extra CSS rules here if you need to override the default withdrawal widget styling. Loaded on every page that renders the button. | (empty) |
Email Settings
| Field | Description | Default |
|---|---|---|
| Send Customer Confirmation Email | Sends the legally-required written confirmation to the customer the moment their withdrawal is recorded. Required by EU law. | Yes |
| Send Admin Notification Email | Notifies your team immediately when a new withdrawal request arrives. | Yes |
| Admin Email Addresses | Comma-separated list of email addresses to receive admin notifications. Leave blank to use the Joomla site's default From address. | (empty — falls back to Joomla mailfrom) |
| Brand Logo for Emails | Selects an image from your Joomla media library to appear at the top of both email templates. | (none) |
| UTM Source | UTM tracking parameter appended to links inside confirmation emails. | j2commerce |
| UTM Medium | UTM medium parameter for email links. | email |
| UTM Campaign | UTM campaign parameter for email links. | withdrawal |
Advanced Settings
| Field | Description | Default |
|---|---|---|
| Auto-Process After Days | If set to a number greater than 0, pending withdrawal requests are automatically marked as Processed after that many days. Set to 0 to disable. | 0 |
| Extend Period if Not Informed | When enabled, the withdrawal deadline extends to 12 months if the store has not properly informed the consumer of their withdrawal right (an EU Directive requirement). Leave this on unless you have a lawyer-reviewed alternative. | Yes |
| Extend Deadline on Weekends | If the withdrawal deadline falls on a Saturday or Sunday, it automatically shifts to the next Monday. Matches standard EU consumer-law practice. | Yes |
| Enable Audit Logging | Records every withdrawal action (created, processed, rejected) in the database for compliance tracking. | Yes |
| Export Format | The format used when exporting the withdrawal list. Currently CSV. | CSV |
Per-Product Exception Settings
Some product types are exempt from the statutory withdrawal right under EU law. The plugin adds a Withdrawal Settings tab directly on each product's edit screen so you can configure exceptions product by product.
To reach a product's withdrawal settings:
- Go to J2Commerce -> Catalog -> Products.
- Open any product and click the Withdrawal Settings tab (added by this plugin).
| Field | Description |
|---|---|
| Withdrawal Allowed | Toggle withdrawal on or off for this specific product. If set to No, any order containing this product is ineligible for withdrawal. |
| Custom Withdrawal Period (Days) | Override the default period for this product. Leave blank to inherit the global default. The plugin uses the longest period among all items in an order. |
| Exception Type | Classifies the product under one of the eight EU-recognised statutory exceptions (see table below). |
| Exception Reason | A short explanation shown to the customer if they try to withdraw and this product is the reason for ineligibility. |
| Require Digital Content Consent | When set to Yes, the customer must explicitly consent before accessing digital content. This is required by EU law for digital downloads to be exempt from the withdrawal right. |
Exception Types
| Exception Type | EU Legal Basis | Example Products |
|---|---|---|
| No Exception | — | Physical goods with no special status |
| Perishable Goods | Art. 16(d) Consumer Rights Directive | Fresh food, flowers, plants |
| Custom / Personalized Products | Art. 16(c) | Engraved items, made-to-order clothing |
| Sealed Hygiene Products | Art. 16(e) | Underwear, cosmetics opened after delivery |
| Sealed Audio / Video / Software | Art. 16(i) | DVDs, games, boxed software with broken seal |
| Digital Content | Art. 16(m) | Downloads, streaming access, software keys |
| Time-Sensitive Services | Art. 16(l) | Event tickets, accommodation for specific dates |
| Urgent Repairs | Art. 16(b) | Emergency repair services explicitly requested |
| Newspapers / Magazines | Art. 16(k) | Single-issue periodicals |
Setting any exception type other than No Exception blocks the withdrawal button for any order containing that product.
How the Customer-Facing Withdrawal Flow Works
When a logged-in customer views an eligible order, the plugin injects a Withdraw from Contract button (with an optional days-remaining countdown badge) into the order details page.
Clicking the button opens a two-step modal:
Step 1 — Withdrawal Form
The modal displays:
- An EU Consumer Rights notice with the exact number of days remaining.
- A Name field (required).
- An Email field (required).
- An optional Reason for Withdrawal text area.
- An important notice explaining that goods must be returned and refunds will use the original payment method.
The customer clicks Confirm Withdrawal to proceed to Step 2.
Step 2 — Final Confirmation
The second screen shows a warning that this action cannot be undone, lists what happens next (email confirmation, review timeline, 14-day refund window, return instructions), and requires the customer to tick a confirmation checkbox before submitting.
After Submission
Once the customer clicks Confirm Withdrawal on Step 2:
- A withdrawal record is created in the database with status Pending.
- A confirmation email is sent to the customer immediately (subject: "Withdrawal Confirmation — Order [number]"). This email serves as the legally-required durable-medium receipt.
- An admin notification email is sent to the addresses configured in Email Settings.
- The button is replaced by an informational message: "Confirmed."
Admin Workflow
All withdrawal requests are managed from J2Commerce -> Apps -> Order Withdrawal Management.
The dashboard is the default view. A quick-icon badge on the J2Commerce home screen shows the count of pending requests when Show Dashboard Quick Icon is enabled.
Dashboard
The dashboard shows four summary statistics:
- Total Requests — lifetime count
- Pending — requests awaiting action
- Processed This Month — withdrawals closed in the current calendar month
- Rejection Rate — percentage of requests that were rejected
Quick links navigate to the full list, the pending-only filtered view, the analytics screen, the CSV export, and the plugin settings.
Withdrawal Requests List
Click Manage Withdrawals from the dashboard, or go to J2Commerce -> Apps -> Order Withdrawal Management and click Manage Withdrawals in the toolbar.
The list shows all withdrawal requests with columns for Order, Customer, Request Date, Status, and Days Since the request. Use the search bar to filter by order number, customer name, or email. Use the Status filter to narrow to Pending, Confirmed, Processed, or Rejected requests.
Available statuses:
| Status | Meaning |
|---|---|
| Pending | Request submitted but not yet actioned by the store |
| Confirmed | Customer completed the two-step form |
| Processed | Admin has processed the refund/return |
| Rejected | Admin has rejected the request |
| Cancelled | Request was cancelled |
Per-row actions: each row has View Details (opens the edit screen) and View Order (opens the J2Commerce order) links.
Toolbar bulk actions (select rows with checkboxes first):
- Change Status -> Process Selected — marks selected requests as Processed
- Change Status -> Reject Selected — marks selected requests as Rejected
- Change Status -> Delete — permanently removes selected records (with confirmation)
Processing a Single Withdrawal
Click View Details on any row to open the withdrawal detail screen. This screen shows:
- Order Information — order number, date, total, current status, and the ordered items
- Customer Information — name, email, user ID, and IP address (for audit purposes)
- Request Details — request date, confirmation date, withdrawal reason, partial items if applicable
- Refund Information — fields to record the refund amount and a payment reference number
- Admin Notes — a free-text field for internal notes not visible to the customer
- Timeline — log of when the record was created and when emails were sent
To action the withdrawal:
- Fill in Refund Amount and Refund Reference once the refund has been issued through your payment gateway.
- Add any Admin Notes for your records.
- Click Save Changes.
- Use the Mark as Processed or Reject toolbar buttons to update the status.
Analytics
Click Analytics in the toolbar from the list view to access the analytics screen. It displays:
- A line chart of withdrawals over the past 12 months
- A pie/doughnut chart of status distribution
- A breakdown table showing count and percentage per status, and average processing time in days
CSV Export
Click Export Data in the toolbar from the list view. The plugin streams a CSV file named withdrawals_YYYY-MM-DD.csv containing: ID, Order number, Customer name, Email, Request date, Status, Reason, Refund amount, Processed date, and Admin notes.
Emails
Customer Confirmation Email
Sent automatically when a withdrawal is confirmed. Contains:
- Greeting with the customer's name
- Confirmation that the withdrawal request has been received
- Request details (order number, withdrawal ID, date and time of receipt)
- What happens next (review, return instructions, 14-day refund)
- A legal notice citing EU Directive 2023/2673, which serves as written confirmation on a durable medium as required by law
- Your brand logo (if configured in Email Settings)
Admin Notification Email
Sent to each address in Admin Email Addresses (or the site's default mailfrom). Contains:
- The order number and customer details
- Request date and withdrawal reason
- A direct link to the withdrawal detail screen in the admin back end
Troubleshooting
The Withdraw from Contract button does not appear
Work through the following checklist in order:
- Master switch disabled. Go to the plugin settings (Basic Settings tab) and confirm Enable Withdrawal Function is set to Yes.
- Exempt products in the order. If any product in the order has an Exception Type set (other than "No Exception") or has Withdrawal Allowed set to No, the entire order is ineligible. Check the Withdrawal Settings tab on each product in the order.
- Withdrawal period expired. The order is older than the configured withdrawal period. Check the order's creation date against Default Withdrawal Period. If Extend Period if Not Informed is enabled, the effective deadline is 12 months.
- Order status not eligible. If Eligible Order Statuses has any entries, the order's current status must be in that list. Open the order and compare its status to the selected statuses in the plugin settings.
- Withdrawal already submitted. If the customer already submitted a withdrawal for this order (and it was not cancelled), the button is replaced by a "Confirmed" notice. Check the Withdrawal Requests list for a record matching that order number.
- Customer is not the order owner. The button only appears to the logged-in user who placed the order. Administrators do not see it when viewing orders in the front end under a different account.
- Plugin is disabled. Go to System -> Manage -> Extensions, search for Order Withdrawal, and confirm the plugin is enabled (green tick).
Customers are not receiving confirmation emails
- Confirm Send Customer Confirmation Email is set to Yes in Email Settings.
- Confirm your Joomla outgoing mail is configured and working: System -> Global Configuration -> Server tab -> Mail Settings. Use the Send Test Mail button.
- Ask the customer to check their spam folder. The email is sent from your site's From address.
- Enable Debug Mode in Basic Settings, reproduce the withdrawal, and check
administrator/logs/app_orderwithdrawal.phpfor any "Email send failed" entries.
The withdrawal period appears much longer than expected
Check the Extend Period if Not Informed setting in the Advanced Settings tab. When this is enabled, the plugin uses a 12-month (360-day) period if the "not informed" condition is met. Turn this off if your store correctly informs customers of their withdrawal right at the point of sale, and set your preferred period in Default Withdrawal Period.
The deadline falls on a weekend and the button disappears unexpectedly
This is by design. When Extend Deadline on Weekends is enabled (the default), a deadline that falls on Saturday is pushed to Monday, and one that falls on Sunday is pushed to the following Monday. This is standard EU practice and cannot legally disadvantage the consumer.
What's New Compared to J2Store
If your store previously used the J2Store version of this plugin, here is what changed in J2Commerce:
| Area | J2Store (v4) | J2Commerce (v6) |
|---|---|---|
| Architecture | FOF 2 (plgJ2StoreApp_orderwithdrawal extending F40\Plugin\Plugin) | Native Joomla 6 MVC — CMSPlugin + SubscriberInterface, DI container |
| JavaScript | jQuery AJAX | Vanilla ES6+ fetch() with async/await |
| Emails | Called J2Store email helper indirectly | Joomla 6 MailerFactoryInterface — emails actually send via your configured mail transport |
| IDOR protection | Order ownership not always verified server-side | Every AJAX handler verifies $order->user_id === $identity->id before processing |
| Admin views | FOF-generated list / edit screens | Core Joomla list layout primitives (LayoutHelper, HTMLHelper::_('searchtools.sort'), Pagination) |
| CSRF | Token present but inconsistently checked | Session::checkToken() on every POST handler; admin actions also check core.manage ACL |
| Audit log | Flat file only | Joomla Log API to administrator/logs/app_orderwithdrawal.php with configurable debug/error levels |
| Analytics | Basic counts | Chart.js line and doughnut charts with monthly trend data and per-status breakdown |