Custom shipping method in Magento

Custom shipping method in Magento

In this article, I will demonstrate how to write custom shipping in Magento, or to be more precise, two of them: standard shipping and express shipping, which is only available if none of your cart items exceeds specified weight threshold. Lets start by explaining how Magento handles shipping, and what would be needed to achieve our goal.


When looking for available shipping methods, Magento first gathers all available carriers. A “Carrier” represents a shipping carrier, just like in real world (ex. FedEx). Each carrier is represented with the class that extends Mage_Shipping_Model_Carrier_Abstract.

After list of carriers has been received, shipping information(implemented as Mage_Shipping_Model_Rate_Request) is sent to carrier in order to retrieve all available rates provided by given carrier, represented as Mage_Shipping_Model_Rate_Result.

This process happens in Mage_Shipping_Model_Shipping::collectRates() as seen in code below:

...
$carriers = Mage::getStoreConfig('carriers', $storeId);
 
foreach ($carriers as $carrierCode => $carrierConfig) {
    $this->collectCarrierRates($carrierCode, $request);
}
...

Function collectCarrierRates() is responsible for checking carrier availability (is carrier enabled in admin, is it available for requested country, etc.), and eventually triggers collectRates() function of your class, which we will implement later.

And that is general outline of what is going on behind the scenes. We are now ready to write some code that will fit nicely into everything explained above. First thing you will need to do, is create new module which depends on Mage_Shipping. Besides standard module configuration, you will have following inside your config.xml:

<config>
    ...
    <default>
        ...
        <carriers>
            <inchoo_shipping>
                <active>1</active>
                <model>inchoo_shipping/carrier</model>
                <title>Inchoo Shipping Carrier</title>
                <sort_order>10</sort_order>
                <sallowspecific>0</sallowspecific>
                <express_max_weight>1</express_max_weight>
            </inchoo_shipping>
        </carriers>
        ...
    </default>
    ...
</config>

Entries active, sallowspecific and express_max_items are config entries which will be used and explained later. We will start with model entry. You can see that our carrier will be represented with Inchoo_Shipping_Model_Carrier, so lets implement that class. As previously said, carrier needs to extend Mage_Shipping_Model_Carrier_Abstract and implement Mage_Shipping_Model_Carrier_Interface in order to ensure Magento can work with it. We will start by doing just that:

