=== Solucom Recesso EU for WooCommerce ===
Contributors: solucom
Tags: woocommerce, funnelkit, right of withdrawal, recesso, gdpr
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.0.0
License: GPL-3.0-or-later
License URI: https://www.gnu.org/licenses/gpl-3.0.html

EU statutory withdrawal button (Art. 11a) for WooCommerce and FunnelKit: two-step flow, durable-medium receipt, tamper-evident log.

== Description ==

Product page & documentation: https://solucom.si/solucom-recesso-eu/

From 19 June 2026, EU law (Directive (EU) 2023/2673, new Art. 11a of the Consumer Rights Directive; Italy: Art. 54-bis Codice del Consumo, D.Lgs. 209/2025) requires online stores to provide a **withdrawal function** that lets consumers withdraw from a distance contract as easily as they concluded it. Solucom Recesso EU adds that function — and everything around it you need to run it and to prove you did it right — to WooCommerce and WooCommerce stores running FunnelKit.

= How it works (in plain terms) =

1. An eligible customer opens their order and clicks the statutory **"Withdraw from contract here"** button — in their account, from a link in the order e-mail, or on a public page (no account needed: they look the order up with its number + e-mail).
2. A simple **two-step form** appears: they review what they are withdrawing from (optionally ticking only some items — partial withdrawal is allowed), then confirm. No reason required, no hoops.
3. The instant they confirm, the customer receives an **acknowledgement of receipt on a durable medium** — an e-mail, a PDF copy and a permanent verifiable link — showing exactly what was withdrawn and the precise date and time. The order is flagged "withdrawal requested".
4. Every step is written to a **tamper-evident, append-only log** (hash-chained and timestamped) so you can prove what happened and when. You then handle the refund as usual — the plugin records that too.

That is the whole customer experience. Everything below exists to make it correct, easy to run, and defensible.

= For your customers =

* A prominently displayed, legible button with the **exact statutory wording per language** (IT, EN, DE, FR, ES, SV — extensible).
* The button appears where customers actually look: the **account area** (order list, order detail, a dedicated "Right of withdrawal" tab), a **link inside order e-mails**, a **public self-service page** with guest lookup, and anywhere via **shortcodes** or the **Gutenberg block**.
* On stores using **FunnelKit**, the withdrawal link also appears on the **FunnelKit thank-you page** and can be placed in FunnelKit Automations e-mails via the `{{recesso_url}}` merge tag.
* A short, reassuring **step-by-step guide** during the flow (timing, refund, returns); the wording and the withdrawal window (≥14 days — you may grant more) are editable.
* When an order is genuinely exempt, a clear **"why is the button not here" note** explains the specific legal exception, instead of leaving the customer confused.
* A human-readable **verification certificate** for the receipt (integrity, order, date, hash) — not raw code.

= For you (the merchant) =

* An onboarding **Dashboard** with a setup checklist (one-click fixes), a plain "how it works" walkthrough, and a "where the button appears / why it might not" explainer.
* A one-click **e-mail delivery test** that detects your SMTP plugin and proves the receipt actually reaches the inbox — the #1 cause of "nothing happened".
* A **Requests dashboard** to manage every withdrawal: status (open / processed / refunded), a chain-integrity badge, and one-click **mark processed**, **resend receipt** and **open the order to refund** (the refund is logged as proof you met the 14 days). Subscription and partial-withdrawal requests are flagged.
* A **Compliance page**: a go-live countdown, the statutory labels in use, the document checklist with ready-to-paste clauses, and environment warnings (Complianz / cache / multilingual) to fix.
* Receipts are **real WooCommerce e-mails** (your logo, colours, header) with a preview; the front-end is styleable through **documented CSS variables and class names** from your theme or the Customizer's Additional CSS.

