Skip to content
MAGENTO EXTENSIONS

Magento 2 Checkout Fees Done Right: Tax, Invoices, Credit Memos, Refunds

A checkout fee is the easy part. Here is how Magento 2 fees should behave through tax, partial invoices, credit memos, and refunds, and where DIY breaks.

Shane Blandford
Shane Blandford · FOUNDERPUBLISHED · 8 MIN READ

The demo of any fee implementation looks the same: add item to cart, see the fee line, place the order, applause. The failures never happen in the demo. They happen three weeks later, when accounting invoices half the order and the fee is on neither half, or a second credit memo refunds the fee a second time and the books are off by exactly one fee per unlucky order.

We have cleaned this up on client stores enough times to have opinions about it. We also sell the OCM Labs Extra Fees module, whose lifecycle rules are the subject of half this post, so you know where we stand. Whether you buy ours, buy someone else's, or build your own, this is the checklist a fee has to pass after checkout.

Why do checkout fees break after the order is placed?

Because in Magento 2, the quote, the order, the invoice, and the credit memo each have their own totals, and nothing moves between them for free. A quote totals collector gets your fee into the grand total at checkout, but invoices and credit memos run separate collectors, emails and PDFs render from their own blocks, and refund tracking does not exist unless someone builds it. Most fee implementations stop at the quote, which is why the fee is correct at checkout and wrong everywhere afterward.

If you want to see exactly how much is involved in the quote stage alone, we published the working collector code and its limitations in custom code vs extension for Magento 2 fees. This post is about everything after that.

How should a Magento 2 fee be taxed?

By Magento's tax engine, not by fee code. Our module does not calculate a cent of tax itself: taxable fees are registered with core as associated taxables, and core computes the tax with the same rules, rates, and rounding it applies to products and shipping. That is what keeps fee tax correct across tax zones, customer groups, and Cross Border Trade without maintaining a parallel tax implementation, which is a thing nobody should do on purpose.

The merchant-facing controls are small: a per-fee tax class for the odd fee taxed differently, a global default class for the rest, and an Is Taxable flag. If neither resolves to a real tax class, the fee is simply not taxed, by design rather than by error. Fee tax is added to the order's existing Tax total line; no separate per-fee tax line appears anywhere, which matches how shipping tax behaves and keeps totals blocks sane.

Stores that price tax-inclusive, which is most stores outside the US, can flip one setting so entered fee amounts are treated as already including tax, and the engine derives the exclusive amount used on totals and documents. Min/max caps on percentage fees then apply to the tax-inclusive figure, so a fee capped at $50 never displays more than $50 including tax. The full decision table lives in the tax and currency docs.

If you are building this yourself, the associated-taxables route is the correct one, and it is genuinely fiddly: our implementation splits into two collectors on purpose, one before core tax that registers the taxables and one after it that reads back what core computed. Budget for that or budget for wrong tax.

What happens to a fee on a partial invoice?

This is the question most fee implementations have never been asked, and it shows. A fee is an order-level charge; it does not belong to any line item, so it cannot be split proportionally across partial invoices without inventing an allocation rule that accounting then has to reverse-engineer.

Our rule is deliberately blunt: the first invoice created while any fee remains uninvoiced captures the full fee and its tax. Later partial invoices carry no fee. Tracker columns on the order record what has been invoiced, and each invoice stores the amount it carries, so the numbers are auditable per document rather than reconstructed from guesswork.

Blunt beats clever here. An all-or-nothing rule is explainable to a bookkeeper in one sentence, produces the same result no matter how the warehouse splits shipments, and never strands a rounding remainder across three invoices.

How do fee refunds work on credit memos?

Mirrored, with one extra guarantee: the first credit memo created after the fee was invoiced refunds the full invoiced fee, later credit memos carry none, and the refundable amount is always computed as invoiced minus already refunded. A fee that was never invoiced is never auto-refunded, and no sequence of credit memos can refund more fee than was invoiced. That double-refund case is not hypothetical; untracked fee refunds across multiple credit memos are the single most common fee bug we have found in code we inherited.

For the cases the rule does not cover, a partial goodwill refund of a fee, or legacy orders placed before tracking existed, the answer is core's own Adjustment Refund field on the credit memo. Same tool merchants already use for any out-of-band amount, no special casing. The complete rules are in the order lifecycle docs.

What about multi-currency orders?

