Table of Contents

Introduction

This tutorial is similar to Creation of Payment Method module, and differs the most in adapter model.

Each shipping method can be done as separate module or few methods can be combined in same module if they share functionality or could be used together.

Our new module will be called NewModule.

Replace all instances of ‘NewModule’ with name of your module and ‘newmodule’ with simplified code, that contains only alphanumeric characters and underscore.

To make this tutorial most concise, it’s implied that mentioned folders will be created when needed.

Make sure that app/code/local is in PHP‘s include_path. To do so, execute the following code, either in a shell or inside a web-accessible PHP file:

  1. <?=get_include_path();?>

Make sure you put this code somewhere after Magento was loaded, at the bottom of index.php for example, because Magento might modify the include_path on its own to fit its needs and dynamically attribute locations.

If you are using configuration cache, don’t forget to clean it after modifying config xml files.

Configuration

Create app/code/local/Mage/NewModule/etc/config.xml:

  1. <?xml version="1.0"?>
  2. <config>
  3.   <modules>
  4. <!-- declare module's version information -->
  5.     <Mage_NewModule>
  6. <!-- this version number will be used for database upgrades -->
  7.       <version>0.1.0</version>
  8.     </Mage_NewModule>
  9.   </modules>
  10.  
  11.   <global>
  12. <!-- declare model group for new module -->
  13.     <models>
  14. <!-- model group alias to be used in Mage::getModel() -->
  15.       <newmodule>
  16. <!-- base class name for the model group -->
  17.         <class>Mage_NewModule_Model</class>
  18.       </newmodule>
  19.     </models>
  20.  
  21. <!-- declare resource setup for new module -->
  22.     <resources>
  23. <!-- resource identifier -->
  24.       <newmodule_setup>
  25. <!-- specify that this resource is a setup resource and used for upgrades -->
  26.         <setup>
  27. <!-- which module to look for install/upgrade files in -->
  28.           <module>Mage_NewModule</module>
  29.         </setup>
  30. <!-- specify database connection for this resource -->
  31.         <connection>
  32. <!-- do not create new connection, use predefined core setup connection -->
  33.           <use>core_setup</use>
  34.         </connection>
  35.       </newmodule_setup>
  36.     </resources>
  37.   </global>
  38. </config>

Edit app/etc/modules/Mage_All.xml:

  1. <config>
  2. <!-- ... -->
  3.   <modules>
  4. <!-- ... -->
  5. <!-- declare Mage_NewModule module -->
  6.     <Mage_NewModule>
  7.       <active>true</active>
  8.       <codePool>local</codePool>
  9.     </Mage_NewModule>
  10. <!-- ... -->
  11.   </modules>
  12. <!-- ... -->
  13. </config>

Now application is aware of the module, but nothing will happen until we’ll create model logic.

Adapter model

Note: ShippingMethod name is arbitrary and is up to your decision.

