Carrier Configuration
Carrier Configuration Structure
<!-- app/code/Vendor/Shipping/etc/system.xml -->
<config>
<section id="carriers" translate="label" sortOrder="320">
<group id="custom_rate" translate="label" type="text" sortOrder="100">
<label>Custom Rate Shipping</label>
<field id="active" translate="label" type="select" sortOrder="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="title" translate="label" type="text" sortOrder="10">
<label>Title</label>
</field>
<field id="name" translate="label" type="text" sortOrder="20">
<label>Method Name</label>
</field>
<field id="price" translate="label" type="text" sortOrder="30">
<label>Default Price</label>
<validate>validate-number validate-zero-or-greater</validate>
</field>
<field id="handling_type" translate="label" type="select" sortOrder="40">
<label>Calculate Handling Fee</label>
<source_model>Magento\Shipping\Model\Config\Source\Handling\Type</source_model>
</field>
<field id="handling_fee" translate="label" type="text" sortOrder="50">
<label>Handling Fee</label>
</field>
<field id="sallowspecific" translate="label" type="select" sortOrder="60">
<label>Ship to Applicable Countries</label>
<source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model>
</field>
<field id="specificcountry" translate="label" type="multiselect" sortOrder="70">
<label>Ship to Specific Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
</field>
</group>
</section>
</config>
Carrier Model
namespace Vendor\Shipping\Model\Carrier;
class CustomRate extends \Magento\Shipping\Model\Carrier\AbstractCarrier
{
protected $_code = 'custom_rate';
protected $_isFixed = false;
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Quote\Model\Quote\Address\RateResult\Factory $rateFactory,
\Magento\Shipping\Model\Rate\Result\Factory $resultFactory,
array $data = []
) {
$this->rateFactory = $rateFactory;
$this->resultFactory = $resultFactory;
parent::__construct($scopeConfig, $data);
}
public function collectRates(\Magento\Shipping\Model\Request\Data $request)
{
$result = $this->resultFactory->create();
if (!$this->getConfigData('active')) {
return $result;
}
$rate = $this->rateFactory->create();
$rate->setCarrier($this->_code);
$rate->setMethod($this->_code . '_flat');
$rate->setCarrierTitle($this->getConfigData('title'));
$rate->setMethodTitle('Flat Rate');
$rate->setPrice($this->getConfigData('price'));
$rate->setCost(0);
$result->append($rate);
return $result;
}
}
Rate Calculation Logic
Complex Rate Calculation
namespace Vendor\Shipping\Model\Carrier;
class WeightBasedRate extends \Magento\Shipping\Model\Carrier\AbstractCarrier
{
protected $_code = 'weight_rate';
public function collectRates(\Magento\Shipping\Model\Request\Data $request)
{
$result = $this->resultFactory->create();
$totalWeight = $request->getPackageWeight();
$destCountry = $request->getDestCountryId();
$destZip = $request->getDestPostcode();
// Rate tiers
$rates = [
['min' => 0, 'max' => 1, 'price' => 5.00],
['min' => 1, 'max' => 5, 'price' => 10.00],
['min' => 5, 'max' => 10, 'price' => 15.00],
['min' => 10, 'max' => 50, 'price' => 25.00],
];
foreach ($rates as $tier) {
if ($totalWeight >= $tier['min'] && $totalWeight < $tier['max']) {
$price = $tier['price'];
// Zone-based adjustment
if ($destCountry === 'US') {
$price *= 1.0;
} elseif ($destCountry === 'CA') {
$price *= 1.5;
} else {
$price *= 2.0;
}
// Handling fee
$handlingFee = (float) $this->getConfigData('handling_fee');
$handlingType = $this->getConfigData('handling_type');
if ($handlingType === 'F') {
$price += $handlingFee;
} else {
$price += ($price * $handlingFee / 100);
}
$rate = $this->rateFactory->create();
$rate->setCarrier($this->_code);
$rate->setMethod($this->_code . '_weight');
$rate->setCarrierTitle($this->getTitle());
$rate->setMethodTitle('Weight Based');
$rate->setPrice($price);
$rate->setCost($price * 0.5);
$result->append($rate);
}
}
return $result;
}
}
Handling Fees
Handling Fee Calculator
namespace Vendor\Shipping\Fee;
class HandlingFeeCalculator
{
public function calculate(
float $basePrice,
string $feeType,
float $feeValue,
array $conditions = []
): float {
$fee = 0;
if ($feeType === 'F') {
// Fixed fee
$fee = $feeValue;
} elseif ($feeType === 'P') {
// Percentage fee
$fee = $basePrice * ($feeValue / 100);
}
// Apply conditions
if (!empty($conditions['min_order'])) {
$minOrder = $conditions['min_order'];
if ($basePrice >= $minOrder) {
$fee = 0; // Free shipping above threshold
}
}
return round($fee, 2);
}
}
Handling Fee Configuration
<field id="handling_type" translate="label" type="select" sortOrder="40">
<label>Calculate Handling Fee</label>
<source_model>Magento\Shipping\Model\Config\Source\Handling\Type</source_model>
</field>
<field id="handling_fee" translate="label" type="text" sortOrder="50">
<label>Handling Fee</label>
<validate>validate-number validate-zero-or-greater</validate>
</field>
Free Shipping Rules
namespace Vendor\Shipping\Free;
class FreeShippingRule
{
public function isEligible(
CartInterface $cart,
array $config
): bool {
$minOrderAmount = $config['min_order_amount'] ?? 0;
$freeMethods = $config['free_methods'] ?? [];
if ($cart->getGrandTotal() >= $minOrderAmount) {
return true;
}
return false;
}
}
Shipping Method Rules
Rule-Based Method Selection
namespace Vendor\Shipping\Rule;
interface ShippingRuleInterface
{
public function matches(CartInterface $cart): bool;
public function getMethods(): array;
}
class WeightRule implements ShippingRuleInterface
{
private float $minWeight;
private float $maxWeight;
private array $methods;
public function __construct(float $min, float $max, array $methods)
{
$this->minWeight = $min;
$this->maxWeight = $max;
$this->methods = $methods;
}
public function matches(CartInterface $cart): bool
{
return $cart->getWeight() >= $this->minWeight
&& $cart->getWeight() < $this->maxWeight;
}
public function getMethods(): array
{
return $this->methods;
}
}
class CountryRule implements ShippingRuleInterface
{
private array $allowedCountries;
private array $methods;
public function matches(CartInterface $cart): bool
{
return in_array($cart->getShippingAddress()->getCountryId(), $this->allowedCountries);
}
public function getMethods(): array
{
return $this->methods;
}
}
Method Availability Checker
namespace Vendor\Shipping\Rule;
class MethodAvailability
{
private array $rules;
public function getAvailableMethods(CartInterface $cart): array
{
$available = [];
foreach ($this->rules as $rule) {
if ($rule->matches($cart)) {
$available = array_merge($available, $rule->getMethods());
}
}
return array_unique($available);
}
}
Quiz
1. What does handling_type 'F' mean?
2. How do you restrict shipping to specific countries?
3. What is zone-based rate adjustment?
Flashcards
Question
What does collectRates() return?
Click to reveal answer
Answer
Rate\Result object with available shipping methods
Question
What is handling_type 'F' vs 'P'?
Click to reveal answer
Answer
F = fixed amount, P = percentage of price
Question
How to restrict shipping countries?
Click to reveal answer
Answer
sallowspecific + specificcountry in system.xml
Question
What are shipping rules?
Click to reveal answer
Answer
Conditions that determine available shipping methods
Revision Notes
Key Takeaways
- 1. Carrier configuration in system.xml defines shipping settings
- 2. Rate calculation can use weight, price, or custom logic
- 3. Handling fees can be fixed or percentage-based
- 4. Country restrictions limit where carriers are available
- 5. Rule-based method selection enables complex shipping logic
Interview Tips
- • Explain the rate calculation flow in collectRates()
- • Discuss handling fee calculation types
- • Describe country restriction configuration
- • Talk about designing complex shipping rules
Cheat Sheet
Shipping Methods:
carrier_code → unique identifier
method_code → specific service
collectRates() → returns Rate\Result
Rate Calculation:
Weight-based → tiers by weight
Price-based → tiers by cart total
Zone-based → adjust by destination
Handling Fees:
Type F → fixed amount
Type P → percentage
Country Restriction:
sallowspecific=1
specificcountry=US,CA,GB