Skip to content
advanced Phase 90 · Shipping Deep Dive

Rate Calculation

Rate calculation - weight-based, price-based, destination-based, table rates

45m
0 problems
Topic Progress 0%

Weight-Based Rates

Weight Rate Calculator

namespace Vendor\Shipping\Rate\Calculation;

class WeightRateCalculator
{
    private array $tiers = [
        ['min' => 0, 'max' => 1, 'rate' => 5.00],
        ['min' => 1, 'max' => 5, 'rate' => 10.00],
        ['min' => 5, 'max' => 10, 'rate' => 15.00],
        ['min' => 10, 'max' => 20, 'rate' => 25.00],
        ['min' => 20, 'max' => 999, 'rate' => 35.00],
    ];

    public function calculate(float $weight): float
    {
        foreach ($this->tiers as $tier) {
            if ($weight >= $tier['min'] && $weight < $tier['max']) {
                return $tier['rate'];
            }
        }
        return end($this->tiers)['rate'];
    }
}

Per-Unit Weight Rate

namespace Vendor\Shipping\Rate\Calculation;

class PerUnitWeightRate
{
    private float $ratePerLb = 2.50;
    private float $minimumCharge = 5.00;

    public function calculate(array $items): float
    {
        $totalWeight = 0;
        foreach ($items as $item) {
            $totalWeight += $item->getWeight() * $item->getQty();
        }

        return max($this->minimumCharge, $totalWeight * $this->ratePerLb);
    }
}

Weight-Based with Zones

namespace Vendor\Shipping\Rate\Calculation;

class WeightZoneRate
{
    private array $zones = [
        'local' => [
            'countries' => ['US'],
            'tiers' => [
                ['min' => 0, 'max' => 5, 'rate' => 5.00],
                ['min' => 5, 'max' => 20, 'rate' => 10.00],
            ],
        ],
        'domestic' => [
            'countries' => ['CA', 'MX'],
            'tiers' => [
                ['min' => 0, 'max' => 5, 'rate' => 15.00],
                ['min' => 5, 'max' => 20, 'rate' => 25.00],
            ],
        ],
        'international' => [
            'countries' => ['*'],
            'tiers' => [
                ['min' => 0, 'max' => 5, 'rate' => 25.00],
                ['min' => 5, 'max' => 20, 'rate' => 45.00],
            ],
        ],
    ];

    public function calculate(float $weight, string $country): float
    {
        $zone = $this->getZone($country);

        foreach ($this->zones[$zone]['tiers'] as $tier) {
            if ($weight >= $tier['min'] && $weight < $tier['max']) {
                return $tier['rate'];
            }
        }

        return 0;
    }

    private function getZone(string $country): string
    {
        foreach ($this->zones as $zone => $config) {
            if (in_array($country, $config['countries']) || in_array('*', $config['countries'])) {
                return $zone;
            }
        }
        return 'international';
    }
}

Price-Based Rates

Price Rate Calculator

namespace Vendor\Shipping\Rate\Calculation;

class PriceRateCalculator
{
    private array $tiers = [
        ['min' => 0, 'max' => 50, 'rate' => 5.99],
        ['min' => 50, 'max' => 100, 'rate' => 9.99],
        ['min' => 100, 'max' => 200, 'rate' => 14.99],
        ['min' => 200, 'max' => 500, 'rate' => 19.99],
        ['min' => 500, 'max' => PHP_FLOAT_MAX, 'rate' => 0.00],
    ];

    public function calculate(float $cartTotal): float
    {
        foreach ($this->tiers as $tier) {
            if ($cartTotal >= $tier['min'] && $cartTotal < $tier['max']) {
                return $tier['rate'];
            }
        }
        return 0;
    }
}

Percentage-Based Rate

namespace Vendor\Shipping\Rate\Calculation;

class PercentageRate
{
    private float $percentage = 10.0; // 10% of cart total
    private float $minimumCharge = 5.00;
    private float $maximumCharge = 50.00;

    public function calculate(float $cartTotal): float
    {
        $rate = $cartTotal * ($this->percentage / 100);
        return max($this->minimumCharge, min($this->maximumCharge, $rate));
    }
}

Free Shipping Threshold

namespace Vendor\Shipping\Rate\Calculation;

class FreeShippingThreshold
{
    private float $threshold = 50.00;

    public function calculate(
        float $cartTotal,
        float $baseRate
    ): float {
        if ($cartTotal >= $this->threshold) {
            return 0.00;
        }
        return $baseRate;
    }
}

Destination-Based Rates

Zone-Based Rate Calculator

namespace Vendor\Shipping\Rate\Calculation;

class DestinationZoneRate
{
    private array $zones = [
        'zone_1' => [
            'countries' => ['US'],
            'rate' => 5.00,
        ],
        'zone_2' => [
            'countries' => ['CA', 'MX'],
            'rate' => 15.00,
        ],
        'zone_3' => [
            'countries' => ['GB', 'DE', 'FR'],
            'rate' => 25.00,
        ],
        'zone_4' => [
            'countries' => ['*'],
            'rate' => 35.00,
        ],
    ];

