In this tutorial, we will use Magento’s powerful shipping-method code abstraction to create a shipping carrier. We will create three shipping methods that provide a fixed shipping price, allow for free shipping promotions, define logic based on an item’s weight and, finally, make it all configurable in the admin panel.

We will cover the following:

Extend the abstract shipping class and implement the required methods.

Make the shipping method configurable in Magento’s admin panel.

Work with promotions to allow for free shipping.

Allow tracking codes to be set against an order.

Before We Start

This tutorial assumes that you are familiar with how to create a Magento module. If you are not, please first refer to an earlier tutorial in this series, “The Basics of Creating a Magento Module1.” To begin, you will need a Community or Enterprise installation of Magento, either locally or on a server that you are able to access.

The logic we will implement in this tutorial could be client-specific, so we will implement our module as a “local module” and, therefore, create it in app/code/local. Let’s start by creating the following file structure:

Notice the dependency on the shipping module. This ensures that our SmashingMagazine MyCarrier module will load after the Mage Shipping module, and it will throw an error if the Mage Shipping module has been disabled.

Carriers, Methods, Requests And Results

Before continuing, we should understand the terminology that Magento uses throughout its shipping abstraction. A “carrier” represents a shipping carrier in the sense you would expect (DPD, FedEx, etc.). Each carrier has one or many shipping methods, which contain the carrier code, the carrier title, the method code, the method title, a price to be paid by the customer and a cost of shipping to the retailer (optional).

During the checkout process, Magento creates a shipping-rate “request” object that contains all of the shipping information. The request can be used to determine which rates apply. For example, an “express” shipping method might not apply to orders under $10. All applicable rates are then “appended” to a shipping-rate “result” object, which generates a list of methods for the customer to choose from.

The following list names these concepts defined above, along with their representation as Magento classes:

RequestMage_Shipping_Model_Rate_Request

ResultMage_Shipping_Model_Rate_Result

MethodMage_Shipping_Model_Rate_Result_Method

Carrier
Any class that extends the abstract class Mage_Shipping_Model_Carrier_Abstract and implements the interface Mage_Shipping_Model_Carrier_Interface

Module Configuration

<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<SmashingMagazine_MyCarrier>
<module>0.0.1</module>
</SmashingMagazine_MyCarrier>
</modules>
<global>
<models>
<smashingmagazine_mycarrier>
<class>SmashingMagazine_MyCarrier_Model</class>
</smashingmagazine_mycarrier>
</models>
</global>
<!-- Default configuration -->
<default>
<carriers>
<smashingmagazine_mycarrier>
<active>1</active>
<!--
This configuration should not be made visible
to the administrator, because it specifies
the model to be used for this carrier.
-->
<model>smashingmagazine_mycarrier/carrier</model>
<!--
The title as referenced in the carrier class
-->
<title>Smashing Magazine Carrier</title>
<!--
The sort order specifies the position that
this carrier appears relative to the other
carriers available in checkout.
-->
<sort_order>10</sort_order>
<!--
Out of the box, Magento offers shipping
carriers the ability to restrict themselves
to specific countries. For this configuration
option, 0 means allow all countries available,
and 1 means allow all countries specified
in the country list that we will add later
in system.xml
-->
<sallowspecific>0</sallowspecific>
</smashingmagazine_mycarrier>
</carriers>
</default>
</config>

This default configuration “registers” the model we have just created as a shipping carrier. As you may know, Magento merges all of its configuration XML together and caches the result (if the cache is enabled). When a customer loads the shipping-method list, Magento loops through all of the carriers in the carriers node of the configuration and loads the shipping methods from the models determined by the “active” carriers.

We should now be able to see our shipping method in the checkout.

Making It Configurable

