Bagisto Forum

    Bagisto

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    Dynamic Shipping Pricing by Weight and Country

    General Discussion
    2
    2
    37
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      barnaddy06 last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • S
        shivendra-webkul last edited by

        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
        AU
        

        For weight, if you are using Bagisto's native weight attribute, you should use the cart item total_weight instead 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->items is important because all_items may include child items and can result in double-counting for configurable or bundle products.

        Then create a custom carrier extending AbstractShipping and perform the country + weight calculation inside the calculate() 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_price and price on the CartShippingRate. The base price should be calculated in the store's base currency, while price should 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 weight attribute, total_weight may 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

        1 Reply Last reply Reply Quote 0
        • First post
          Last post