Withdrawal ButtonDocs

Withdrawal Button Documentation

Installation steps and a complete reference for every field and function in the admin, with concrete examples — the same level of detail our support uses to help customers.

Installation

Requirements

Magento 2.4.x — tested on 2.4.9, compatible with previous 2.4.* releases. PHP 8.1–8.5. Compatible with both the default Luma theme and the Hyvä theme. Requires the native Magento_ReCaptcha* modules to be present for the optional reCAPTCHA protection (already bundled with Magento core).

Setup steps

  1. 1. Add the credentials you'll receive by email to auth.json in your Magento project root:
    { "http-basic": { "repo.codingrow.com": { "username": "...", "password": "..." } } }
  2. 2. composer config repositories.codingrow composer https://repo.codingrow.com
  3. 3. composer require codingrow/module-withdrawal-button
  4. 4. bin/magento module:enable Codingrow_WithdrawalButton
  5. 5. bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush
  6. 6. Paste your license key into Admin → Stores → Configuration → Codingrow Extensions → Withdrawal Button → License → License Key, save, then bin/magento cache:flush.
  7. 7. Place the request link somewhere customers will find it — see Placing the widget below.

The whole frontend form, emails and admin interface are available in 7 languages, auto-selected per store/admin locale:

English Italiano Español Français Deutsch Português Nederlands

Uninstallation

composer remove codingrow/module-withdrawal-button then bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush. Requests already recorded in codingrow_withdrawalbutton_request are never deleted automatically — export the grid first if you need to keep a record.

User guide

The withdrawal request page

One page, two steps, exactly as required by the underlying directive: the customer first fills in the fixed fields (name, email, order number, receipt date) plus any enabled custom fields, sees a read-only summary of everything entered, and only then confirms on a final, separate button. The hidden fields carrying the data forward between the two steps are re-validated server-side on confirmation — a customer can never bypass the checks by forcing the "confirmed" step directly.

Order verification

Before accepting a request, the module checks that the order number actually exists and that the email address matches the one on that order (case-insensitive, works for both guest and registered-customer orders). If either check fails, the same generic "not found" message is shown either way — this is deliberate: revealing "wrong email" versus "order doesn't exist" would let someone probe for valid order numbers by trial and error.

Duplicate-request prevention

One order can have only one active withdrawal request, regardless of its status — even a "Rejected" one. A customer who disagrees with a rejection can't simply resubmit the same request to route around it; if a genuine exception is needed, the admin deletes the previous request from the grid first.

Receipt date & elapsed days

The 14-day withdrawal period starts from when the goods were received, not from the purchase or invoice date — information Magento has no way of knowing on its own, since it depends on the carrier. The form asks for it with a native HTML5 date picker (no jQuery UI dependency that could conflict with a theme), capped so a future date can't be entered. The order date itself is read automatically from the linked order — the customer never has to type it. Both dates, plus the days elapsed since receipt, are shown in the admin grid (flagged once past 14 days) and available as email variables.

Custom fields builder

Beyond the four fields required by law (name, email, order number, receipt date — always mandatory, never removable), you can add an unlimited number of your own fields from Codingrow Extensions → Withdrawal Button → Form Fields: label, type, whether it's required, whether it's currently shown on the form, and its display order relative to the other fields.

TypeRendered as
TextSingle-line input
TextareaMulti-line box
DropdownA <select> with the options you typed, one per line
CheckboxA single checkbox (e.g. "I still have the original packaging")
Example — a required Dropdown field "Reason for return" with options "Defective / Doesn't match description / Changed my mind": customers must pick one before they can submit, and the chosen value shows up in the admin grid's "Custom Fields" column and in both notification emails.

Values submitted for a field that gets disabled later are kept in the request's history and still shown wherever they were recorded — only the field's label is re-resolved by ID, so renaming a field afterward updates the label everywhere its old answers are displayed too.

"Items to return" field