Create app/code/local/Mage/NewModule/Model/Carrier/ShippingMethod.php:

  1. <?php
  2.  
  3. /**
  4. * Our test shipping method module adapter
  5. */
  6. class Mage_NewModule_Model_Carrier_ShippingMethod extends Mage_Shipping_Model_Carrier_Abstract
  7. {
  8.   /**
  9.    * unique internal shipping method identifier
  10.    *
  11.    * @var string [a-z0-9_]
  12.    */
  13.   protected $_code = 'newmodule';
  14.  
  15.     /**
  16.      * Collect rates for this shipping method based on information in $request
  17.      *
  18.      * @param Mage_Shipping_Model_Rate_Request $data
  19.      * @return Mage_Shipping_Model_Rate_Result
  20.      */
  21.   public function collectRates(Mage_Shipping_Model_Rate_Request $request)
  22.   {
  23.     // skip if not enabled
  24.     if (!Mage::getStoreConfig('carriers/'.$this->_code.'/active')) {
  25.         return false;
  26.     }
  27.  
  28.     /**
  29.      * here we are retrieving shipping rates from external service
  30.      * or using internal logic to calculate the rate from $request
  31.      * you can see an example in Mage_Usa_Model_Shipping_Carrier_Ups::setRequest()
  32.      */
  33.  
  34.     // get necessary configuration values
  35.     $handling = Mage::getStoreConfig('carriers/'.$this->_code.'/handling');
  36.  
  37.     // this object will be returned as result of this method
  38.     // containing all the shipping rates of this method
  39.     $result = Mage::getModel('shipping/rate_result');
  40.  
  41.     // $response is an array that we have
  42.     foreach ($response as $rMethod) {
  43.       // create new instance of method rate
  44.       $method = Mage::getModel('shipping/rate_result_method');
  45.  
  46.       // record carrier information
  47.       $method->setCarrier($this->_code);
  48.       $method->setCarrierTitle(Mage::getStoreConfig('carriers/'.$this->_code.'/title'));
  49.  
  50.       // record method information
  51.       $method->setMethod($rMethod['code']);
  52.       $method->setMethodTitle($rMethod['title']);
  53.  
  54.       // rate cost is optional property to record how much it costs to vendor to ship
  55.       $method->setCost($rMethod['amount']);
  56.  
  57.       // in our example handling is fixed amount that is added to cost
  58.       // to receive price the customer will pay for shipping method.
  59.       // it could be as well percentage:
  60.       /// $method->setPrice($rMethod['amount']*$handling/100);
  61.       $method->setPrice($rMethod['amount']+$handling);
  62.  
  63.       // add this rate to the result
  64.       $result->append($method);
  65.     }
  66.  
  67.     return $result;
  68.   }
  69. }

Now that we have the model let’s give admin a way to configure it and also make checkout process aware of this method.

Admin Configuration Implementation

