Custom Shipping Rate
eigene Versandkosten-Logik in Magento 2
Sobald Versandkosten nicht mehr nur pauschal oder tabellenbasiert sind, braucht dein Shop eine eigene Carrier-Logik. Magento 2 bietet dafür einen klaren Einstieg über eine eigene Shipping Method mit `collectRates()` und Systemkonfiguration.
Inhaltsverzeichnis
- 1. Wann eine eigene Versandlogik sinnvoll ist
- 2. Modulstruktur und Konfiguration
- 3. Eigene Carrier-Klasse mit collectRates()
- 4. RateResult und Versandmethode zurückgeben
- 5. Eigene Versandkosten-Regeln aufbauen
- 6. Typische Fehler
- 7. Eigener Carrier vs. Table Rates
- 8. Magento 2 Unterstützung
- 9. Zusammenfassung
- 10. FAQ
1. Wann eine eigene Versandlogik sinnvoll ist
Eine Custom Shipping Rate Magento 2 wird dann interessant, wenn Standardversandmethoden nicht mehr ausreichen. Das ist zum Beispiel der Fall, wenn Versandkosten vom Warenkorbgewicht, von Produktattributen, Lieferzonen, Kundengruppen, Gefahrgut, Kühlversand oder einer externen API abhängen. In solchen Fällen wird eine eigene Carrier-Implementierung oft sauberer als Workarounds über Preisregeln oder unübersichtliche Tabellen.
Magento bringt bereits Versandmethoden und Table Rates mit. Für einfache Szenarien sind diese ausreichend. Sobald die Logik aber fachlich komplex wird oder du Daten aus einem ERP, einer Speditions-API oder aus individuellen Produktregeln brauchst, ist ein eigener Carrier der bessere Weg. Eine Custom Shipping Rate Magento 2 lässt sich dann klar im Modul kapseln, konfigurieren und testen.
Wichtig ist die Abgrenzung: Eigene Versandlogik sollte keine Sammlung harter Sonderfälle im Template oder Checkout-Frontend werden. Die Berechnung gehört in eine serverseitige Carrier-Klasse und deren Services. Genau so bleibt das Verhalten nachvollziehbar und auch in späteren Magento-Upgrades kontrollierbar.
2. Modulstruktur und Konfiguration
Für eine Custom Shipping Rate Magento 2 brauchst du ein normales Modul mit einer Carrier-Klasse, Konfiguration und Admin-Einstellungen. Das Modul sollte sauber unter app/code/Vendor/Module liegen. Die Konfiguration erfolgt typischerweise über config.xml, system.xml und etc/acl.xml, damit der Shopbetreiber die Methode aktivieren, Titel ändern oder Grundpreise steuern kann.
Im Beispiel verwenden wir ein Modul Mironsoft_CustomShipping. Es bekommt einen eigenen Konfigurationsbereich mit Aktiv-Flag, Titel, Methodenname und Basispreis. Diese Werte sollten nicht hart im Carrier stehen. Gerade bei Versandmethoden ändern sich Texte und Preise oft im Betrieb. Eine Custom Shipping Rate Magento 2 ohne Admin-Konfiguration ist deshalb nur für sehr enge Spezialfälle sinnvoll.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<carriers>
<mironsoft_customshipping>
<active>1</active>
<title>Mironsoft Shipping</title>
<name>Custom Shipping Rate</name>
<price>9.90</price>
</mironsoft_customshipping>
</carriers>
</default>
</config>
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="carriers">
<group id="mironsoft_customshipping" translate="label" sortOrder="910"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Mironsoft Custom Shipping</label>
<field id="active" translate="label" type="select" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="title" translate="label" type="text" sortOrder="20"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Title</label>
</field>
<field id="name" translate="label" type="text" sortOrder="30"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Method Name</label>
</field>
<field id="price" translate="label" type="text" sortOrder="40"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Base Price</label>
</field>
</group>
</section>
</system>
</config>
Damit die Methode im Admin sauber erscheint, müssen die XML-Struktur und der Carrier-Code dieselbe Carrier-ID verwenden. Hier ist das mironsoft_customshipping. Genau solche kleinen Inkonsistenzen sind später oft die Ursache, wenn eine Custom Shipping Rate Magento 2 scheinbar korrekt entwickelt wurde, aber im Checkout nie auftaucht.
3. Eigene Carrier-Klasse mit collectRates()
Das Herzstück einer Custom Shipping Rate Magento 2 ist die Carrier-Klasse. Sie erweitert typischerweise AbstractCarrier und implementiert CarrierInterface. Die zentrale Methode heißt collectRates(). Magento ruft sie auf, wenn verfügbare Versandmethoden für die aktuelle Adresse und den aktuellen Warenkorb berechnet werden.
In collectRates() prüfst du zuerst, ob die Methode aktiviert ist. Danach bewertest du den Request-Kontext: Zieladresse, Artikel, Gewicht, Subtotal, Kundenkontext oder andere Faktoren. Wenn die Methode nicht anwendbar ist, gibst du false zurück. Wenn sie anwendbar ist, erzeugst du ein RateResult mit einer oder mehreren Versandmethoden.
<?php
declare(strict_types=1);
namespace Mironsoft\CustomShipping\Model\Carrier;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
use Magento\Quote\Model\Quote\Address\RateResultFactory;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Psr\Log\LoggerInterface;
/**
* Custom shipping carrier implementation.
*/
final class CustomShipping extends AbstractCarrier implements CarrierInterface
{
protected $_code = 'mironsoft_customshipping';
public function __construct(
ScopeConfigInterface $scopeConfig,
ErrorFactory $rateErrorFactory,
LoggerInterface $logger,
private readonly RateResultFactory $rateResultFactory,
private readonly MethodFactory $methodFactory,
array $data = []
) {
parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
}
/**
* Collects available shipping rates for the current quote address.
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
$result = $this->rateResultFactory->create();
$method = $this->methodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle((string) $this->getConfigData('title'));
$method->setMethod('standard');
$method->setMethodTitle((string) $this->getConfigData('name'));
$price = (float) $this->getConfigData('price');
$method->setPrice($price);
$method->setCost($price);
$result->append($method);
return $result;
}
/**
* Returns the allowed shipping methods for this carrier.
*
* @return array<string, string>
*/
public function getAllowedMethods(): array
{
return [
'standard' => (string) $this->getConfigData('name')
];
}
}
Diese Basis liefert eine einzelne Versandmethode mit konfigurierbarem Preis. Das ist noch keine komplexe Fachlogik, aber die entscheidende Struktur steht. Jede ernsthafte Custom Shipping Rate Magento 2 baut auf genau diesem Muster auf und erweitert dann die Preis- oder Verfügbarkeitsregeln.
4. RateResult und Versandmethode zurückgeben
Viele Entwickler verstehen collectRates() zunächst als Funktion, die einfach eine Zahl zurückgibt. Tatsächlich erwartet Magento ein RateResult-Objekt mit einer oder mehreren Method-Instanzen. Genau dort definierst du, wie die Versandmethode im Checkout erscheint. Carrier-Code, Method-Code, Titel und Preis müssen konsistent sein, sonst wird die Methode zwar gebaut, aber nicht sauber im Checkout angezeigt.
Eine Custom Shipping Rate Magento 2 kann auch mehrere Methoden aus einem Carrier zurückgeben, zum Beispiel Standard, Express und Same Day. Dann bleibt der Carrier identisch, aber der Method-Code und die Preislogik unterscheiden sich. Für komplexere Fälle ist das oft sinnvoller als mehrere völlig getrennte Carrier-Klassen.
Wichtig ist auch der Unterschied zwischen Preis und Kosten. setPrice() ist der Betrag, den der Kunde sieht. setCost() kann für interne Logik oder Margenbetrachtung relevant sein. Viele einfache Implementierungen setzen beide Werte identisch. Das ist technisch ok, solange keine differenzierte Kostenlogik benötigt wird.
5. Eigene Versandkosten-Regeln aufbauen
Jetzt kommt der eigentlich interessante Teil: die fachliche Berechnung. Eine Custom Shipping Rate Magento 2 ist wertvoll, wenn du Regeln aufbauen kannst, die über einen festen Preis hinausgehen. Typische Kriterien sind Gewicht, Anzahl Pakete, Gefahrgut-Flags, Ziel-Land, PLZ-Zonen, sperrige Produkte, Lagerstandorte oder B2B-Kundenkontexte.
Diese Logik sollte nicht unkontrolliert direkt in der Carrier-Klasse wachsen. Für einfache Fälle ist das noch überschaubar. Sobald mehrere Bedingungen kombiniert werden, lohnt sich eine separate Service-Klasse wie etwa ShippingPriceCalculator. Dann bleibt der Carrier ein Integrationspunkt mit Magento, und die Preislogik selbst bleibt eigenständig testbar.
<?php
declare(strict_types=1);
namespace Mironsoft\CustomShipping\Service;
use Magento\Quote\Model\Quote\Address\RateRequest;
/**
* Calculates shipping rates based on quote conditions.
*/
final class ShippingPriceCalculator
{
/**
* Calculates the final shipping price.
*/
public function calculate(RateRequest $request, float $basePrice): float
{
$weight = (float) $request->getPackageWeight();
$subtotal = (float) $request->getPackageValueWithDiscount();
if ($subtotal >= 150.00) {
return 0.0;
}
if ($weight > 10.0) {
return $basePrice + 5.0;
}
return $basePrice;
}
}
Mit so einer Trennung kannst du eine Custom Shipping Rate Magento 2 schrittweise erweitern: Freibetrag, Zuschläge, Ausschlüsse, API-basierte Echtzeitpreise oder Abhängigkeiten von Produktattributen. Der Checkout bleibt gleich, aber die Regelengine wird sauberer. Genau das ist langfristig wartbarer als ein Carrier mit endlosen If-Else-Blöcken.
6. Typische Fehler
Die typischen Fehler bei eigenen Versandmethoden sind gut vorhersehbar. Erstens sind Carrier-ID und XML-Konfiguration nicht konsistent. Zweitens wird false zu früh oder zu spät zurückgegeben. Drittens mischt die Carrier-Klasse Preislogik, API-Calls und Quote-Auswertung direkt in einer Methode. Viertens werden Konfigurationswerte hart codiert. Fünftens wird nicht geprüft, ob bestimmte Ziel-Länder oder Warenkorbkombinationen die Methode überhaupt zulassen.
Ein weiterer häufiger Fehler ist fehlendes Debugging der realen Request-Daten. Der RateRequest enthält Gewicht, Werte, Ziel-Land und weitere Informationen, aber nicht immer so, wie man es spontan erwartet. Eine Custom Shipping Rate Magento 2 sollte deshalb nie nur gegen einen Minimal-Warenkorb entwickelt werden. Unterschiedliche Produktkombinationen, virtuelle Produkte, Rabattfälle und Multistore-Kontexte müssen geprüft werden.
Auch Performance kann relevant werden. Wenn eine Versandmethode bei jedem Checkout-Schritt mehrere externe APIs abfragt, wird das Frontend spürbar träge. In solchen Fällen braucht man Caching, asynchrone Vorberechnung oder eine bewusstere Architektur als nur „API in collectRates() aufrufen“.
Gerade im B2B-Umfeld lohnt sich außerdem ein Blick auf Multistore- und Mehrwährungsfälle. Eine Versandmethode kann je Website unterschiedliche Carrier-Titel, Basispreise oder Freigrenzen benötigen. Wenn diese Unterschiede früh in der Konfiguration berücksichtigt werden, bleibt die Lösung auch bei mehreren Shops innerhalb einer Instanz konsistent und administrierbar.
7. Eigener Carrier vs. Table Rates
Nicht jede Versandlogik braucht sofort einen eigenen Carrier. Table Rates sind für lineare, tabellenförmige Preisregeln oft ausreichend. Eine Custom Shipping Rate Magento 2 lohnt sich erst dann wirklich, wenn Regeln dynamisch werden oder von Informationen abhängen, die mit Table Rates kaum noch sauber modellierbar sind.
| Ansatz | Gut geeignet für | Grenze |
|---|---|---|
| Table Rates | Einfache Zonen-, Preis- oder Gewichtstabellen | Schlecht bei dynamischen oder API-basierten Regeln |
| Eigener Carrier | Komplexe Fachlogik, Integrationen, mehrere Bedingungen | Mehr Entwicklungs- und Testaufwand |
| Externe Shipping Extension | Standardisierte Carrier-Integrationen | Weniger flexibel bei projektspezifischen Sonderregeln |
Die Entscheidung sollte also nicht ideologisch sein. Wenn Table Rates reichen, nutze sie. Wenn die Fachlogik darüber hinausgeht, ist eine Custom Shipping Rate Magento 2 der saubere Weg. Wichtig ist nur, dass die Logik serverseitig und modular bleibt.
Mironsoft
Magento 2 Checkout, Shipping und modulare Versandlogik
Eigene Versandkosten sauber in Magento abbilden?
Wir entwickeln Magento 2 Carrier-Module mit konfigurierbarer Versandlogik, sauberer Preisberechnung, Checkout-Integration und wartbarer Architektur für komplexe Versandfälle.
Carrier
Eigene Shipping Methods mit sauberem `collectRates()`-Aufbau
Regellogik
Gewicht, Zonen, Produktattribute, Freibeträge und API-Preise
Checkout
Magento 2.4.8, PHP 8.4 und saubere Erweiterung ohne Quick Fixes
9. Zusammenfassung
Eine Custom Shipping Rate Magento 2 ist der richtige Weg, wenn Standardversandmethoden die Fachlogik nicht mehr sauber abbilden. Der Kern besteht aus Carrier-Klasse, Konfiguration und einer klaren Preisberechnung über collectRates(). Mit zusätzlicher Service-Schicht bleibt die Logik testbar und erweiterbar.
Wichtig ist, dass Versandregeln nicht als ungeordnete Sonderfälle wachsen. Saubere Modulstruktur, Admin-Konfiguration und eine klare Trennung zwischen Carrier und Preislogik machen die Versandmethode langfristig wartbar. Genau dann wird eine eigene Shipping Method in Magento wirklich sinnvoll.
Custom Shipping Rate Magento 2 — Das Wichtigste auf einen Blick
Carrier
Eigene Versandmethode über `AbstractCarrier`, `CarrierInterface` und `collectRates()` implementieren.
Konfiguration
Titel, Aktiv-Status und Basispreis nicht hart codieren, sondern über `config.xml` und `system.xml` pflegbar machen.
Preislogik
Komplexe Regeln in Services auslagern statt alles direkt in der Carrier-Klasse zu bündeln.
Checkout
Mit unterschiedlichen Warenkörben, Ländern, Gewichten und Sonderfällen realistisch testen.
10. FAQ: Custom Shipping Rate in Magento 2
1 Was ist eine Custom Shipping Rate in Magento 2?
2 Wann braucht man einen eigenen Carrier?
3 Welche Methode ist zentral?
collectRates() berechnet, welche Versandmethoden verfügbar sind und welchen Preis sie haben.4 Was gibt collectRates() zurück?
5 Braucht die Methode system.xml?
6 Preis und Cost: der Unterschied?
setPrice() ist der Kundenpreis, setCost() kann interne Kosten oder Margenlogik abbilden.