class Inchoo_Shipping_Model_Carrier
    extends Mage_Shipping_Model_Carrier_Abstract
    implements Mage_Shipping_Model_Carrier_Interface
{
    protected $_code = 'inchoo_shipping';

Next, our interface requires us to implement getAllowedMethods() which returns array of key-value pairs of all available methods, so let’s do that:

public function getAllowedMethods()
{
    return array(
        'standard'    =>  'Standard delivery',
        'express'     =>  'Express delivery',
    );
}

Finally, we said that rates are collected by calling collectRates(). This function takes shipping information as parameter, and returns all available rates. It is also responsible for determining which rate is available for given request:

public function collectRates(Mage_Shipping_Model_Rate_Request $request)
{
	/** @var Mage_Shipping_Model_Rate_Result $result */
	$result = Mage::getModel('shipping/rate_result');
 
	/** @var Inchoo_Shipping_Helper_Data $expressMaxProducts */
	$expressMaxWeight = Mage::helper('inchoo_shipping')->getExpressMaxWeight();
 
	$expressAvailable = true;
	foreach ($request->getAllItems() as $item) {
	    if ($item->getWeight() > $expressMaxWeight) {
		$expressAvailable = false;
	    }
	}
 
	if ($expressAvailable) {
	    $result->append($this->_getExpressRate());
	}
	$result->append($this->_getStandardRate());
 
	return $result;
}

As you can see, code is pretty straight forward: Weight of all products in cart are compared to value stored in config, which determines availability of our ‘express‘ rate. Our ‘standard‘ rate is always available. Each rate is added by passing Mage_Shipping_Model_Rate_Result_Method object to append() of our result object. And we get those rate objects by calling _getExpressRate() and _getStandardRate(), which are implemented as following:

protected function _getStandardRate()
{
    /** @var Mage_Shipping_Model_Rate_Result_Method $rate */
    $rate = Mage::getModel('shipping/rate_result_method');
 
    $rate->setCarrier($this->_code);
    $rate->setCarrierTitle($this->getConfigData('title'));
    $rate->setMethod('large');
    $rate->setMethodTitle('Standard delivery');
    $rate->setPrice(1.23);
    $rate->setCost(0);
 
    return $rate;
}

And that’s all that our class needs in order to work. We will finish up by adding admin configuration through system.xml. Here is an shortened version:

<config>
    <sections>
        <carriers>
            <groups>
                <inchoo_shipping translate="label">
                    ...
                    <fields>
                        <active translate="label">
                            ...
                        </active>
                        <title translate="label">
                            ...
                        </title>
                        <sallowspecific translate="label">
                            ...
                            <frontend_type>select</frontend_type>
                            <frontend_class>shipping-applicable-country</frontend_class>
                            <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
                            ...
                        </sallowspecific>
                        <specificcountry translate="label">
                            ...
                            <frontend_type>multiselect</frontend_type>
                            <source_model>adminhtml/system_config_source_country</source_model>
                            ...
                        </specificcountry>
                        <express_max_weight translate="label">
                            ...
                        </express_max_weight>
                    </fields>
                </inchoo_shipping>
            </groups>
        </carriers>
    </sections>
</config>

What is important to note here, is that active, title, sallowspecific and specificcountry are handled automatically by Magento, so besides adding this to admin, you aren’t required to do anything else. With others being self explanatory, there are only two options being interesting here. First one, sallowspecific, tells Magento should carrier be available for all countries, or only for once that are specified in specificcountry.

And that is all work required for our shipping method to appear on checkout step. This module has been written as an example for Magento CE 1.9.0.1 and can be downloaded here.

Note: This is a revamp of an article originally written in July 2009.

In case you still need some help regarding your checkout or would simply like to have your site improved, we can help you out by creating a detailed custom report based on our Magento Technical Audit. Feel free to drop us a line!

You made it all the way down here so you must have enjoyed this post! You may also like:

Making FedEx api show shipping estimate Toni Pap
Toni Pap, | 1

Making FedEx api show shipping estimate

Sorry, we can’t ship there Sasa Brankovic
Sasa Brankovic, | 12

Sorry, we can’t ship there

UPS API Quest Tomislav Bilic
, | 30

UPS API Quest

44 comments

  1.         <carriers>
                <inchoo_shipping>

    The carrier code/name shouldn’t contain the underscore character. I.e. it is better to use just inchoo, like the following.

            <carriers>
                <inchoo>
  2. Working fine on checkout/cart page and display proper total cart table.

    But when we go to PROCESSED TO CHECKOUT not display same total table in onepage checkout page.

    Grand total value display wrong.

  3. I want to create custom shipping module
    1) First thing will be in that shipping will be calculated on per Item wise not on the whole shopping cart item.
    2) There will be option of selecting the country in which the product will be shipped it can be different for each item added in the cart.
    3) There will be option of selecting the (Express Delivery & Standard delivery) again it can be different for each item added in the cart and every country’s (Express Delivery & Standard delivery) charges may vary.
    please help me on this i would be sincerely thankful to the all community members.

  4. Hi, Great article…. I’m trying this on Magento 1.9.3 but I cannot get it to show up on the admin Shipping Methods page nor in the frontend. Is like the extension wasn’t enabled. Can you help me with this?

  5. Can u just direct me where should i put the second n third code of this page…i mean under which folder should i paste it..is it under config.xml?

  6. I follow this tutorial…but it display shipping method block on checkout/cart page also.
    I am working with magento 1.9.2.4.
    And i don’t want to that shipping method block on checkout/cart page.
    It should be display on on checkout/onepage page.
    How can i do that…??

  7. Hi, If I need reporting one error generated while process my new module, how i can do??

  8. how to set specif country in system.xml tag?

    I need do not select in backend, only show USA for example on backend.
    – “Allow specific countries” set to yes
    – “Specific Countries” set to USA
    And disable edition

  9. can you plz tell me how we can configure this shipping method for all stores available in magento.

  10. pls pay attention: when you add configurable products to cart, this code get the weight two times

    ex.:

    cool-shirt -> configurable product weight = 135g
    |cool-shirt-red -> associated product weight = 135g
    in the total, it shows 270g, but we want just 135g (just the red cool shirt)

    it mess with the shipping price, if you calculate the weight-based shipping price

  11. Hello Stjepan,

    I have used this module in our site to apply custom shipping which is working fine but I have also enabled free shipping which is showing on cart page. On selecting free shipping, shipping price is not reflecting. Can you please help me to resolve this issue asap.

    Thanks

  12. i am new in magento and i follow your article and create a shipping method but problem is how to change $12.3 price in magento i changed in this line $rate->setPrice(12.3); not working. i want to change dynamicly shipping method price according to my custom table. pls.. give me any solution

  13. i used this source code for my project, but i must add
    public function isTrackingAvailable(){
    return true;
    }
    in Inchoo_Shipping_Model_Carrier , then Inchoo Shipping Carriers appear in Carrier.

  14. This would be a very helpful solution to me if I could restrict Customer Groups? Any ideas to implement this addition?
    This way I could charge a handling fee to only Dealers/Distributors who want us to ship under their FedEx account, while showing our cost if they wanted to ship under our FedEx account.
    I am also using WebShopApps Shipping Override. The issue I run into with the WSA extension is I can only show one. I would like to show Dealer Ground – $6.95 and Ground – $our cost + 6.95. Currently I have accomplished this by setting up ‘2-Day’ shipping for only Dealer/Distributor and assigning the $6.95 charge but I know this will confuse our Dealer/Distributor.
    Thanks,
    Ryan

  15. We want to provide the tracking details in the shipment tracking popup. We know we can use the getTrackingInfo function. Currently we are using it like this

    public function getTrackingInfo($tracking)
    {
    $track = Mage::getModel(‘shipping/tracking_result_status’);
    $track->setUrl($this->getConfigData(‘tracking_api’).$tracking)
    ->setTracking($tracking)
    ->setCarrierTitle($this->getConfigData(‘title’));
    return $track;
    }

    Can you please help us to know how we can add the details which we will retrieve from the tracking url ? Thanks

  16. Hi Stjepan Udovicic
    Nice and very easy article for custom shipping method ,
    Is it possible to have custom express shipping method only for selected product ,

    1. Hi again

      how can i use datepicker custome field for this custome shipping method , If the radio button is click the customer can chose the date of he wants the product to get delivery.

  17. Thanks Stjepan, nice Article. I have few confusions in calculating shipping. So my question is say I have a custom shipping module providing 2 services a) upto 100g LWH: 24cm/16.5cm/0.5cm cost: $1 b) > 100g LWH: 35.3cm/25cm/2.5cm cost: $2. So a single product <= 100g weight with defined dimensions cost say $1 shipping, so what it will cost if I am ordering same product with quantity of 10. I have few confusions how these shipping costs are calculated. Can you please help me and provide some guidance how should I approach regarding the same.

    Thanks and I would appreciate it.
    Vik

  18. Hi, great article. Is there a native way to also add specific region restriction for the custom shipping method?

  19. how to make a shipping method shown up, but not selectable and includes a description on why it is unselectable?

    1. Hi doniking,
      By default, Magento won’t display shipping methods that are not applicable to current order. Therefore, there is no easy way of doing so, unless you override this functionality.

  20. Good article, thanks for share with us.
    Is possible built a shipping method withou price, using only label: eg: (FOB – pay on delivery) ?
    I do not want to induce the client to think that shipping costs are $ 0.00

  21. Hi there,
    Thank you for the article. But does the list of carriers get stored in a database within Mage? Or does it pull from list of available shipping methods?
    Thank you.
    Maybray Digital

  22. Good article guys. Am I the only person who thinks that adding a custom shipping method should be built into the admin interface out of the box and we shouldn’t need to create a custom module?

  23. Does this module support adding multiple shipping methods or would I need to install the modules as many times are I want separate shipping methods?

    1. Hi Stephen, this module feature two shipping methods: express rate and standard rate. They are specified in getAllowedMethods() and in collectRates().

  24. What happened to the old Inchoo Shipping Method? This one doesn’t offer an option to add a price, what’s going on? Thank you for posting the smashing link Matthew, it makes much more sense.

    1. Hi Gen! We created this new module, since it is more ‘Magento way’ style of coding. However, both (old and new one) are written for educational purposes and are not meant to be used in production in current form.

  25. Its 2014 … Articles about creating custom shipping methods exist since 1.3.2.4. no need for Mr. Haworth to worry about ownership and his efame.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.