    public function calculate(string $country): float
    {
        foreach ($this->zones as $zone) {
            if (in_array($country, $zone['countries'])) {
                return $zone['rate'];
            }
        }
        return 35.00; // Default international
    }
}

Region-Based Rate

namespace Vendor\Shipping\Rate\Calculation;

class RegionRate
{
    private array $regionRates = [
        'US-CA' => 10.00,
        'US-NY' => 12.00,
        'US-TX' => 11.00,
        'CA-ON' => 15.00,
        'CA-BC' => 18.00,
    ];

    public function calculate(string $countryId, string $regionCode): float
    {
        $key = $countryId . '-' . $regionCode;
        return $this->regionRates[$key] ?? 25.00;
    }
}

Zip Code Range Rate

namespace Vendor\Shipping\Rate\Calculation;

class ZipCodeRangeRate
{
    private array $ranges = [
        ['from' => '00000', 'to' => '29999', 'rate' => 5.00],
        ['from' => '30000', 'to' => '59999', 'rate' => 8.00],
        ['from' => '60000', 'to' => '89999', 'rate' => 10.00],
        ['from' => '90000', 'to' => '99999', 'rate' => 12.00],
    ];

    public function calculate(string $zipCode): float
    {
        foreach ($this->ranges as $range) {
            if ($zipCode >= $range['from'] && $zipCode <= $range['to']) {
                return $range['rate'];
            }
        }
        return 15.00;
    }
}

Table Rates

Table Rate Implementation

namespace Vendor\Shipping\Rate\TableRate;

class TableRateCalculator
{
    private array $tableRates = [];

    public function loadFromCsv(string $csvPath): void
    {
        $handle = fopen($csvPath, 'r');
        while (($data = fgetcsv($handle)) !== false) {
            $this->tableRates[] = [
                'country' => $data[0],
                'region' => $data[1],
                'zip' => $data[2],
                'weight' => (float) $data[3],
                'price' => (float) $data[4],
                'rate' => (float) $data[5],
            ];
        }
        fclose($handle);
    }

    public function calculate(
        string $country,
        string $region,
        string $zip,
        float $weight,
        float $price
    ): float {
        foreach ($this->tableRates as $row) {
            if ($this->matches($row, $country, $region, $zip, $weight, $price)) {
                return $row['rate'];
            }
        }
        return 0;
    }

    private function matches(
        array $row,
        string $country,
        string $region,
        string $zip,
        float $weight,
        float $price
    ): bool {
        if ($row['country'] !== '*' && $row['country'] !== $country) {
            return false;
        }
        if ($row['region'] !== '*' && $row['region'] !== $region) {
            return false;
        }
        if ($row['zip'] !== '*' && strpos($zip, $row['zip']) !== 0) {
            return false;
        }
        if ($row['weight'] > 0 && $weight > $row['weight']) {
            return false;
        }
        if ($row['price'] > 0 && $price > $row['price']) {
            return false;
        }
        return true;
    }
}

CSV Format

country,region,zip,weight,price,rate
US,*,*,10,0,5.00
US,*,*,50,0,10.00
US,CA,*,0,100,8.00
CA,*,*,20,0,15.00
*,*,*,0,0,25.00

Magento Table Rate Export

namespace Vendor\Shipping\Rate\TableRate\Import;

class TableRateImport
{
    public function importFromMagento(array $rates): void
    {
        foreach ($rates as $rate) {
            $this->tableRateCalculator->addRow([
                'country' => $rate->getCountryId(),
                'region' => $rate->getRegionId() ?: '*',
                'zip' => $rate->getZip() ?: '*',
                'weight' => $rate->getWeight() ?: 0,
                'price' => $rate->getPrice() ?: 0,
                'rate' => $rate->getCost(),
            ]);
        }
    }
}

Quiz

1. What is the advantage of table rates?

Question 1 options

2. How do you implement free shipping above a threshold?

Question 2 options

3. What does a zone define in shipping?

Question 3 options

Flashcards

Question

What are the main rate calculation types?

Answer

Weight-based, price-based, destination-based, table rates

Question

How do table rates work?

Answer

CSV with country/region/zip/weight/price → rate mappings

Question

What is a shipping zone?

Answer

Geographic area with specific rate rules

Question

How to implement free shipping?

Answer

Check threshold and return rate of 0

Revision Notes

Key Takeaways

  • 1. Weight-based rates use product/package weight for calculation
  • 2. Price-based rates use cart subtotal for rating
  • 3. Destination zones group countries/regions with same rates
  • 4. Table rates provide maximum flexibility via CSV import
  • 5. Free shipping thresholds encourage higher order values

Interview Tips

  • Explain weight vs price rate calculation trade-offs
  • Discuss how to design shipping zones
  • Describe table rate implementation approach
  • Talk about combining multiple rate factors

Cheat Sheet

Rate Types:
  Weight → rate × weight
  Price → rate × % of total
  Destination → zone-based
  Table → CSV mapping

Zones:
  Local → same country
  Domestic → neighboring countries
  International → rest of world

Table Rate CSV:
  country,region,zip,weight,price,rate
  * = wildcard (any value)