In this step, we need to tell Magento how to display our module in the Configuration section of the administrative panel. In order to do so, we must create app/code/local/Mage/NewModule/etc/system.xml and make it look something like this

  1. <?xml version="1.0"?>
  2. <config>
  3.    <sections>
  4.     <carriers>
  5.         <groups>
  6.             <carrier_name translate="label" module="shipping">
  7.                 <label>Carrier Name</label>
  8.                 <frontend_type>text</frontend_type>
  9.                 <sort_order>13</sort_order>
  10.                 <show_in_default>1</show_in_default>
  11.                 <show_in_website>1</show_in_website>
  12.                 <show_in_store>1</show_in_store>
  13.                    <fields>
  14.                       <account translate="label">
  15.                             <label>Account number</label>
  16.                             <frontend_type>text</frontend_type>
  17.                             <sort_order>7</sort_order>
  18.                             <show_in_default>1</show_in_default>
  19.                             <show_in_website>1</show_in_website>
  20.                             <show_in_store>1</show_in_store>
  21.                         </account>
  22.                         <active translate="label">
  23.                             <label>Enabled</label>
  24.                             <frontend_type>select</frontend_type>
  25.                             <source_model>adminhtml/system_config_source_yesno</source_model>
  26.                             <sort_order>1</sort_order>
  27.                             <show_in_default>1</show_in_default>
  28.                             <show_in_website>1</show_in_website>
  29.                             <show_in_store>1</show_in_store>
  30.                         </active>
  31.                         <contentdesc translate="label">
  32.                             <label>Package Description</label>
  33.                             <frontend_type>text</frontend_type>
  34.                             <sort_order>12</sort_order>
  35.                             <show_in_default>1</show_in_default>
  36.                             <show_in_website>1</show_in_website>
  37.                             <show_in_store>1</show_in_store>
  38.                         </contentdesc>
  39.                         <!--
  40.                         If the free_shipping_enable flag enable, the system will check free_shipping_subtotal to give free shipping
  41.                         otherwise will use shopping cart price rule behaviour
  42.                         -->
  43.                         <free_shipping_enable translate="label">
  44.                             <label>Free shipping with minimum order amount</label>
  45.                             <frontend_type>select</frontend_type>
  46.                             <source_model>adminhtml/system_config_source_enabledisable</source_model>
  47.                             <sort_order>21</sort_order>
  48.                             <show_in_default>1</show_in_default>
  49.                             <show_in_website>1</show_in_website>
  50.                             <show_in_store>1</show_in_store>
  51.                         </free_shipping_enable>
  52.                         <free_shipping_subtotal translate="label">
  53.                             <label>Minimum order amount for free shipping</label>
  54.                             <frontend_type>text</frontend_type>
  55.                             <sort_order>22</sort_order>
  56.                             <show_in_default>1</show_in_default>
  57.                             <show_in_website>1</show_in_website>
  58.                             <show_in_store>1</show_in_store>
  59.                         </free_shipping_subtotal>
  60.                         <dutiable translate="label">
  61.                             <label>Shipment Dutiable</label>
  62.                             <frontend_type>select</frontend_type>
  63.                             <source_model>adminhtml/system_config_source_yesno</source_model>
  64.                             <sort_order>13</sort_order>
  65.                             <show_in_default>1</show_in_default>
  66.                             <show_in_website>1</show_in_website>
  67.                             <show_in_store>1</show_in_store>
  68.                         </dutiable>
  69.                         <gateway_url translate="label">
  70.                             <label>Gateway URL</label>
  71.                             <frontend_type>text</frontend_type>
  72.                             <sort_order>2</sort_order>
  73.                             <show_in_default>1</show_in_default>
  74.                             <show_in_website>1</show_in_website>
  75.                             <show_in_store>1</show_in_store>
  76.                         </gateway_url>
  77.                         <handling_type translate="label">
  78.                             <label>Calculate Handling Fee</label>
  79.                             <frontend_type>select</frontend_type>
  80.                             <source_model>shipping/source_handlingType</source_model>
  81.                             <sort_order>10</sort_order>
  82.                             <show_in_default>1</show_in_default>
  83.                             <show_in_website>1</show_in_website>
  84.                             <show_in_store>0</show_in_store>
  85.                         </handling_type>
  86.                         <handling_action translate="label">
  87.                             <label>Handling Applied</label>
  88.                             <frontend_type>select</frontend_type>
  89.                             <source_model>shipping/source_handlingAction</source_model>
  90.                             <sort_order>11</sort_order>
  91.                             <show_in_default>1</show_in_default>
  92.                             <show_in_website>1</show_in_website>
  93.                             <show_in_store>0</show_in_store>
  94.                         </handling_action>
  95.                         <handling_fee translate="label">
  96.                             <label>Handling fee</label>
  97.                             <frontend_type>text</frontend_type>
  98.                             <sort_order>12</sort_order>
  99.                             <show_in_default>1</show_in_default>
  100.                             <show_in_website>1</show_in_website>
  101.                             <show_in_store>1</show_in_store>
  102.                         </handling_fee>
  103.                         <max_package_weight translate="label">
  104.                             <label>Maximum Package Weight (Please consult your shipping carrier for maximum supported shipping weight)</label>
  105.                             <frontend_type>text</frontend_type>
  106.                             <sort_order>13</sort_order>
  107.                             <show_in_default>1</show_in_default>
  108.                             <show_in_website>1</show_in_website>
  109.                             <show_in_store>1</show_in_store>
  110.                         </max_package_weight>
  111.                         <id translate="label">
  112.                             <label>Access ID</label>
  113.                             <frontend_type>text</frontend_type>
  114.                             <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
  115.                             <sort_order>5</sort_order>
  116.                             <show_in_default>1</show_in_default>
  117.                             <show_in_website>1</show_in_website>
  118.                             <show_in_store>1</show_in_store>
  119.                         </id>
  120.                         <password translate="label">
  121.                             <label>Password</label>
  122.                             <frontend_type>text</frontend_type>
  123.                             <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
  124.                             <sort_order>6</sort_order>
  125.                             <show_in_default>1</show_in_default>
  126.                             <show_in_website>1</show_in_website>
  127.                             <show_in_store>1</show_in_store>
  128.                         </password>
  129.                         <shipping_intlkey translate="label">
  130.                             <label>Shipping key (International)</label>
  131.                             <frontend_type>text</frontend_type>
  132.                             <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
  133.                             <sort_order>8</sort_order>
  134.                             <show_in_default>1</show_in_default>
  135.                             <show_in_website>1</show_in_website>
  136.                             <show_in_store>1</show_in_store>
  137.                         </shipping_intlkey>
  138.                         <shipping_key translate="label">
  139.                             <label>Shipping key</label>
  140.                             <frontend_type>text</frontend_type>
  141.                             <backend_model>adminhtml/system_config_backend_encrypted</backend_model>
  142.                             <sort_order>8</sort_order>
  143.                             <show_in_default>1</show_in_default>
  144.                             <show_in_website>1</show_in_website>
  145.                             <show_in_store>1</show_in_store>
  146.                         </shipping_key>
  147.                         <sort_order translate="label">
  148.                             <label>Sort order</label>
  149.                             <frontend_type>text</frontend_type>
  150.                             <sort_order>100</sort_order>
  151.                             <show_in_default>1</show_in_default>
  152.                             <show_in_website>1</show_in_website>
  153.                             <show_in_store>1</show_in_store>
  154.                         </sort_order>
  155.                         <title translate="label">
  156.                             <label>Title</label>
  157.                             <frontend_type>text</frontend_type>
  158.                             <sort_order>2</sort_order>
  159.                             <show_in_default>1</show_in_default>
  160.                             <show_in_website>1</show_in_website>
  161.                             <show_in_store>1</show_in_store>
  162.                         </title>
  163.                         <sallowspecific translate="label">
  164.                             <label>Ship to applicable countries</label>
  165.                             <frontend_type>select</frontend_type>
  166.                             <sort_order>90</sort_order>
  167.                             <frontend_class>shipping-applicable-country</frontend_class>
  168.                             <source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
  169.                             <show_in_default>1</show_in_default>
  170.                             <show_in_website>1</show_in_website>
  171.                             <show_in_store>1</show_in_store>
  172.                         </sallowspecific>
  173.                         <specificcountry translate="label">
  174.                             <label>Ship to Specific countries</label>
  175.                             <frontend_type>multiselect</frontend_type>
  176.                             <sort_order>91</sort_order>
  177.                             <source_model>adminhtml/system_config_source_country</source_model>
  178.                             <show_in_default>1</show_in_default>
  179.                             <show_in_website>1</show_in_website>
  180.                             <show_in_store>1</show_in_store>
  181.                         </specificcountry>
  182.                         <showmethod translate="label">
  183.                             <label>Show method if not applicable</label>
  184.                             <frontend_type>select</frontend_type>
  185.                             <sort_order>92</sort_order>
  186.                             <source_model>adminhtml/system_config_source_yesno</source_model>
  187.                             <show_in_default>1</show_in_default>
  188.                             <show_in_website>1</show_in_website>
  189.                             <show_in_store>1</show_in_store>
  190.                         </showmethod>
  191.                         <specificerrmsg translate="label">
  192.                             <label>Displayed Error Message</label>
  193.                             <frontend_type>textarea</frontend_type>
  194.                             <sort_order>80</sort_order>
  195.                             <show_in_default>1</show_in_default>
  196.                             <show_in_website>1</show_in_website>
  197.                             <show_in_store>1</show_in_store>
  198.                         </specificerrmsg>
  199.                     </fields>
  200.                 </carrier_name>
  201.             </groups>
  202.         </carriers>
  203.     </sections>
  204. </config>

You should now see your module in the Administration under “System” > “Configuration” > “Shipping Methods”. It’s now up to you to add your custom fields in the <fields> tag, and subsequently make your configuration do something constructive.

Common Problems

Here is a list of things that have happened to others while trying to implement thier own Shipping Module. (Please add to this list or provide responses to unresolved issues if you can. A collection of common problems and how to avoid them can only help everybody.)

Module doesn't appear in frontend.

UNRESOLVED

Module doesn't appear in admin.

Check to make sure that you have the system.xml and config.xml files in the suggested directory structure. Capitilization appears to be important. Be careful not to use “locale” where you meant to use “local”.