tmonier / sylius-withdrawal-plugin
EU withdrawal button (Directive (EU) 2023/2673, Art. 11a Consumer Rights Directive) for Sylius 2
Package info
github.com/thibmonier/sylius-withdrawal-plugin
Type:sylius-plugin
pkg:composer/tmonier/sylius-withdrawal-plugin
Requires
- php: ^8.2
- sylius/sylius: ^2.2
Requires (Dev)
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.1
- phpstan/phpstan-doctrine: ^2.0
- phpstan/phpstan-webmozart-assert: ^2.0
- phpunit/phpunit: ^10.5
- sylius-labs/coding-standard: ^4.4
- sylius/test-application: ^2.0.0@alpha
- symfony/browser-kit: ^6.4 || ^7.4 || ^8.0
- symfony/css-selector: ^6.4 || ^7.4 || ^8.0
- symfony/debug-bundle: ^6.4 || ^7.4 || ^8.0
- symfony/dom-crawler: ^6.4 || ^7.4 || ^8.0
- symfony/dotenv: ^6.4 || ^7.4 || ^8.0
- symfony/http-client: ^6.4 || ^7.4 || ^8.0
- symfony/intl: ^6.4 || ^7.4 || ^8.0
- symfony/runtime: ^6.4 || ^7.4 || ^8.0
- symfony/web-profiler-bundle: ^6.4 || ^7.4 || ^8.0
- symfony/webpack-encore-bundle: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-10 18:07:30 UTC
README
The EU "withdraw from contract here" button for Sylius 2.
Since 19 June 2026, every online shop selling to consumers in the European Union must offer a withdrawal function: a clearly labelled button letting consumers withdraw from a distance contract online, followed by a confirmation step and an acknowledgement of receipt on a durable medium (Article 11a of Directive 2011/83/EU, introduced by Directive (EU) 2023/2673).
This plugin implements it for Sylius 2.2+, free and open source.
Features
| Requirement (Art. 11a) | What the plugin does |
|---|---|
| Function labelled "withdraw from contract here", continuously available, easily accessible | Link in the footer of every shop page and button on the customer's order page. No login required. |
| Consumer provides or confirms name, contract and e-mail | Statement form, prefilled for logged-in customers (recital 37) |
| Separate step labelled only "confirm withdrawal" | Dedicated confirmation page with a single confirm button; nothing is recorded before it |
| Acknowledgement of receipt on a durable medium with content, date and time | E-mail sent immediately with the full statement and the server timestamp |
| Statement sent within the period is timely | Statements are never refused; timeliness is computed and shown to the merchant |
Also included:
- Partial withdrawal: logged-in customers pick the order items; guests describe them in free text.
- Back office: Sales → Withdrawals grid with filters, detail page, accept / reject workflow (Symfony Workflow), deadline and timeliness indicator, and proof that the acknowledgement was sent.
- Order matching without order enumeration: a guest statement is linked to the order only when the number and the customer e-mail match, and the response is the same either way.
- Merchant notification e-mail to the channel contact address.
- Official wording in English, French, German, Spanish, Italian, Dutch and Polish, taken from the Official Journal and checked by tests.
- Customisable through Twig Hooks; MySQL, MariaDB and PostgreSQL support.
Screenshots
| Confirmation step | Acknowledgement of receipt |
|---|---|
![]() |
![]() |
Requirements
| Version | |
|---|---|
| PHP | 8.2+ |
| Sylius | 2.2, 2.3 |
| Symfony | 6.4, 7.4, 8.0 |
Installation
-
Require the package:
composer require tmonier/sylius-withdrawal-plugin
-
Register the bundle in
config/bundles.php(Symfony Flex does it automatically once a recipe exists):Tmonier\SyliusWithdrawalPlugin\TmonierSyliusWithdrawalPlugin::class => ['all' => true],
-
Import the configuration in
config/packages/tmonier_sylius_withdrawal.yaml:imports: - { resource: "@TmonierSyliusWithdrawalPlugin/config/config.yaml" }
-
Import the routes in
config/routes/tmonier_sylius_withdrawal.yaml:tmonier_sylius_withdrawal: resource: "@TmonierSyliusWithdrawalPlugin/config/routes.yaml"
-
Run the migrations:
bin/console doctrine:migrations:migrate
-
Make sure each channel has a contact e-mail if you want to receive merchant notifications.
Configuration
# config/packages/tmonier_sylius_withdrawal.yaml tmonier_sylius_withdrawal: period_days: 14 # used to flag late statements; never used to refuse one notify_merchant: true # copy of each statement to the channel contact e-mail
The withdrawal period starts when the goods are delivered (Art. 9). Sylius does not track delivery, so the plugin uses the last shipment date, falling back to the checkout completion date. The deadline is informational.
Customisation
Every page is built with Twig Hooks:
| Hook | Content |
|---|---|
sylius_shop.base.footer.content.menu.help_links → tmonier_withdrawal |
Footer link |
sylius_shop.account.order.show.content.main.header.buttons → tmonier_withdrawal |
Button on the account order page |
tmonier_withdrawal.shop.withdrawal.form.content |
Statement form page |
tmonier_withdrawal.shop.withdrawal.confirm.content |
Confirmation page |
tmonier_withdrawal.shop.withdrawal.done.content |
Acknowledgement page |
tmonier_withdrawal.admin.withdrawal_request.show.content.sections#left/#right |
Admin detail page |
E-mail templates can be overridden in templates/bundles/TmonierSyliusWithdrawalPlugin/email/.
Important
If you move or restyle the footer link, keep it prominent and reachable from every page, and keep the confirm button labelled only with the official wording.
What you still have to do
The plugin provides the technical function. As a merchant you remain responsible for:
- mentioning the existence and placement of the withdrawal function in your pre-contractual information and terms (Art. 6(1)(h) as amended);
- processing refunds (for example with
sylius/refund-plugin); - handling the exceptions of Art. 16 (personalised goods, perishables, etc.) when you review a statement;
- checking national transposition specifics.
This plugin is not legal advice.
Development
docker compose up -d mysql mailhog # or your own MySQL composer install (cd vendor/sylius/test-application && yarn install && yarn build) vendor/bin/console doctrine:database:create vendor/bin/console doctrine:migrations:migrate -n vendor/bin/console sylius:fixtures:load -n vendor/bin/phpunit --testsuite unit APP_ENV=test vendor/bin/phpunit --testsuite non-unit # needs a test database with fixtures vendor/bin/phpstan analyse vendor/bin/ecs check
License
MIT. See LICENSE.



