joseph-leedy / module-custom-fees
joseph-leedy/module-custom-fees
Adds configurable custom fees to orders
Custom Fees for Magento 2
by Joseph Leedy
Custom Fees allows merchants to configure additional fees to be charged to
customers when orders are placed.
Features
- Allows fees to be configured with a label and amount to be added to an order
- Custom fees configuration can be imported via a CSV spreadsheet
- Conditions can be defined to determine whether a custom fee should be
applied to an order based on product and cart details, including
specific product attributes, product SKU, cart subtotal, cart item count,
total cart weight and more - Custom fees can be applied as a fixed amount or a percentage of the order
subtotal
- Custom fees are displayed for orders, invoices and credit memos in both the
frontend and backend - Custom fees can be charged and displayed including or excluding tax
- Custom fees can be refunded via Magento's credit memo functionality
- Discounts can be applied to custom fees using Cart Price Rules
- Includes a report detailing all charged custom fees for a given time period
- Fully compatible with the Hyvä theme (Hyvä Default, Hyvä CSP, Hyvä Checkout
and Hyvä Checkout CSP)
Requirements
- PHP 8.1 or greater
- Magento Open Source 2.4.4 or greater, Adobe Commerce 2.4.4 or greater, or
Mage-OS 1.0 or greater* - MySQL 8.0.4 or greater, MariaDB 10.6.0 or greater, or a MySQL 8-compatible
database server (for generating reports)
Note: Mage-OS compatibility has not been tested, but it
is functionally equivalent to Magento Open Source 2.4.6 or greater.
Installation
This extension can be installed via Composer from Packagist by running
these commands from a terminal on a Web server or in the desired installation
location:
cd /path/to/your/store
composer require joseph-leedy/module-custom-fees
php bin/magento module:enable JosephLeedy_CustomFees
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
Updating
This extension can be updated via Composer from Packagist by running
these commands from a terminal on a Web server or in the desired installation
location:
cd /path/to/your/store
composer update joseph-leedy/module-custom-fees
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static-content:deploy
Usage
Configuration
Custom Fees
Custom Fees can be added from the Magento Admin panel by going to Stores > Settings > Configuration > Sales > Sales > Custom Order Fees. The overall
display order of the Custom Fees block in relation to other totals shown in the
cart and checkout can be configured at Stores > Settings > Configuration > Sales > Sales > Checkout Totals Sort Order. All settings for this extension
can be configured in the Global (Default), Website or Store scope.
Report Aggregation
Report aggregation can be configured at Stores > Settings > Configuration > General > Reports > Custom Order Fees Report.
Taxes
Taxes can be configured at Stores > Settings > Configuration > Sales > Tax.
Available Settings
| Setting | Section | Description | Default |
|---|---|---|---|
| Tax Class for Custom Fees | Tax Classes | Sets the class used to determine fee tax amount | None |
| Custom Fees | Calculation Settings | Sets whether the custom fees entered include or exclude tax | Excluding Tax |
| Display Custom Fee Amount | Shopping Cart Display Settings | Controls how custom fees are shown in the Cart and Checkout | Excluding Tax |
| Display Custom Fee Amount | Orders, Invoices, Credit Memos Display Settings | Controls how custom fees are shown on the Sales pages | Excluding Tax |
Importing Custom Fees
The Custom Fees configuration page allows fees to imported from a CSV
spreadsheet containing the following columns:
| Column | Description | Example |
|---|---|---|
| code | A unique code identifying the custom fee (allowed characters: "A-Z", "a-z", "0-9", "_") | example_fee |
| title | A short label describing the custom fee | "Example Fee" |
| type | The type of custom fee ("fixed" or "percent" [of order subtotal]) | "fixed" |
| status | Whether the custom fee is enabled or diasbled ("0", "disabled", "1", "enabled") | "enabled" |
| show_percentage | Optional - Whether or not to show the percentage next to the fee name ("0", "n", "no", "1", "y", "yes") | "yes" |
| value | The amount of the custom fee, in the store's default currency | 5.00 |
Example CSV Import Spreadsheet
code,title,type,status,show_percentage,value
example_fee,"Example Fee",fixed,enabled,0,5.00
Configuring Adavanced Settings
Additional settings for a Custom Fee, including Conditions, can be found in the
Advanced Settings dialog by clicking on the cog icon "⚙️."
Reporting
To view a report of the collected custom order fees, go to Reports > Sales > Custom Order Fees in the Magento Admin panel.
Note: For performance reasons, the report generation process makes use of
special database functions that are only available in MySQL 8.0.4+ or
MariaDB 10.6.0+. Errors or unexpected behavior may occur when using incompatible
database server software versions.
Applying Discounts to Custom Fees
To apply a discount to a custom fee, go to Marketing > Promotions > Cart Price Rules in the Magento Admin panel and follow these instructions:
- Click on "Add New Rule" or select an existing rule
- Expand the "Actions" section and click on the "Apply to Custom Fee Amount"
toggle
Note: To apply a discount to a specific custom fee, add an action
condition by following these instructions:
- In the "Actions" section, under the "Apply the rule only to cart items
matching the following conditions..." heading, click on the green plus icon - Select "Custom Fee" from the condition dropdown
- Click on the ellipses button and choose the desired custom fee from the list
Support
If you experience any issues or errors while using this extension, please
open an issue in the GitHub repository. Be sure to include all relevant
information, including a description of the issue or error, what you were doing
when it occurred, what versions of Magento Open Source or Adobe Commerce and PHP
are installed and any other pertinent details. I will do my best to respond to
your request in a timely manner.
License
The source code contained in this extension is licensed under the Open Software
License version 3.0 (OSL-3.0) license. A copy of this license can be found in
the LICENSE file included with the source code or online at
https://opensource.org/licenses/OSL-3.0.
Copyright for the included source code is exclusively held by Joseph Leedy,
all rights reserved.
History
A full history of the extension can be found in the CHANGELOG.md file.
Contributing
We welcome and value your contribution. For more details on how you can help us
improve and maintain this tool, please see the CONTRIBUTING.md file.
Shout-Outs
Contributors
These wonderful people have helped this extension become what it is today, and
I am truly grateful to them for their contributions:
- A huge thanks to @pykettk, @Vinai and @hostep for reviewing the Hyvä
compatibility implementation in pull request #16 and suggesting improvements!️
Supporters
I owe the following fine folks a debt of gratitude for their continuing
support:
Changelog for Custom Fees for Magento 2 by Joseph Leedy
All notable changes to this extension will be documented in this file.
The format is based on Keep a Changelog, and this extension adheres to
Semantic Versioning.
For more information about this extension, please refer to the README
document.
Unreleased
1.5.0
Added
- A product attribute that can be used to determine whether or not a custom fee
should be applied to an order is now created automatically when the extension
is installed - Data for the Custom Order Fees Report can now be aggregated automatically on
a chosen schedule - Custom fees can now be charged with tax and displayed inclusive or exclusive
of tax - Cart Price Rules can now be used to apply discounts to custom fees for
orders, invoices and credit memos
Changed
- Ordered custom fees are now indexed by their corresponding fee code
- Data for ordered, invoiced and refunded custom fees is now represented by
models with corresponding interfaces instead of arrays - Usages of the
mixed[]type for extension attributes have been replaced with
the new interfaces for ordered, invoiced and refunded custom fees - All total models, blocks and other logic now use the new interfaces and models
Removed
- The data patch used to add missing fields to the existing custom order fees
data has been removed - The plug-in used to temporarily store the refunded custom fee amounts entered
in the Admin panel during credit memo creation has been removed
1.4.0
- This version was skipped due to an issue with creating the tag for it
1.3.2
Fixed
- Missing return type annotations have been added to the Custom Order Fees
Interface - Incorrect sales orders were included in packing slips printed from the
Magento Admin panel (caused by the plug-in used to join custom order fees
with sales orders in the Admin Sales Order Grid)
1.3.1
Changed
- Totals in the Custom Order Fees Report are now sorted by order currency
Fixed
- Custom fee totals were rendered for the related order instead of the
invoice or credit memo on sales PDF files printed from the Magento Admin panel
1.3.0
Added
- Custom fees can be enabled or disabled
- Custom fees can be refunded from credit memos without requiring an adjustment
fee (the previous work-around was to create an adjustment fee of the same
amount as the custom fee)
Changed
- Renamed the
custom_feescolumn in thecustom_order_feestable to clarify
its purpose (new name:custom_fees_ordered) - The
custom_order_feestable now includes a column called
custom_fees_refundedto track the total amount of custom fees refunded for
each credit memo related to the order - The Custom Order Fees Report now includes a column for the total amount of
custom fees refunded for each order, aggregated from the new
custom_fees_refundedcolumn in thecustom_order_feestable - The
custom_order_feestable now includes a column called
custom_fees_invoicedto track the total amount of custom fees invoiced for
each invoice related to the order
Deprecated
- The method
retrieve()in theCustomFeesRetrieverservice has been
deprecated in favor of theretrieveOrderedCustomFees()method
1.2.3
Fixed
- An exception was thrown when loading the Sales Order Grid in the Admin panel
if the JSON data in thecustom_order_feestable was quoted
1.2.2
Fixed
- The Custom Order Fees Report did not aggregate custom fees if the custom
order fee data was quoted in the database - The Custom Order Fees Report did not aggregate custom fees if the related
orders had multiple invoices
Changed
- Regenerated the database schema allowlist to add missing constraints for the
custom_order_feestable
1.2.1
Fixed
- Custom fees were not calculated correctly when calculating totals for
orders with multiple invoices - Custom fees were not calculated correctly when calculating totals for
orders with multiple credit memos
1.2.0
Added
- Configuration for custom fees can be imported from a CSV spreadsheet
- Conditions can be defined in the extension configuration to determine whether
or not to apply a custom fee to an order - Custom Fees can be calculated as a percentage of an order's subtotal
1.1.1
Fixed
- Softened dependency on Zend Framework 1 Database component to fix
incompatibility with Magento 2.4.4 and 2.4.5
1.1.0
Added
- Custom fees are now rendered in columns in the Sales Order Grid in the Admin
panel - A report summarizing the total amount of collected custom order fees can be
generated from the Admin panel - Custom fees are now rendered on the Cart page in the Hyvä frontend
- Custom fees are now rendered on the Checkout pages in the Hyvä frontend
Fixed
- Reorded custom fees totals to be placed after tax totals on customer and
guest order, invoice and credit memo pages in Hyvä frontend
1.0.2
Fixed
- Mark constructor parameters as explicitly nullable in custom order fees model
to fix deprecation errors thrown by PHP 8.4
Changed
- Moved the Custom Fees configuration field to be placed after the Tax
field in the Totals Sort Order group
1.0.1
Fixed
- Narrowed type for
custom_feesCart extension attribute to fix Swagger error - Added and updated method annotations in Custom Order Fees Interface to fix
Swagger errors
1.0.0
Added
- Initial version of extension
| Version | Stability | QA Status | Compatibility | Released |
|---|---|---|---|---|
| 1.5.0 | stable | Fail | Magento 2.4.7-2.4.9 Details | 2026-04-05 21:45:38 |
| 1.3.2 | stable | Not tested | Not yet tested Details | 2025-11-26 17:30:12 |
| 1.3.1 | stable | Not tested | Not yet tested Details | 2025-11-14 20:20:23 |
| 1.3.0 | stable | Not tested | Not yet tested Details | 2025-10-01 21:45:16 |
| 1.2.3 | stable | Not tested | Not yet tested Details | 2025-09-16 16:00:13 |
| 1.2.2 | stable | Not tested | Not yet tested Details | 2025-09-10 17:36:10 |
| 1.2.1 | stable | Not tested | Not yet tested Details | 2025-08-07 17:56:46 |
| 1.2.0 | stable | Not tested | Not yet tested Details | 2025-07-01 23:05:18 |
| 1.2.0-beta.1 | beta | Not tested | Not yet tested Details | 2025-06-27 20:19:44 |
| 1.1.1 | stable | Not tested | Not yet tested Details | 2025-05-08 16:21:24 |
| 1.1.0 | stable | Not tested | Not yet tested Details | 2025-05-05 15:01:40 |
| 1.0.2 | stable | Not tested | Not yet tested Details | 2025-04-10 22:51:47 |
| 1.0.1 | stable | Not tested | Not yet tested Details | 2025-04-08 22:18:48 |
| 1.0.0 | stable | Not tested | Not yet tested Details | 2025-02-05 00:18:02 |
Requires 25
| Package | Constraint |
|---|---|
| php | ^8.1 |
| ext-json | * |
| ext-pcre | * |
| magento/framework | ~103.0.4 |
| magento/module-backend | ~102.0.4 |
| magento/module-catalog | ~104.0.4 |
| magento/module-checkout | ~100.4.4 |
| magento/module-config | ~101.2.4 |
| magento/module-cron | ~100.4.4 |
| magento/module-directory | ~100.4.4 |
| magento/module-eav | ~102.1.4 |
| magento/module-quote | ~101.2.4 |
| magento/module-media-storage | ~100.4.3 |
| magento/module-reports | ~100.4.4 |
| magento/module-rule | ~100.4.3 |
| magento/module-sales | ~103.0.4 |
| magento/module-sales-rule | ~101.2.4 |
| magento/module-shipping | ~100.4.4 |
| magento/module-store | ~101.1.4 |
| magento/module-tax | ~100.4.4 |
| magento/module-ui | ~101.2.4 |
| psr/log | >= 1.0 |
| symfony/polyfill-php83 | ^1.27 |
| symfony/polyfill-php84 | ^1.30 |
| symfony/polyfill-php85 | ^1.33 |
Requires-dev 11
| Package | Constraint |
|---|---|
| colinodell/psr-testlogger | ^v1.3 |
| dms/phpunit-arraysubset-asserts | ^0.5.0 |
| squizlabs/php_codesniffer | ^3.10 |
| dealerdirect/phpcodesniffer-composer-installer | ^1.0 |
| magento/magento-coding-standard | * |
| phpstan/phpstan | ^1.12 || ^2.1 |
| phpstan/extension-installer | ^1.4 |
| bitexpert/phpstan-magento | ^v0.32 || ^v0.40 |
| phpunit/phpunit | ^9.6 |
| php-parallel-lint/php-parallel-lint | ^1.4 |
| friendsofphp/php-cs-fixer | ^v3.75 |
Suggests 2
| Package | Reason |
|---|---|
| magento/zendframework1 | Version 1.15 is used to interact with the database in Magento 2.4.4 and 2.4.5 |
| magento/zend-db | Version 1.16 is used to interact with the database in Magento 2.4.6+ |
Compatibility
Each Magento release line is installed on its supported PHP versions, then the module is built (DI compilation + static-content deploy) and its unit and integration suites are run. The matrix shows the lines and PHP versions the module is confirmed to install and run on. Code-quality results further down (phpstan, phpcs, …) are reported separately and never affect compatibility.
Code Quality
Advisory checks against the module's source. Static analysis runs once across the whole module; PHPStan re-runs per Magento + PHP version because resolvable symbols differ between releases. These NEVER affect the Compatibility badge — a phpcs finding can't make a module incompatible.
Static analysis
Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.
| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Fail | 132 | 51 errors, 81 warnings (ruleset: Magento2) — 60 auto-fixable with phpcbf |
| PHPMD | Warning | 32 | 32 rule violations (UnusedFormalParameter:14, ExcessiveMethodLength:8, NPathComplexity:4, CyclomaticComplexity:3, ExcessiveParameterList:1) |
| Cpd | Warning | 28 | 28 duplicated chunks spanning 1235 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 2 | valid; 2 advisory notes (composer validate --strict) |
PHPStan
Type-checks the module's PHP against a real Magento install at the configured gate level. Re-runs per Magento and PHP version because resolvable symbols differ between releases. Cell → details modal.
Tests
Unit and integration suites, run for each applicable Magento and PHP version. A test failure speaks to the module's behaviour, not its compatibility with a Magento line, so it is reported here separately and never reddens the compatibility matrix.
Unit tests
| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | ||
| 2.4.8 | N/A | N/A | ||
| 2.4.9 | N/A | N/A |
Integration tests
| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | ||
| 2.4.8 | N/A | N/A | ||
| 2.4.9 | N/A | N/A |
Security
Security checks run directly against the module: an audit of its declared dependencies for known vulnerabilities (composer audit) and a scan of its source for malware and web-shell signatures. Each runs once. A malware detection fails the version outright.
Turn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.