Every fee amount is stored in both the display and base currency, on the quote, the order, and each invoice and credit memo, so base-currency reporting stays exact regardless of what the shopper paid in.

Fixed fees are configured in base currency and converted to the display currency at the current rate. Percentage fees do something more careful: they are computed independently in each currency from that currency's own pre-discount, tax-exclusive row totals, never computed once and converted, so the fee stays consistent with the item prices the shopper actually saw. Caps and the cart subtotal condition always operate in base currency.

Every surface a fee has to reach

The demo shows cart and checkout. Production adds this list, and shoppers notice every gap:

  • Order, invoice, and credit memo emails. Ours injects into the standard totals blocks, so heavily customized templates only need those blocks present.
  • PDF invoices and credit memos, including the bulk PDF grid actions, with fee tax folded into the PDF's Tax line.
  • Customer account order views, guest order lookup, and all the print pages.
  • Admin order, invoice, and credit memo views, plus admin order creation, where fees evaluate live as staff build a quote, since backend quotes run the same collectors.
  • The admin orders grid, which gets a dedicated Extra Fees column that stays accurate under both synchronous grid refresh and the async cron indexer.
  • REST and GraphQL, where fee data rides on order extension attributes for whatever your ERP or OMS pulls.

One deliberate refusal: multishipping. On a multi-address checkout, fees are skipped entirely and a warning is logged, because no allocation of one order-level fee across multiple addresses survives contact with accounting. We would rather charge nothing than guess.

The takeaway for merchants and the one for developers

If you are a merchant evaluating any fee extension, ask the vendor three questions. Which invoice carries the fee when the order is invoiced in two parts? What stops the fee from being refunded twice? Who calculates the tax? Our answers are above and in the docs; a vendor without answers is selling you the demo.

If you are a developer building fees in-house, none of this is impossible, it is just larger than it looks: invoice and credit memo collectors, tracker columns, taxable registration, email and PDF wiring, and the multi-currency math, all maintained through every Magento upgrade. That is the work you are pricing against a $250 license, sold and updated as described in how OCM Labs extensions install and update. And if you have an existing fee implementation misbehaving in exactly the ways this post describes, that is a thing our Magento support team untangles regularly. Our other modules are on the Magento extensions page.

FAQ

Why does my Magento 2 fee show at checkout but not on the invoice?+
Because quote totals and invoice totals are separate systems. A custom quote collector affects checkout only; invoices and credit memos need their own totals collectors plus tracking columns to know how much fee has been captured. The OCM Labs Extra Fees module ships all of that: the fee persists on the order and renders on invoices, credit memos, emails, and PDFs.
How is tax calculated on an extra fee in Magento 2?+
The correct approach is to register the fee with Magento's tax engine as an associated taxable so core computes the tax under the store's normal rules and rates. The OCM Labs Extra Fees module works this way, with a per-fee tax class or a global default, and adds the fee tax to the order's existing Tax line rather than creating a separate tax row.
What happens to a fee when an order is partially invoiced?+
In the OCM Labs Extra Fees module, the first invoice created while any fee remains uninvoiced captures the full fee and its tax; later partial invoices carry no fee. Fees are order-level charges, so all-or-nothing capture keeps every document auditable instead of splitting the fee by an arbitrary allocation.
Can a fee be refunded twice across multiple credit memos?+
Not with the module: the refundable fee is always computed as invoiced minus already refunded, so the first credit memo after invoicing refunds the fee and later ones carry none. In custom implementations without tracking, double refunds across credit memos are a real and common bug.
How do I refund only part of an extra fee?+
Use the core Adjustment Refund field on the credit memo. The module's automatic handling is all-or-nothing by design, and Adjustment Refund is the standard Magento tool for out-of-band amounts, so partial fee refunds go through the same field merchants already know.
Do extra fees work with multiple currencies?+
Yes. Fixed fees convert from base to display currency at the current rate, percentage fees are computed per currency from that currency's own item row totals, and every order document stores both currencies, so base-currency reporting stays exact whatever the shopper paid in.
Shane Blandford

Shane Blandford

FOUNDER

Founder of Orange Collar Media, a Denver ecommerce agency behind 800+ Magento and Shopify builds. In Magento since version 1.x - building, rescuing, and supporting revenue-critical stores since 2008.

All posts by Shane Blandford

KEEP READING

Related from the Journal.

Your store can sell more. Let's find out how much.

Start a project