storetown-media / module-surcharge-max
Magento 2 surcharge module with category- and product-level rules, named reusable rules with per-product binding and bulk-assign from the catalog grid, tier and customer-group pricing, conditional caps (min/max/free-threshold) and country-based shipping restriction.
STM Surcharge Max for Magento 2
Rule-based surcharges for Adobe Commerce and Magento Open Source. Adds a
configurable surcharge to the cart and order totals — per category, per product,
per customer group or by quantity tier — with caps, free-shipping thresholds and
country restrictions.
Built and maintained by Storetown Media, an
e-commerce agency from Tornesch near Hamburg, and used in live customer projects.
Features
- Named, reusable rules. Maintain surcharge rules under Catalog → STM
Surcharge Rules (name, cost, calculation mode, active flag, sort order) and
bind them to products. - Bulk assignment from the product grid. Select products — including
select-all across pages — and assign a rule in one pass via the
Assign STM Surcharge Rule mass action. - Per-product override. A product-level amount always wins over the rule.
- Category surcharges. Apply a surcharge to one or more category IDs.
- Tier pricing. Different surcharge amounts by quantity.
- Customer group pricing. Different amounts per customer group.
- Conditions and limits. Minimum amount, maximum cap, free above a cart
subtotal, and restriction by shipping country. - Storefront notice. Optional custom notice on the product page.
- Multi-language. German and English translations included.
Resolution order
For every quote item the surcharge is resolved in this order:
stm_surcharge_amount— per-product override- Rule cost — from the bound rule
- Customer group price
- Tier price
- Section default
Products bound to a rule are eligible even without a category assignment.
Requirements
- PHP 8.1 or newer
- Magento Open Source / Adobe Commerce 2.4.x (
magento/framework>= 103.0)
Installation
composer require storetown-media/module-surcharge-max
bin/magento module:enable STM_SurchargeMax
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
Configuration
Stores → Configuration → STM → Category Surcharge
| Section | What it does |
|---|---|
| General Settings | Switch the module on or off |
| Surcharge Configuration | Category IDs, cost, label, calculation mode |
| Tier Pricing | Amounts by quantity |
| Customer Group Pricing | Amounts per customer group |
| Conditions & Limits | Minimum, cap, free-above threshold, country restriction |
Access is controlled by the ACL resource STM_SurchargeMax::rules with the
sub-permissions rules_save, rules_delete and rules_assign.
Support
- E-mail: [email protected]
- Issues: https://github.com/storetown-media/module-surcharge-max/issues
- Website: https://www.storetown-media.de
License
Proprietary — see LICENSE.txt. Changes are documented in
CHANGELOG.md.
Changelog
Alle nennenswerten Änderungen an diesem Modul werden in dieser Datei dokumentiert.
Das Format basiert auf Keep a Changelog
und das Projekt folgt Semantic Versioning.
[2.4.0] - 2026-04-26
Multi-Rule-Engine. Admin kann benannte Surcharge-Regeln pflegen und mehreren
Produkten per Bulk-Aktion aus dem Produkt-Grid zuordnen. Lean-Scope (Name,
Kosten, Berechnungsmodus, Aktiv-Flag, Sortierung); Conditions/Date-Range
sind für eine spätere Minor reserviert.
Hinzugefügt
- Neue DB-Tabelle
stm_surchargemax_rule(rule_id,name,cost,
calculation_mode,is_active,sort_order,created_at,updated_at).
Index(is_active, sort_order)für die Resolver-Lookups. - Neues Produkt-Attribut
stm_surcharge_rule_id(int, Scope=Store,
Attribut-GruppeSTM Surcharge Max). Quelle-Klasse
STM\SurchargeMax\Model\Config\Source\Rulelistet aktive Regeln nach
sort_order/name. Setup-PatchAddRuleAttribute. - Resolver-Erweiterung in
Model/Total/Quote/Surcharge.phpund
ViewModel/Product/ShippingNotice.php. Neue Priority-Chain pro Item:
stm_surcharge_amount(per-Produkt-Override) > Rule-Kosten > Customer-Group >
Staffel > Section-Default. Rule-bound Produkte sind eligible auch ohne
stm_surcharge_enabledund ohne Kategorie-Zuordnung. Per-Request-Memoization
des Rule-Lookups. - Adminhtml-CRUD für Regeln: Menüpunkt unter
Catalog → STM Surcharge Rules, eigener Route-FrontNamestm_surchargemax. Listing-Grid mit
Filter/Sort/Bookmarks und Massenaktionen Delete / Enable / Disable;
Edit-Formular mit Validation. - Mass-Action im Produkt-Grid: "Assign STM Surcharge Rule". Selektion
wird viaMagento\Ui\Component\MassAction\Filteraufgelöst (Select-All
über Pages funktioniert), Chooser-Page zeigt Anzahl + Rule-Dropdown,
Magento\Catalog\Model\Product\Action::updateAttributessetzt
stm_surcharge_rule_idindexfreundlich in einem Pass. - ACL-Resourcen:
STM_SurchargeMax::rules(Sub-Nodesrules_save,
rules_delete,rules_assign). - Übersetzungen: 60 neue Strings in
i18n/de_DE.csvund
i18n/en_US.csv.
Geändert
etc/db_schema_whitelist.jsonum die neue Tabelle erweitert.etc/di.xml: virtualTypeSTM\SurchargeMax\Model\ResourceModel\Rule\Grid\Collection
als SearchResult-Datasource für das Listing-UI-Component, plus Mapping in
Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory.Surcharge::collect-Constructor: 6. Parameter
RuleCollectionFactory $ruleCollectionFactory(war zuvor 5 nach
License-Removal).
[2.3.0] - 2026-04-26
Erste Adobe-Marketplace-fähige Variante. Lizenzpflichtige Funktionen
(STM_License) wurden entfernt; alle Settings sind ab dieser Version
unter dem zentralen STM-Tab erreichbar.
Umbenannt
- Modul umbenannt von
STM_RohreShippingnachSTM_SurchargeMax(finaler
Marketplace-Name). PHP-NamespaceSTM\RohreShipping\→STM\SurchargeMax\,
Composer-Packagestm/module-rohre-shipping→storetown-media/module-surcharge-max,
Section-IDstm_rohreshipping/*→stm_surchargemax/*,
ACL-ResourceSTM_RohreShipping::config→STM_SurchargeMax::config. - DB-Spalten
stm_rohre_surcharge/base_stm_rohre_surchargeund der
Total-Codestm_rohre_surchargebleiben unverändert (Persistence-Stabilität). - Setup-Patches
MigrateConfigPathsundAddProductAttributesdeklarieren
übergetAliases()ihre alten Klassen-Namen, damit bereits angewendete
Patches nicht erneut laufen. - Neuer Setup-Patch
RenameSectionPathmigriert Konfigurationswerte aus dem
Interim-Pfadstm_rohreshipping/*(Pre-Rename-Builds) idempotent nach
stm_surchargemax/*.
Geändert
- Backend-Konfiguration ist jetzt unter
Shop > Konfiguration > STM > Category Surcharge
zu finden (vorher:Sales > Shipping Methods > STM Zuschnittkosten). Die Settings
sind in fünf logische Gruppen aufgeteilt: General, Surcharge Configuration,
Tier Pricing, Customer Group Pricing, Product Page Notice — analog zu den
übrigen STM-Marketplace-Modulen. - Section-ID umbenannt von
carriers/stmrohreshipping/*nach
stm_surchargemax/{group}/{field}. Die XPaths inModel/Total/Quote/Surcharge.php,
ViewModel/Product/ShippingNotice.phpundBlock/Checkout/SurchargeInit.php
wurden entsprechend aktualisiert. etc/config.xml— Default-Werte in die neue Section/Group-Struktur verschoben.
Entfernt (Marketplace-Variante)
STM_License-Integration vollständig entfernt: Composer-Dependency
stm/module-license, Modul-Sequence-EintragSTM_Licenseinmodule.xml,
Extension-Registrierung inetc/di.xmlsowie Konstruktor-Injektion und
licenseService->isValid()-Aufruf inModel/Total/Quote/Surcharge.php.
Die lizenzpflichtige Variante des Moduls wird in einem separaten Branch
weitergeführt; diese Marketplace-Variante ist nach Adobes Vorgaben frei
von Drittpartei-Lizenzprüfungen.
Hinzugefügt
- Produkt-Level-Zuschlag: zwei neue Produkt-Attribute (Scope=Store,
Attribut-GruppeSTM Surcharge Maxauf der Produkt-Edit-Seite):stm_surcharge_enabled(yes/no) — markiert ein einzelnes Produkt als
zuschlagspflichtig, unabhängig von der Kategorie-Zuordnung. Damit lassen
sich Zuschläge auf Produkte erheben, die zu keiner Surcharge-Kategorie
gehören (z.B. ein einzelner Sonderartikel).stm_surcharge_amount(decimal) — überschreibt den Per-Unit-Zuschlag
für genau dieses Produkt. Hat Priorität vor Customer-Group-, Tier- und
Default-Preis. Damit lassen sich Premium-Produkte mit höherem Zuschlag
versehen, während andere Produkte derselben Kategorie den Default zahlen.
- Eligibility-Logik: Cart-Item ist zuschlagspflichtig wenn (Produkt in
Surcharge-Kategorie) ODER (stm_surcharge_enabled = yes). - Cost-Priorität pro Item: Produkt-Override > Customer-Group-Preis > Tier-Preis > Default.
- Modus-Semantik mit Overrides:
Per item: Summe aller (Per-Unit-Cost × Menge) über beide Buckets (Override + Standard).Per order: Maximum aller anwendbaren Per-Unit-Costs in der Order
(höchster Zuschlag-Wert gewinnt — semantisch "der teuerste Zuschlag-Artikel
bestimmt die Order-Surcharge").
Setup/Patch/Data/AddProductAttributes.php— Data-Patch der die zwei
Attribute beimsetup:upgradeidempotent anlegt.- Conditions & Limits-Gruppe in der Admin-Section (sortOrder 45):
min_amount(decimal) — Wenn der berechnete Surcharge unter diesem Betrag
läge, wird er auf 0 gesetzt (skip). Intent: "kleine Beträge nicht erheben",
nicht "auf Minimum heben".max_amount(decimal) — Cap für den berechneten Surcharge. Übersteigt der
Wert das Maximum, wird er auf max_amount gekappt.free_threshold(decimal) — Surcharge entfällt vollständig sobald die
Cart-Subtotal (netto, vor Steuer) ≥ threshold ist. Klassischer
"ab X € versandkostenfrei"-Pattern, übertragen auf den Surcharge.restrict_by_country(yes/no) +allowed_countries(multiselect) —
Surcharge gilt nur, wenn die Versandadresse in einem der ausgewählten
Länder liegt. Source-ModelMagento\Directory\Model\Config\Source\Country.- Logik in
Surcharge::collect(): Country-Check als Early-Out (vor
analyzeCart()), Free-Threshold gegenquote->getSubtotal()(Subtotal-
Collector ist bei totals_sort=65 bereits gelaufen), Min/Max-Caps am Ende
der Calculation, vorsetTotalAmount().
Setup/Patch/Data/MigrateConfigPaths.php— Data-Patch, der bestehende
carriers/stmrohreshipping/*-Konfigurationswerte beimsetup:upgrade
idempotent in die neuen Pfade übernimmt (Default- und Store-Scope).Setup/Uninstall.php— entfernt jetzt sowohl die neuen als auch die
Alt-Pfade auscore_config_data.
[2.2.0] - 2026-03-09
Hinzugefügt
- STM License Integration — Lizenzprüfung über
stm/module-licenseModul di.xml— Extension-Registrierung beiSTM\License\Model\ExtensionRegistry(Key:stm_rohre_shipping)
Geändert
composer.json— Dependencystm/module-license: ^1.0hinzugefügtmodule.xml—STM_Licensein Modul-Sequence aufgenommen
[2.1.0] - 2026-03-07
Hinzugefügt
Admin-Backend (Order / Invoice / Creditmemo)
- Zuschnittkosten-Anzeige im Admin Order View (zwischen Versand und Steuer)
- Zuschnittkosten-Anzeige im Admin Invoice View und Creditmemo View
- Zuschnittkosten-Anzeige beim Erstellen neuer Invoices und Creditmemos
- Adminhtml-Block
Block\Adminhtml\Sales\Totals\SurchargemitinitTotals() - Layout-XMLs:
sales_order_view,sales_order_invoice_view,sales_order_invoice_new,
sales_order_creditmemo_view,sales_order_creditmemo_new
Datenbank & Persistenz
- Deklaratives DB-Schema (
db_schema.xml) — Spaltenstm_rohre_surchargeund
base_stm_rohre_surchargeaufquote,sales_order,sales_invoice,sales_creditmemo db_schema_whitelist.jsonfür Magento 2.4+ Kompatibilität- Fieldset-Mapping (
fieldset.xml) für automatische Daten-Übertragung
Quote→Order (sales_convert_quote) und Order→Invoice/Creditmemo (sales_convert_order) - Observer
AddSurchargeToOrderauf Eventsales_model_service_quote_submit_beforeals
Safety-Net für die Quote→Order-Datenübertragung
Invoice & Creditmemo Collectors
Model\Total\Invoice\Surcharge— liest Zuschlag von Order, addiert zum Invoice Grand TotalModel\Total\Creditmemo\Surcharge— liest Zuschlag von Order, addiert zum Creditmemo Grand Total- Registrierung in
sales.xml(Sectionsorder_invoiceundorder_creditmemo, sort_order 399)
E-Mail-Integration
- Zuschnittkosten in der Bestellbestätigungs-E-Mail (
sales_email_order_items.xml) - Zuschnittkosten in der Invoice-E-Mail (
sales_email_order_invoice_items.xml) - Zuschnittkosten in der Creditmemo-E-Mail (
sales_email_order_creditmemo_items.xml)
Frontend Kundenbereich
- Zuschnittkosten in "Meine Bestellungen" → Bestelldetails (
sales_order_view.xml)
Warenkorb & Checkout
- Hyvä-Cart-Totals: Alpine.js-Template mit
x-for/x-iffürtotal_segments
(php-cart/totals/surcharge.phtml) - IA24 OnePageCheckout: SurchargeInit-Block liefert JSON-Daten für Client-seitige Berechnung
- Client-seitige Recalculation bei Versandart-Wechsel (
checkout-surcharge.js
mit MutationObserver) - Cart-Surcharge DOM-Injection als Fallback (
cart-surcharge.js) SurchargeInit.phperzwingtsetTotalsCollectedFlag(false)vorcollectTotals()
Steuer
- Zuschnittkosten-Steuer wird automatisch zum Tax-Total addiert (gleicher MwSt.-Satz
wie erstes steuerpflichtiges Produkt im Warenkorb)
Qualitätssicherung
- 43 Unit Tests, 53 Assertions — 100% bestanden
- PHPCS Magento2-Standard: 0 Errors, 0 Warnings
setup:di:compileerfolgreich- Testabdeckung: Quote-Total-Collector, Observer, ViewModel, Config-Source-Models
Geändert
Model\Total\Quote\Surcharge::collect()setzt Zuschlag nun auch auf$quote->setData()
(zusätzlich zu$total->setTotalAmount()) für Persistenz und Fieldset-Mappingetc/sales.xmlerweitert um Invoice- und Creditmemo-Sectionsetc/config.xmltotals_sort auf 65 angepasst
[2.0.0] - 2025-11-15
Hinzugefügt
Kernfunktionalität
- Zuschlag-Berechnung: Pro Bestellung (einmalig) oder pro Artikel (Menge × Zuschlag)
- Schwellenwert-Scope: Nur Nicht-Zuschlag-Produkte oder gesamter Warenkorb
- Staffelpreise: Mengenbasierte Zuschlag-Staffelung mit dynamischer Admin-Tabelle
- Kundengruppen-Preise: Individuelle Kosten pro Kundengruppe mit konfigurierbarem
Zuschlag, Standard-Versand und Free-Shipping-Schwelle
Admin-Konfiguration
- Vollständiges
system.xmlmit Validierung auf allen Feldern - Deutsche Labels und Hilfe-Kommentare für jedes Konfigurationsfeld
- Dynamische Admin-Tabellen für Staffelpreise und Kundengruppen-Preise
CustomerGroupColumnmit automatischer Gruppenauswahl aus Magento
Produktseite
- Konfigurierbare Hinweis-Box mit eigenen Farben (Hintergrund, Rahmen, Text)
- Platzhalter
{{label}}und{{cost}}für dynamischen Hinweistext - Benutzerdefinierter Hinweistext im Admin konfigurierbar
- ViewModel
Product\ShippingNotice(ersetzt deprecated Registry-Block) - Hyvä-kompatibles Template (
shipping_notice_hyva.phtml)
Architektur & Code-Qualität
- PHPUnit Unit Tests für alle Kernkomponenten
Setup\UninstallInterface für saubere Deinstallation (entfernt Config-Daten)di.xmlmit CustomerSession Proxy (vermeidet Session-Initialisierung im Konstruktor)- ACL-Resource
STM_SurchargeMax::configfür Admin-Berechtigungen - Composer-Installation via
storetown-media/module-surcharge-max - Mehrsprachigkeit:
de_DE.csvunden_US.csvÜbersetzungen
Geändert
- Carrier-Model komplett refactored:
declare(strict_types=1), typed Properties - Mehrere Kategorie-IDs kommagetrennt konfigurierbar (statt nur eine)
- Zuschlag-Bezeichnung konfigurierbar (nicht mehr hardcoded "Zuschnittkosten")
- Free-Shipping-Schwelle bezieht sich standardmäßig auf Nicht-Zuschlag-Produkte
- Länderbeschränkung nutzt Magento-Konfiguration (
specificcountry) statt hardcoded 'DE' - Templates:
escapeHtml(), ARIA-Attribute, CSS-Klassen statt Inline-Styles
Entfernt
- Observer mit
echo-Ausgabe (AddShippingNotice.php) - Deprecated Registry-Block (
Block/Product/ShippingNotice.php) - Hardcoded Länderbeschränkung auf 'DE'
- Überflüssige Dateien:
registration.xml,events.txt,.html, Cart-Template-Override
Behoben
- Typo "Zuschitt" → "Zuschnitt" in allen Texten und Übersetzungen
- Observer-Ordner PSR-4-Verstoß (
observer/→Observer/) system.xmlfehlerhafter Root-Tag- PHPCS: Alle Zeilen auf ≤ 120 Zeichen (Multi-Line-Umbrüche in XML)
[1.0.0] - 2024-06-01
Features
- Kategorie-basierter Versandkosten-Zuschlag für konfigurierbare Produktkategorien
- Standard-Versandkosten (Flatrate) für alle nicht-zuschlagpflichtigen Produkte
- Kostenlose Lieferung ab konfigurierbarem Schwellenwert
- Produktseiten-Hinweis für Zuschlag-Produkte
- Länderbeschränkung (hardcoded auf Deutschland)
Requires 2
| Package | Constraint |
|---|---|
| magento/framework | >=103.0 |
| php | >=8.1 |
Requires-dev 1
| Package | Constraint |
|---|---|
| phpunit/phpunit | >=9.5 |
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.
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.
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.
More from Storetown Media
View vendorAutomatic EU VAT rate synchronization for Magento 2 — imports and updates all European tax rates including automatic Tax Rule creation. Free open-source under MIT.
Adds file attachments (PDF, Office, images, videos, YouTube) to products with frontend download and inline preview (PDF, images, txt/csv, Word, Excel, PowerPoint), mass assign, CSV import, auto-assign rules and category grouping for Adobe Commerce / Magento Open Source.
One Page Checkout for Magento 2 — All checkout steps on a single page with AJAX validation, real-time shipping calculation, and responsive design. Compatible with Luma, Blank, and Hyvä themes.
B2B customer registration with admin approval workflow for Magento 2 / Adobe Commerce. Adds a dedicated B2B signup form with company data, VAT ID, and reCAPTCHA, holds new accounts in a pending state until a store admin reviews them, and triggers separate transactional emails for the customer and the shop owner.
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.