Dynamic Shipping Pricing by Weight and Country
-
I'm working on an online pharmacy project for Pills4Cure, where we ship healthcare products to customers in the USA, UK, Netherlands, and Australia. I need to calculate a custom payment or shipping fee based on two factors: the total weight of the items in the cart and the customer's destination country.
I already have access to the product weight through a custom attribute, but I'm not sure of the best way to retrieve the destination country selected during checkout or from the shopping cart. I'd also like to know the recommended approach for combining the cart weight with the shipping country to calculate a dynamic fee.
Has anyone implemented a similar solution for international shipping? Any suggestions, best practices, or code examples would be greatly appreciated.
-
Hello @barnaddy06
The best approach is to implement this as a custom shipping carrier.
You should not read the destination country directly from the request or session. Get the current cart and retrieve the country from the cart's shipping address:
use Webkul\Checkout\Facades\Cart; $cart = Cart::getCart(); $country = $cart->shipping_address?->country ?? $cart->billing_address?->country;The country value will be the ISO country code, for example:
US GB NL AUFor weight, if you are using Bagisto's native
weightattribute, you should use the cart itemtotal_weightinstead of recalculating the product weight manually:$totalWeight = 0; foreach ($cart->items as $item) { if (! $item->getTypeInstance()->isStockable()) { continue; } $totalWeight += (float) $item->total_weight; }Using
$cart->itemsis important becauseall_itemsmay include child items and can result in double-counting for configurable or bundle products.Then create a custom carrier extending
AbstractShippingand perform the country + weight calculation inside thecalculate()method:class InternationalRate extends AbstractShipping { protected $code = 'international'; public function calculate() { if (! $this->isAvailable()) { return false; } $cart = Cart::getCart(); if (! $cart || ! $cart->haveStockableItems()) { return false; } $country = $cart->shipping_address?->country ?? $cart->billing_address?->country; $weight = 0; foreach ($cart->items as $item) { if (! $item->getTypeInstance()->isStockable()) { continue; } $weight += (float) $item->total_weight; } $rates = [ 'US' => ['handling' => 8, 'per_kg' => 2.5], 'GB' => ['handling' => 6, 'per_kg' => 2], 'NL' => ['handling' => 6.5, 'per_kg' => 2.2], 'AU' => ['handling' => 12, 'per_kg' => 4], ]; if (! isset($rates[$country])) { return false; } $basePrice = $rates[$country]['handling'] + ($weight * $rates[$country]['per_kg']); return $this->getRate($basePrice); } }The main point is:
Cart → Shipping Address Country + Cart Item Weight → Custom Carrier → Calculated Shipping Rate
Also, make sure to set both
base_priceandpriceon theCartShippingRate. The base price should be calculated in the store's base currency, whilepriceshould be converted using:$rate->base_price = $basePrice; $rate->price = core()->convertPrice($basePrice);If the shipping rules become more complex, such as multiple weight slabs per country, I would recommend storing the rules in a database table rather than hardcoding them.
One important note: if your product weight is stored in a completely custom attribute instead of Bagisto's native
weightattribute,total_weightmay not be populated. In that case, either map your value to Bagisto's native weight field or calculate it from your custom attribute manually.For this requirement, a custom shipping carrier is the cleanest and most maintainable Bagisto solution.
Best regards,
Shivendra Gupta
Team Bagisto