Creating a custom shipping method is very easy, you just need to know all the useful functions to be used inside the shipping method class to put in various configuration options. We will see in this blog many different use cases and functions to use in shipping methods.
So let’s start of by creating the shipping method. I have used a module for this tutorial named Excellence_Ship. You can create your own module and change class names accordingly.
Attached is source code of the module shown in this blog
Error... Unable to load download template. Search single-download-template.tpl in your plugin folder!
These are the primary areas where the shipping method shows up
To achieve this there are 3 things required
- config.xml entries
- system.xml entries
- Shipping Module
So lets start of with the steps.
Step1First thing when creating a shipping method is to decide the code for the shipping method. In the current module i am using the code for shipping method as “excellence”. Next we will create the system.xml file for admin entries.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
config
>
<
sections
>
<
carriers
>
<
groups
>
<
excellence
translate
=
"label"
module
=
"ship"
>
<
label
>Excellence Shipping Module</
label
>
<
frontend_type
>text</
frontend_type
>
<
sort_order
>99</
sort_order
>
<
show_in_default
>1</
show_in_default
>
<
show_in_website
>1</
show_in_website
>
<
show_in_store
>1</
show_in_store
>
<
fields
>
<
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
>1</
show_in_store
>
</
active
>
<
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
>
<
name
translate
=
"label"
>
<
label
>Method Name</
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
>
</
name
>
<
price
translate
=
"label"
>
<
label
>Price</
label
>
<
frontend_type
>text</
frontend_type
>
<
sort_order
>3</
sort_order
>
<
show_in_default
>1</
show_in_default
>
<
show_in_website
>1</
show_in_website
>
<
show_in_store
>0</
show_in_store
>
</
price
>
<
specificerrmsg
translate
=
"label"
>
<
label
>Displayed Error Message</
label
>
<
frontend_type
>textarea</
frontend_type
>
<
sort_order
>4</
sort_order
>
<
show_in_default
>1</
show_in_default
>
<
show_in_website
>1</
show_in_website
>
<
show_in_store
>1</
show_in_store
>
</
specificerrmsg
>
<
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
>
<
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
>
</
excellence
>
</
groups
>
</
carriers
>
</
sections
>
</
config
>
So, let me explain this. first we always have to create our shipping method inside
123<
sections
>
<
carriers
>
<
groups
>
As you can see the field have been created inside the ‘excellence’ tag which is our shipping method code. Next, we have created fields for active,title,name,price. There fields are required for every shipping method created. To know more details of system.xml entries please refer to this blog
Next, i have included to more fields sallowspecific and specificcountry. This is required, when you want your shipping method to available for only few countries. To implement this restriction in the frontend (i.e suppose if user chooses India as shipping country our shipping doesnt show) we don’t have to do any more programming. Just included these two fields in the xml and country based restrictions start working automatically. Of course, we have to setup the country restriction in System Configuration in admin.
Next, i have included specificerrmsg field. This field is used whenever there some error in our shipping method and we to show an error message to the user.Step2Next in our config.xml file of our module we add the below code directly inside the <config>
123456789101112<
default
>
<
carriers
>
<
excellence
>
<
active
>1</
active
>
<
model
>ship/carrier_excellence</
model
>
<
title
>Carrier Title</
title
>
<
name
>Method Name</
name
>
<
price
>5.00</
price
>
<
specificerrmsg
>This shipping method is currently unavailable. If you would like to ship using this shipping method, please contact us.</
specificerrmsg
>
</
excellence
>
</
carriers
>
</
default
>
As we know the <default> tag is used to assign default values to our system.xml fields created. So here we specify default values for active,title,name,price etc. The important field to notice here is the <model> which contains the path of our shipping model. This field is very important, or else the shipping method won’t show on frontend. After this step is over go to Admin -> System -> Configuration -> Shipping Methods and you should see your shipping method there with all the default values pre-filled.
Step3Now all we need to do is to create our shipping model class. The model class name is Excellence.php and is created Model/Carrier folder.
12345678910111213141516171819202122232425262728293031323334353637383940<?php
class
Excellence_Ship_Model_Carrier_Excellence
extends
Mage_Shipping_Model_Carrier_Abstract
implements
Mage_Shipping_Model_Carrier_Interface {
protected
$_code
=
'excellence'
;
public
function
collectRates(Mage_Shipping_Model_Rate_Request
$request
)
{
if
(!Mage::getStoreConfig(
'carriers/'
.
$this
->_code.
'/active'
)) {
return
false;
}
$handling
= Mage::getStoreConfig(
'carriers/'
.
$this
->_code.
'/handling'
);
$result
= Mage::getModel(
'shipping/rate_result'
);
$show
= true;
if
(
$show
){
// This if condition is just to demonstrate how to return success and error in shipping methods
$method
= Mage::getModel(
'shipping/rate_result_method'
);
$method
->setCarrier(
$this
->_code);
$method
->setMethod(
$this
->_code);
$method
->setCarrierTitle(
$this
->getConfigData(
'title'
));
$method
->setMethodTitle(
$this
->getConfigData(
'name'
));
$method
->setPrice(
$this
->getConfigData(
'price'
));
$method
->setCost(
$this
->getConfigData(
'price'
));
$result
->append(
$method
);
}
else
{
$error
= Mage::getModel(
'shipping/rate_result_error'
);
$error
->setCarrier(
$this
->_code);
$error
->setCarrierTitle(
$this
->getConfigData(
'name'
));
$error
->setErrorMessage(
$this
->getConfigData(
'specificerrmsg'
));
$result
->append(
$error
);
}
return
$result
;
}
public
function
getAllowedMethods()
{
return
array
(
'excellence'
=>
$this
->getConfigData(
'name'
));
}
}
The above code is for our shipping model. The code is very basic, let go line by line
1public
function
collectRates(Mage_Shipping_Model_Rate_Request
$request
)
This is the function which we need to implement. This function is called by magento for all the shipping methods to find out the shipping rates.
123if
(!Mage::getStoreConfig(
'carriers/'
.
$this
->_code.
'/active'
)) {
return
false;
}
This line simply checks if the shipping method is enabled from admin.
1$result
= Mage::getModel(
'shipping/rate_result'
);
here we create the result object, which is always returned from the shipping method collectRate function.
12345678$method
= Mage::getModel(
'shipping/rate_result_method'
);
$method
->setCarrier(
$this
->_code);
$method
->setMethod(
$this
->_code);
$method
->setCarrierTitle(
$this
->getConfigData(
'title'
));
$method
->setMethodTitle(
$this
->getConfigData(
'name'
));
$method
->setPrice(
$this
->getConfigData(
'price'
));
$method
->setCost(
$this
->getConfigData(
'price'
));
$result
->append(
$method
);
This code is used to return the shipping price. The code is quite clear.
12345$error
= Mage::getModel(
'shipping/rate_result_error'
);
$error
->setCarrier(
$this
->_code);
$error
->setCarrierTitle(
$this
->getConfigData(
'name'
));
$error
->setErrorMessage(
$this
->getConfigData(
'specificerrmsg'
));
$result
->append(
$error
);
The above code is used to return error message.
After this model is created, the shipping method should be working properly. It will show up on the checkout page and cart page.
Different Cases and Function to use inside shipping methodAll the function written below are to be implemented in the collectRates function only. I will write down function you can use to put your condition and calculate price, the price needs to be set in the result variable as shown above.
Shipping Price is Based On Destination Country,State and Zip Code1234567891011//Case1: Price Depends on Country,State and Pin Code
echo
$destCountry
=
$request
->getDestCountryId().
': Dest Country<br/>'
;
echo
$destRegion
=
$request
->getDestRegionId().
': Dest Region<br/>'
;
echo
$destRegionCode
=
$request
->getDestRegionCode().
': Dest Region Code<br/>'
;
print_r(
$destStreet
=
$request
->getDestStreet());
echo
': Dest Street<br/>'
;
echo
$destCity
=
$request
->getDestCity().
': Dest City<br/>'
;
echo
$destPostcode
=
$request
->getDestPostcode().
': Dest Postcode<br/>'
;
echo
$country_id
=
$request
->getCountryId().
': Package Source Country ID<br/>'
;
echo
$region_id
=
$request
->getRegionId().
': Package Source Region ID<br/>'
;
echo
$city
=
$request
->getCity().
': Package Source City<br/>'
;
echo
$postcode
=
$request
->getPostcode().
': Package Source Post Code<br/>'
;
Use the above function to put conditions and set price based on your business logic.
Shipping Price is based on total order cost or weight123456//Case2: Price Depends on Total Order Value or Weight
echo
$packageValue
=
$request
->getPackageValue().
': Dest Package Value<br/>'
;
echo
$packageValueDiscout
=
$request
->getPackageValueWithDiscount().
': Dest Package Value After Discount<br/>'
;
echo
$packageWeight
=
$request
->getPackageWeight().
': Package Weight<br/>'
;
echo
$packageQty
=
$request
->getPackageQty().
': Package Quantity <br/>'
;
echo
$packageCurrency
=
$request
->getPackageCurrency().
': Package Currency <br/>'
;
Shipping Price Depends on Dimensions
1234//Case3: Price Depends on order dimensions
echo
$packageheight
=
$request
->getPackageHeight() .
': Package height <br/>'
;
echo
$request
->getPackageWeight().
': Package Width <br/>'
;
echo
$request
->getPackageDepth().
': Package Depth <br/>'
;
Shipping Price Depends on Product Attributes
As example, suppose we want to have shipping price different for each product. So for this, we create a product attribute called ‘shipping_price’ and on each product edit page in admin we assign the shipping price of the product. Now in our shipping method we need to access this shipping_price again and show the total shipping cost.123456789101112131415161718192021222324//Case4: Price based on product attribute
if
(
$request
->getAllItems()) {
foreach
(
$request
->getAllItems()
as
$item
) {
if
(
$item
->getProduct()->isVirtual() ||
$item
->getParentItem()) {
continue
;
}
if
(
$item
->getHasChildren() &&
$item
->isShipSeparately()) {
foreach
(
$item
->getChildren()
as
$child
) {
if
(
$child
->getFreeShipping() && !
$child
->getProduct()->isVirtual()) {
$product_id
=
$child
->getProductId();
$productObj
= Mage::getModel(
'catalog/product'
)->load(
$product_id
);
$ship_price
=
$productObj
->getData(
'shipping_price'
);
//our shipping attribute code
$price
+= (float)
$ship_price
;
}
}
}
else
{
$product_id
=
$item
->getProductId();
$productObj
= Mage::getModel(
'catalog/product'
)->load(
$product_id
);
$ship_price
=
$productObj
->getData(
'shipping_price'
);
//our shipping attribute code
$price
+= (float)
$ship_price
;
}
}
}
Shipping Price Based on Configurable Product Option Selected
Example, suppose on our website we have configurable product with Size as the drop down attribute. If user selected selects Size as Small the shipping cost is 15$, Medium is 20$ and Large is 25$.12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455//Case5: Shipping option based configurable product option
if
(
$request
->getAllItems()) {
foreach
(
$request
->getAllItems()
as
$item
) {
if
(
$item
->getProduct()->isVirtual() ||
$item
->getParentItem()) {
continue
;
}
if
(
$item
->getHasChildren() &&
$item
->isShipSeparately()) {
foreach
(
$item
->getChildren()
as
$child
) {
if
(
$child
->getFreeShipping() && !
$child
->getProduct()->isVirtual()) {
$product_id
=
$child
->getProductId();
$value
=
$item
->getOptionByCode(
'info_buyRequest'
)->getValue();
$params
= unserialize(
$value
);
$attributeObj
= Mage::getModel(
'eav/config'
)->getAttribute(Mage_Catalog_Model_Product::ENTITY,
'shirt_size'
);
// our configurable attribute
$attribute_id
=
$attributeObj
->getAttributeId();
$attribute_selected
=
$params
[
'super_attribute'
][
$attribute_id
];
$label
=
''
;
foreach
(
$attributeObj
->getSource()->getAllOptions(false)
as
$option
){
if
(
$option
[
'value'
] ==
$attribute_selected
){
$label
=
$option
[
'label'
];
}
}
if
(
$label
=
'Small'
){
$price
+= 15;
}
else
if
(
$label
=
'Medium'
){
$price
+= 20;
}
else
if
(
$label
=
'Large'
){
$price
+= 22;
}
}
}
}
else
{
$product_id
=
$item
->getProductId();
$value
=
$item
->getOptionByCode(
'info_buyRequest'
)->getValue();
$params
= unserialize(
$value
);
$attributeObj
= Mage::getModel(
'eav/config'
)->getAttribute(Mage_Catalog_Model_Product::ENTITY,
'shirt_size'
);
// our configurable attribute
$attribute_id
=
$attributeObj
->getAttributeId();
$attribute_selected
=
$params
[
'super_attribute'
][
$attribute_id
];
$label
=
''
;
foreach
(
$attributeObj
->getSource()->getAllOptions(false)
as
$option
){
if
(
$option
[
'value'
] ==
$attribute_selected
){
$label
=
$option
[
'label'
];
}
}
if
(
$label
=
'Small'
){
$price
+= 15;
}
else
if
(
$label
=
'Medium'
){
$price
+= 20;
}
else
if
(
$label
=
'Large'
){
$price
+= 22;
}
}
}
}
Shipping Price based on Product Custom Options
Example, suppose we have custom drop down options created for a simple product. The Custom Option is Delivery and values are Express,Normal. If user selects Express when adding product, the shipping cost is 50$ and for normal the cost is 10$.1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859//Case6: Price based on custom options
if
(
$request
->getAllItems()) {
foreach
(
$request
->getAllItems()
as
$item
) {
if
(
$item
->getProduct()->isVirtual() ||
$item
->getParentItem()) {
continue
;
}
if
(
$item
->getHasChildren() &&
$item
->isShipSeparately()) {
foreach
(
$item
->getChildren()
as
$child
) {
if
(
$child
->getFreeShipping() && !
$child
->getProduct()->isVirtual()) {
$product_id
=
$child
->getProductId();
$value
=
$item
->getOptionByCode(
'info_buyRequest'
)->getValue();
$params
= unserialize(
$value
);
$options_select
=
$params
[
'options'
];
$product
= Mage::getModel(
'catalog/product'
)->load(
$product_id
);
$options
=
$product
->getOptions();
foreach
(
$options
as
$option
) {
if
(
$option
->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) {
$option_id
=
$option
->getId();
foreach
(
$option
->getValues()
as
$value
) {
if
(
$value
->getId() ==
$options_select
[
$option_id
]){
if
(
$value
->getTitle() ==
'Express'
){
$price
+= 50;
}
else
if
(
$value
->getTitle() ==
'Normal'
){
$price
+= 10;
}
}
}
}
}
}
}
}
else
{
$product_id
=
$item
->getProductId();
$value
=
$item
->getOptionByCode(
'info_buyRequest'
)->getValue();
$params
= unserialize(
$value
);
$options_select
=
$params
[
'options'
];
$product
= Mage::getModel(
'catalog/product'
)->load(
$product_id
);
$options
=
$product
->getOptions();
foreach
(
$options
as
$option
) {
if
(
$option
->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) {
$option_id
=
$option
->getId();
foreach
(
$option
->getValues()
as
$value
) {
if
(
$value
->getId() ==
$options_select
[
$option_id
]){
if
(
$value
->getTitle() ==
'Express'
){
$price
+= 50;
}
else
if
(
$value
->getTitle() ==
'Normal'
){
$price
+= 10;
}
}
}
}
}
}
}
}