= Smart legal handling (so you don't have to think about it) =

* **Subscriptions** — the law gives one 14-day right per contract, so the button shows on the **initial order only** and is hidden on renewals (WooCommerce Subscriptions). Fail-safe, with opt-in overrides.
* **Partial withdrawal** — customers can withdraw from only some items of an order.
* **Art. 59 exemptions** — tag products or categories by the specific statutory reason (events on a fixed date, digital content with immediate access, a service fully performed…). For the conditional reasons the plugin captures the customer's **express consent at checkout** (WooCommerce classic + block checkout and FunnelKit Aero checkout), stores it as evidence, sends the required durable-medium confirmation, and only then hides the button. **Physical products always keep the right** — never hidden by mistake.
* **Applicability by country** — EU/EEA consumers only (default) or always; B2B (VAT) orders can be treated as out of scope.

= FunnelKit integration =

The FunnelKit integration works on top of standard WooCommerce orders — no separate order store, no data duplication. Specifically:

* The withdrawal link appears on the **FunnelKit thank-you page** (Funnel Builder / Aero Checkout) so customers can act immediately after purchase.
* The **Art. 59 exemption consent checkboxes** are rendered on the **FunnelKit Aero checkout**, using the same validation as the WooCommerce checkout.
* A **`{{recesso_url}}`** merge tag is registered for **FunnelKit Automations** emails, resolving to the per-order withdrawal link so you can include it in any follow-up sequence.
* **Fail-open / fail-safe:** all FunnelKit surfaces are additive. The withdrawal is **always** reachable via the WooCommerce **My Account** area and the **public standalone page** (guest lookup by order number + e-mail) regardless of FunnelKit configuration.

= Evidence, timestamps & integrity =

* The immutable log is **append-only and hash-chained** (HMAC-keyed with your site secret), so tampering is detectable.
* **Optional, opt-in timestamping** (off by default — no external call is made until you enable a provider): free, independently-verifiable **OpenTimestamps** (Bitcoin) anchoring, or a **qualified eIDAS RFC 3161** timestamp (Sectigo, or your national authority — Aruba, InfoCert, D-Trust, Universign, FNMT, SwissSign) for stronger "data certa". Failed stamps retry automatically and any not-yet-anchored records are surfaced in the admin.

= Privacy & GDPR =

* The log commits to an **anonymised IP**; the full IP lives separately and is **erased after a configurable retention** (10 years by default).
* A **Consent records** screen lists and exports the exemption consents (CSV). Two ready-to-paste privacy clauses are generated (withdrawal log + exemption-consent), on a legitimate-interest basis. The uninstaller keeps the evidence log by default (legal hold) unless you opt to erase it.

= Documents & compliance =

* Generates the **Annex I-B model withdrawal form** and ready clauses for **pre-contractual information, Terms & Conditions and Privacy** — and reminds you, clearly, that installing the button is **not enough**: your Terms and pre-contractual withdrawal article must be updated to describe the new button modality (the plugin gives you the exact text to paste).

= Integrations & automation =

* A **read-only REST API** (authenticated with a standard Application Password) to list requests and check an order's withdrawal status, plus an optional **signed webhook** (HMAC-SHA256) fired the moment a withdrawal is confirmed — for Zapier, Make, n8n, a CRM or a helpdesk. Privacy-first: the consumer's IP is never exposed. **33 documented hooks/filters** for developers.
* Plays nicely with **Complianz**, **TranslatePress** and page-cache plugins (WP Rocket / LiteSpeed / W3TC).

= Platforms & licence =

* **WooCommerce (HPOS + legacy)** and **WooCommerce + FunnelKit** (Funnel Builder / Aero Checkout / FunnelKit Automations). The WooCommerce My Account area and public standalone page are always available regardless of FunnelKit configuration.
* **Free and open source** (GPLv3) — no upsell, no tracking, no remote scripts or fonts loaded on your site. Passed a full multi-dimension security audit (0 critical / 0 high).

This plugin is a technical aid to compliance and is **not legal advice**. Have your own counsel review your store's documents.

== Installation ==

1. Upload the plugin to `/wp-content/plugins/` and activate it.
2. Make sure **WooCommerce** is active (required). FunnelKit is optional.
3. Go to **Withdrawal Button → Settings**, enable the function, and choose your applicability mode (EU/EEA only is the default).
4. Publish the generated Annex I-B model form and update your Privacy / Terms / pre-contractual information from the Compliance page.

== Frequently Asked Questions ==

= Who must comply? =
Any trader concluding distance B2C contracts via an online interface with EU/EEA consumers, regardless of the trader's own country (Rome I Art. 6). Switzerland-resident consumers are out of scope (voluntary mode).

= Does it replace the model withdrawal form? =
No. The button is **additional** to the Annex I-B model form, which remains mandatory in pre-contractual information. The plugin generates both.

= Do digital products lose the right of withdrawal automatically? =
No. The right of withdrawal applies by default, **including** to digital products. It is removed only for the two conditional Art. 59 exemptions (digital content with immediate access; a service fully performed) and only when the consumer gives prior express consent + acknowledgement at checkout. The plugin captures that on the WooCommerce checkout and on the FunnelKit Aero checkout (a required tick-box), stores it as evidence, and only then hides the button — otherwise the button stays (fail-safe). **Physical products never need consent.** For the digital exemption the plugin also e-mails the consumer a durable-medium confirmation, as the law requires.

= Do I have to keep a register of these consents? =
The law does not name a "register", but the burden of proof is on you (Art. 6(9) Dir. 2011/83/EU; GDPR accountability Art. 5(2)) — you must be able to prove the consent. The plugin keeps it for you: the agreed wording, a SHA-256 hash, the date/time and (optionally) the IP are stored on the order and anchored in the tamper-evident log; a **Consent records** admin screen lists and exports them. The IP is anonymised automatically after the retention period.

= Is the timestamp legally valid? =
OpenTimestamps provides a free, independently-verifiable Bitcoin-anchored proof. A pluggable RFC 3161 / eIDAS qualified-timestamp provider is available for stronger "data certa".

= Do I need FunnelKit? =
No. FunnelKit is entirely optional. The withdrawal button, two-step flow, durable-medium receipt and immutable log are all fully functional on plain WooCommerce. The FunnelKit integration adds the thank-you page link, the Aero checkout consent checkbox, and the `{{recesso_url}}` merge tag for FunnelKit Automations emails — but the withdrawal is always reachable via My Account and the public standalone page regardless.

== Support & professional services ==

This is a free tool. For personalized support, legal-text review, custom development, or other custom plugins for your WooCommerce / FunnelKit store, Solucom can help: https://solucom.si

== External services ==

**This plugin does not contact any external service unless you explicitly enable one.** Out of the box the timestamp provider is set to "none": the withdrawal flow, the durable-medium receipt and the tamper-evident log all work entirely on your own server, with no outbound request.

The following services are **opt-in** and disabled by default. Each one is enabled by you, in the plugin settings.

= 1. OpenTimestamps (optional timestamp provider) =

**What it is and why:** OpenTimestamps is a free, public timestamping service that anchors a proof in the Bitcoin blockchain. Enabling it (Settings -> Timestamping -> provider "OpenTimestamps") adds independently verifiable proof of *when* a withdrawal was recorded, which strengthens the evidential value of the log.

* **What is sent:** only a SHA-256 hash (a one-way digest) of the log record, plus a random privacy nonce. No personal data, order content, names, e-mail addresses or IP addresses are ever transmitted - only an opaque hash that cannot be reversed.
* **When:** once when a withdrawal is confirmed (to submit the hash), then periodically via WP-Cron to retrieve the completed proof.
* **Where:** the public OpenTimestamps calendars: a.pool.opentimestamps.org, b.pool.opentimestamps.org, a.pool.eternitywall.com, ots.btc.catallaxy.com.
* **Service website:** https://opentimestamps.org/
* **Terms of service:** https://opentimestamps.org/ - OpenTimestamps is a free, open-source public service run by volunteers; it publishes no separate terms-of-service document. Its source code and operating model are documented at https://github.com/opentimestamps
* **Privacy policy:** https://opentimestamps.org/ - the service publishes no separate privacy-policy document. By design it only ever receives an irreversible hash and a random nonce, never personal data.

= 2. RFC 3161 / eIDAS timestamp authority (optional, you choose the provider) =

**What it is and why:** instead of OpenTimestamps you may configure any RFC 3161 timestamp authority (for example an eIDAS-qualified one) to obtain a legally qualified timestamp.

* **What is sent:** the same one-way SHA-256 hash of the log record and a nonce. No personal data.
* **When:** once per confirmed withdrawal.
* **Where:** the timestamp-authority URL **you** enter in the settings. The plugin ships with no endpoint configured and contacts no authority until you set one.
* **Terms and privacy:** those of the authority you choose. The settings screen lists some well-known providers for convenience; the plugin has no affiliation with any of them and contacts none of them until you enter their endpoint. For the two free options mentioned there: Sectigo — terms of use: https://www.sectigo.com/legal, privacy policy: https://www.sectigo.com/privacy-policy; DigiCert — terms of use: https://www.digicert.com/legal-repository, privacy policy: https://www.digicert.com/privacy-policy.

= 3. Outbound webhook (optional, your own endpoint) =

**What it is and why:** lets you forward withdrawal events to your own systems (CRM, Zapier, Make, n8n).

* **What is sent:** a signed (HMAC-SHA256) JSON payload with the withdrawal reference, contract reference and verification hash. The consumer's IP address is never included.
* **When:** only when a withdrawal is confirmed, and only if you enable the webhook.
* **Where:** exclusively the endpoint URL **you** enter in the settings.
* **Terms and privacy:** those of the endpoint you control.

No other external services are used. The plugin loads no remote scripts, fonts, images or trackers on your site, and collects no usage data of any kind.

== Privacy ==

The plugin records withdrawal declarations (name, identified contract, email, IP address, date and time) in an append-only, tamper-evident log on **your own server**, because Art. 54-bis requires this as legal evidence (GDPR Art. 6(1)(c)/(f)). It generates a ready-to-paste privacy clause for your policy. Data is retained for a configurable period (10 years by default), and the uninstaller keeps the evidence log by default (legal hold) unless you opt to erase it.

For the conditional Art. 59 exemptions, the plugin also stores the consumer's checkout consent + acknowledgement (the agreed wording, a hash, the date/time and — unless you turn it off — the IP) as evidence to prove the exemption is valid. The lawful basis is **legitimate interest** (GDPR Art. 6(1)(f); defence of legal claims), **not** GDPR consent. The IP lives only on the order (never in the immutable log) and is automatically anonymised once the retention period lapses. A second ready-to-paste privacy clause is generated for this processing.

== Changelog ==

= 1.0.0 =
* Initial release. EU online right-of-withdrawal button for WooCommerce and FunnelKit (Art. 11a / Art. 54-bis), applicable from 19 June 2026.

== Upgrade Notice ==

= 1.0.0 =
Initial release.
