Orange Collar Media
Orange Collar Media
DOCS
OCMLABS/MODULE-EXTRA-FEES · ORDER LIFECYCLE

Where fees display, and how they invoice and refund exactly once

MAGENTO 2.4.7 - 2.4.8 PHP 8.3+ LUMA + HYVÄ COMPOSER

A fee that renders at checkout but vanishes from the invoice email erodes trust fast. This module carries fee lines through every surface an order touches, with no template edits or configuration:

  • Cart and checkout totals - one labeled line per triggered fee (standard Luma/Knockout checkout; Hyva cart via the companion module).
  • Customer account and guest order views - order, invoice, and credit memo pages and their print views, for logged-in customers and guest order lookups alike.
  • Emails - order, invoice, and credit memo confirmation emails show the same rows as the storefront, injected into the standard totals blocks. Heavily customized templates only need the standard order_totals / invoice_totals / creditmemo_totals block present.
  • PDF invoices and credit memos - an “Extra Fees” row on the standard PDFs, from the document view or the bulk PDF grid actions, with fee tax folded into the PDF’s own Tax line.
  • Admin - the order, invoice, and credit memo detail views, and order creation: fees are evaluated live while an admin builds an order in Sales > Orders > Create New Order, since the same quote collectors run for backend quotes.
  • Admin orders grid - a dedicated Extra Fees column in Sales > Orders, kept accurate under both synchronous grid refresh and the async (cron) grid indexer.

On order documents, the order view lists one row per fee from the order’s stored detail; invoice and credit memo views show the single aggregate amount that specific document carries, which follows from the lifecycle below.

What persists on the order

At order placement the quote’s fee data is copied onto the order: total fee amount and fee tax in both display and base currency, plus a JSON detail array with each fee’s code, label (as resolved for that store at purchase time), amount, and tax. Separate tracker columns record how much fee and fee tax has been invoiced and refunded so far. All of it is exposed as order extension attributes for API consumers.

Invoice once

Fees are order-level charges and cannot be split across partial invoices, so capture is all-or-nothing: the first invoice created while any fee amount remains uninvoiced takes the full remaining fee and its tax; later partial invoices carry no fee. The invoiced trackers on the order record what was taken, and each invoice stores the amount it carries.

Orders placed before this tracking existed have empty trackers, so their fee is captured in full on the order’s next invoice.

Refund once, never more than was invoiced

Credit memos mirror the invoice rule: the first credit memo created after the fee was invoiced refunds the full invoiced fee; later partial credit memos carry no fee. The refundable amount is always computed as invoiced minus already refunded - a fee that was never invoiced is never auto-refunded, and the module never refunds more fee than was invoiced, regardless of how many credit memos an order accumulates.

For partial fee refunds, or for legacy orders with no invoiced tracking, use the core Adjustment Refund field on the credit memo - the same tool merchants already use for any out-of-band refund amount.

Multishipping is not supported

On a multi-address (multishipping) checkout, fees are skipped entirely and a warning is logged. No fee line appears anywhere for those orders - a deliberate refusal rather than a guess at how one fee should split across addresses.