A dedicated optional field (disabled by default, since it isn't part of the legal minimum) for a free-text description of what's being returned — toggle it on, and independently choose whether it's mandatory once shown, from Configuration → General.

Button colors & text

Two hex color fields (text and background) let you match the button to your brand — only the colors change; shape, padding and font always stay whatever your theme (Luma or Hyvä) already uses, so the button can never end up visually broken. An invalid hex value is simply ignored, falling back to the theme default. The button's text is also configurable, shown exactly as typed (case preserved, no automatic capitalization) — leave it empty to keep the default translated label.

reCAPTCHA protection

Withdrawal Button registers itself as a protectable form in Magento's own native reCAPTCHA system — Stores → Configuration → Customers → Google reCAPTCHA → Storefront → "Enable for Withdrawal Button". Whichever version you've already configured for the rest of the store (v2 checkbox, v2 invisible, or v3) applies automatically; there is no separate captcha configuration to maintain.

Admin grid & mass actions

Every request lands in Codingrow Extensions → Withdrawal Button, paginated and filterable, with columns for order number, customer, receipt date, days elapsed (flagged past 14), custom field values, and status. Mass actions let you update the status of several selected requests at once, or delete rows to clean up test data.

StatusMeaning
PendingJust submitted, not yet reviewed.
ProcessingBeing handled by the store.
CompleteReturn/refund finished.
RejectedThe store declined the request.
CancelledThe customer withdrew their own request.

Email templates & variables

Two native Magento email templates — a customer receipt (the durable-medium confirmation required by law) and an internal admin notification — sent automatically via TransportBuilder, exactly like Magento's own order emails. Clone either one from Marketing → Email Templates, edit the wording with the native WYSIWYG editor, then select your copy in Configuration → Email Templates — no custom templating code involved.

VariableContent
order_increment_idThe order number
order_dateThe order's own date, read automatically
receipt_dateThe date the customer declared receiving the goods
days_since_receiptDays elapsed since that date
request_idInternal reference number for this request
submitted_atExact date and time the request was confirmed
items_descriptionThe optional free-text field, if enabled
custom_fields_textAll filled-in custom fields, formatted as "Label: value" lines

PDF return label

An optional simple slip (logo, sender name, return address, packaging instructions) generated in-house with Zend_Pdf — the same PDF library Magento's own core uses for invoices and shipments, no third-party dependency added — and attached automatically to the customer's receipt email once enabled in Configuration → Return Label. Upload a logo, fill in the return address and any packaging instructions, and every future request's confirmation email carries a ready-to-print label with that request's own reference number, order number, customer name and receipt date.

Placing the widget

The request link is never hardcoded into the theme — it's placed exclusively via Magento's native Widget system, so a merchant fully controls whether, and where, it appears. Two ways to do it, depending on how broadly you want it shown:

MethodBest for
Content → Elements → WidgetsShowing the link on many/all pages at once, or in a fixed layout position (footer, sidebar) across the whole site.
Insert Widget inside a CMS page's contentPlacing it on one specific page only — e.g. the homepage — exactly where you want it in that page's own content, without affecting any other page.

Site-wide, via Content → Elements → Widgets:

  1. 1. Content → Elements → Widgets → Add Widget, choose the Withdrawal Button link widget.
  2. 2. Assign it to "All Pages" or "Specified Page(s)" and set the display container (e.g. content or sidebar.additional) — on Hyvä themes, the "Footer" container may not be exposed to the widget picker; in that case use the main content area instead, right before the footer.
  3. 3. Save and reload the target page(s) — no cache flush required for a newly saved widget instance.

On one specific page only, e.g. the homepage:

  1. 1. Content → Pages, open the page (e.g. "Home page").
  2. 2. In the Content tab's WYSIWYG editor, place the cursor where you want the button and click Insert Widget.
  3. 3. Choose the Withdrawal Button link widget, set its label text and CSS class, then Insert — this drops a {{widget type="Codingrow\WithdrawalButton\Block\Widget\Link" ...}} directive directly into that page's content, affecting only this page.
  4. 4. Save the page.
Note: the widget is the only supported way to expose the link — the module intentionally never injects it automatically into the footer or any other template, since a merchant may legitimately want it somewhere else entirely (an orders page, a dedicated CMS page, a specific product category) or not shown site-wide at all.

License

Without a valid license key, the withdrawal page stays visible (a merchant should never be left without the legally-required button just because a license lapsed) but new submissions are blocked until a key is entered in Configuration → License → License Key.