We have already specified the default configuration for this module. So, let’s make our module configurable in the admin panel by copying the following into app/code/local/SmashingMagazine/etc/system.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
<sections>
<carriers translate="label" module="shipping">
<groups>
<smashingmagazine_mycarrier translate="label">
<label>Smashing Magazine Carrier</label>
<frontend_type>text</frontend_type>
<sort_order>2</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<fields>
<!--
The following fields are available
to modify in the admin panel.
The values are saved in the
database.
This shipping carrier abstract checks
this value to determine whether
the carrier should be shown.
-->
<active translate="label">
<label>Enabled</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>1</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</active>
<!--
This value can be used to specify a
custom title for our method.
-->
<title translate="label">
<label>Title</label>
<frontend_type>text</frontend_type>
<sort_order>2</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</title>
<!--
The sort order is used in Magento
to determine what order the carrier
will appear in relative to the
other carriers available.
-->
<sort_order translate="label">
<label>Sort Order</label>
<frontend_type>text</frontend_type>
<sort_order>100</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</sort_order>
<!--
This value is used to specify whether
the carrier is available only for
specific countries or all countries
available in the current Magento
installation.
-->
<sallowspecific translate="label">
<label>Ship to Applicable Countries</label>
<frontend_type>select</frontend_type>
<sort_order>90</sort_order>
<frontend_class>shipping-applicable-country</frontend_class>
<source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</sallowspecific>
<!--
If 'specific countries' is chosen
in the previous option, then this field
allows the administrator to specify
which specific countries this carrier
should be available for.
-->
<specificcountry translate="label">
<label>Ship to Specific Countries</label>
<frontend_type>multiselect</frontend_type>
<sort_order>91</sort_order>
<source_model>adminhtml/system_config_source_country</source_model>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<can_be_empty>1</can_be_empty>
</specificcountry>
</fields>
</smashingmagazine_mycarrier>
</groups>
</carriers>
</sections>
</config>

These fields are visible in the admin panel by navigating to System → Configuration → Shipping Method → Smashing Magazine Carrier.

Using Multiple Shipping Methods

Express Shipping

So far, we have added a standard shipping method for the price of $9.99. However, the customer may wish to pay more to receive their order faster. The following code creates a shipping rate with a higher price and different shipping code:

To make this shipping rate appear next to the standard rate that we created earlier, we will need to modify the code in the collectRates method to append the new rate. Add the following before the return statement:

$result->append($this->_getExpressShippingRate());

Finally, add the shipping method to the allowed methods array in getAllowedMethods:

Free Shipping

Many websites offer free shipping when a customer spends over a certain amount or satisfies certain conditions. We need to be able to do the same here. In Magento, you can set up a “Shopping Cart Rule.” With it, you can specify a set of conditions and define actions if those conditions are met; one of those actions is free shipping.

If free shipping is available for a customer, then the request object will populated with is_free_shipping set to 1. We need to check for and handle this possibility in our shipping method. Add the following before the return statement in the collectRates method:

Taking It A Bit Further

Tracking Deliveries

Tracking numbers may be added to shipments through either the admin panel or an API. But to make our shipping methods visible in the admin panel, we will have to overwrite the isTrackingAvailable method in the abstract to return true.

Add the following method to the end of SmashingMagazine_MyCarrier_Model_Carrier.

public function isTrackingAvailable()
{
return true;
}

You should now see the shipping carriers and methods available in the delivery courier drop-down menu when you try to place a shipment in the admin panel.

Using the Weight

Earlier, we added a more expensive express shipping method. But heavier items that require complex shipping arrangements might not be available for next-day delivery. We can check for this using the weight attribute of the request object by wrapping the code that appends the shipping method to the shipping result:

Notice that we have added a reference to the configuration. To make this appear in the admin panel, we need to add the following XML to the app/code/local/SmashingMagazine/MyCarrier/etc/system.xml file in the fields node:

Summary

With a relatively small amount of code, we have been able to define our own shipping logic that integrates with checkout, the admin panel and even the shopping-cart promotions. You can learn much more about creating shipping modules in Magento by looking at the examples in the core files — namely, Mage_Usa and Mage_Shipping.

Pete

Thanks for the article, I’ll give it a go. I’m assuming it works for Magento 1.8 given the date of the article.
@Ze – yes it’s a complex product, but it’s also dominating large-scale online businesses for a reason. It’s not designed for small shops, unsophisticated users, or hackers who are usually the first to criticise it. I’d change your advice to ‘don’t use Magento if you’re going to try and get it built on the cheap in the third world’. Magento development really shows up the shoddy, poor quality, low-skilled work or many of these outfits in places like India and Russia, creating disastrous implementations. If you’re going to get Magento, you need to pay top dollar for its development in a first world country with quality coders, which makes it expensive, but does cut out the fly-by-night 3rd world developer market and means the user community is a lot more sophisticated.
My only gripe with Magento is its processing load – the EAV data model and total OOP approach really makes most servers grind. In my mind, it really only runs effectively on a VPS which is a big bit of overhead to handle.

Dean Podgornik

Joren

Interesting article. One question: don’t you have to omit the coma in arrays at the last item? You have:
public function getAllowedMethods()
{
return array(
‘standard’ => ‘Standard’,
‘express’ => ‘Express’,
‘free_shipping’ => ‘Free Shipping’,
);
}
Is the comma at the end of free_shipping supposed to be there? You do that for the same method elsewhere in this post.

Matthew Haworth

@Joren: Thank you. You don’t ‘have’ to omit the comma, however I like to comply with the Zend Coding Standards for Magento.

@Ze: I agree that Magento is complex, however this complexity comes with the features and customisability that most of its competitors can’t compete with. I have tried alternatives and I personally feel that Magento offers a lot more in terms of scalability.

Golam Faroque

I want to add an input box in the shipping method block in this carrier to take UPS account number from customer. I need to show the account number in order details page in magento admin panel. Can you/anyone please guide me on this? I am running out of time so can you please respond asap?

Abhimand

I am trying to calculate shipping price based on dimensions, post code and type of parcel(regular/express). I am able to calculate that using AUSPOST api but the problem is to save the shipping rate outside of the collectRates method in magento. If I do this…

Chris

A great article that actually makes sense unlike most of the others I’ve found on shipping methods.

I’ve successfully implemented your code (called the carrier “eashipping” with a method of “eashipping”) and can see it active if I call getActiveCarriers()

I’m creating a quote programatically (pulling in orders from another source) and the quote creates successfully but the shipping cost is always zero, even when I try to overide the cost within the main code.

Alex Evans

Fernando

Hi Matthew, I’m writing you from Argentina, sorry for my english.
Realy your post is very usefull, I will try to implement this weekend ;-)
I have a question, “OCA” is the name of one carrier provider in Argentina, they provide a webservice interface to access and query the cost to shipping from certain Postal Code to another, can access a guide to know where is it your merchansis an anothers posibilitys too.
My question is, which php file I have to write the calls to those webservices to implement the third-party service? is in ../Carrier.php ??

Girish

Hi Matthew,
Thanks for the great article. I used this in my code. One more thing I need here is one check-box along with the delivery charges radio button. Is it possible.? Please suggest me how can i do this.

Renish Khunt

josh

We are trying to set up our furniture store using Magento and are looking for a way to have up to 4 shipping choices per product ie: Free curbside delivery, Basic inside delivery +$99, White Glove inside delivery +$199, etc. What is the best/easiest way of doing this?
(we currently have this shipping data and its associated product info in a db)

Adam Allen

Hey, i followed your tutorial and it worked brilliantly so thank you fro that. One ting though how would i hide these shipping rates using the unset method, As i dont want these rates to be avaible on certain products. I have the code that checks for through the produtcs so thats no trouble and im using

unset($_shippingRateGroups[“flatrate”]);

to disable the standard magento shipping. How would i be able to disable yours??

Paul Simmons

We nice tutorial for explaining custom shipping methods. We have developed a shipping extension to let store owners restrict shipping based on product type, locations etc.
Let me me know how do you rate this one – fmeaddons.com/magento/restrict-shipping-extension.html

Manjunth

Thanks for the article, I used it in my code, But I am not getting the option in checkout, Already I have UPS and Canada Post Shipping methods, I can get these two to select while checkout, but I am not getting above “Standard” and “Express” shipping method.
I checked backend Config too, Its enabled.

How can I display It in front-end while Checkout option along with other shipping methods.?

steve

great tutorial works a treat – it would be good to know how the shipping costs (standard and express delivery) could be managed in the admin by the client. I can some config settings but nowhere to manage the actually price.

Ryan TeGantvoort

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.

Jitendra

Leave a Comment

Yay! You've decided to leave a comment. That's fantastic! Please keep in mind that comments are moderated and rel="nofollow" is in use. So, please do not use a spammy keyword or a domain as your name, or else it will be deleted. Let's have a personal and meaningful conversation instead. Thanks for dropping by!

Subscribe to our email newsletter for useful tips and valuable resources, sent out every second Tuesday.

It's finally here. Smashing Book #5, our new book on real-life responsive design. With front-end techniques and patterns from actual projects, it's a playbook to master all the tricky facets and hurdles of responsive design. Get the book.Free shipping.

Fixing RWD issues can be quite easy — once you understand exactly why they come up. The Mobile Web Handbook will help you understand technical issues on mobile and how to deal with them effectively.

Hungry for more content? Over 60 eBooks are waiting to be discovered in our lovely Smashing Library. And guess what? You can watch Smashing Conference talks there, too.