Table of Contents
Magento 2 is a rich eCommerce platform and it also supports few shipping methods in theΒ checkout process. However, they are not enough to make you comfortable. In order to be proportional with your development in the future, the customization of shipping methods is really crucial. Therefore, Magento 2 Create Shipping Method is built to make all easier.
With the simple explanation, it is accessible to follow step-by-step and complete the creation of new shipping methods.
All generated shipping methods are stored in Magento Admin Panel.
Please go toΒ Stores > Settings > Configuration > Sales > Delivery Methods
Β to find and enable it on the storefront. But hold on, access the fileΒ /Model/Carries/Generatedshippingmethod.php
Β in which you can set the specific shipping cost for eachΒ shipping method.
Namely to create the shipping method, please keep tracking on the following steps.
To add a new shipping carrier to the Magento checkout:
- Create a new module
- Add the carrier configuration
- Create the carrier model
- Enable the module
Step 1: Create a new module
The example module for use here isΒ Thecoachsmb_CustomShipping
.
1.1 Register Module
Source code of app/code/Thecoachsmb/CustomShipping/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Thecoachsmb_CustomShipping',
__DIR__
);
1.2 Create composer.json
Source code ofΒ app/code/Thecoachsmb/CustomShipping/composer.json
{ "name": "thecoachsmb/custom-shipping", "description": "Custom shipping module", "require": { "php": "~7.2.0||~7.3.0", "magento/framework": "102.0.*", "magento/module-backend": "101.0.*", "magento/module-catalog": "103.0.*", "magento/module-config": "101.1.*", "magento/module-directory": "100.3.*", "magento/module-quote": "101.1.*", "magento/module-sales": "102.0.*", "magento/module-sales-rule": "101.1.*", "magento/module-shipping": "100.3.*", "magento/module-store": "101.0.*" }, "type": "magento2-module", "license": [ "OSL-3.0", "AFL-3.0" ], "autoload": { "files": [ "registration.php" ], "psr-4": { "Thecoachsmb\\CustomShipping\\": "" } }, "version": "1.0.0" } |
1.3 Declare the Module
Source code of app/code/Thecoachsmb/CustomShipping/etc/module.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Thecoachsmb_CustomShipping" > <sequence> <module name="Magento_Store"/> <module name="Magento_Sales"/> <module name="Magento_Quote"/> <module name="Magento_SalesRule"/> </sequence> </module> </config> |
Step 2: Add the module configuration
To add a module configuration use the following source code snippets.
2.1 Show Configuration in Stores Configuration
Source codeΒ of app/code/Thecoachsmb/CustomShipping/etc/adminhtml/system.xml
TheΒ system.xml
Β source code declares custom shipping module options:
- Enabled
- Title
- Method Name
- Shipping Cost
- Ship to Applicable Countries
- Ship to Specific Countries
- Show Method if Not Applicable
- Sort Order
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="carriers" translate="label" type="text" sortOrder="320" showInDefault="1" showInWebsite="1" showInStore="1">
<group id="customshipping" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Thecoachsmb Custom Shipping Method</label>
<field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Enabled</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Method Name</label>
</field>
<field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Price</label>
<validate>validate-number validate-zero-or-greater</validate>
</field>
<field id="handling_type" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Calculate Handling Fee</label>
<source_model>Magento\Shipping\Model\Source\HandlingType</source_model>
</field>
<field id="handling_fee" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Handling Fee</label>
<validate>validate-number validate-zero-or-greater</validate>
</field>
<field id="sort_order" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Sort Order</label>
</field>
<field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Title</label>
</field>
<field id="sallowspecific" translate="label" type="select" sortOrder="90" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Ship to Applicable Countries</label>
<frontend_class>shipping-applicable-country</frontend_class>
<source_model>Magento\Shipping\Model\Config\Source\Allspecificcountries</source_model>
</field>
<field id="specificcountry" translate="label" type="multiselect" sortOrder="91" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Ship to Specific Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
<can_be_empty>1</can_be_empty>
</field>
<field id="showmethod" translate="label" type="select" sortOrder="92" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Show Method if Not Applicable</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<frontend_class>shipping-skip-hide</frontend_class>
</field>
<field id="specificerrmsg" translate="label" type="textarea" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Displayed Error Message</label>
</field>
</group>
</section>
</system>
</config>
Source code of app/code/Thecoachsmb/CustomShipping/etc/config.xml
First of all, shipping method should be defined in fileΒ config.xml, like in a below. Without it, it canβt work. The main node in xml is βdefaultβ and child of node βcarriersβ should have the same name as property $_code in shipping class Thecoachsmb\CustomShipping\Model\Carrier\Customshipping
.
TheΒ config.xml
file specifies default values for custom shipping module options and the shipping module model, Thecoachsmb\CustomShipping\Model\Carrier\Customshipping
:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<carriers>
<customshipping>
<active>1</active>
<sallowspecific>0</sallowspecific>
<model>Thecoachsmb\CustomShipping\Model\Carrier\Customshipping</model>
<name>Thecoachsmb Custom Shipping Method</name>
<price>10.00</price>
<title>Thecoachsmb Custom Shipping Method</title>
<specificerrmsg>This shipping method is not available. To use this shipping method, please contact us.</specificerrmsg>
<handling_type>F</handling_type>
</customshipping>
</carriers>
</default>
</config>
Thecoachsmb\CustomShipping\Model\Carrier\Customshipping
β. This model class is charged for shipping method. In this class should be implemented all logic for shipping calculation.Step 3: Create the carrier model
In this example, the Thecoachsmb\CustomShipping\Model\Carrier\Customshipping
Β class is a skeleton of a carrier model. You can extend it to fit your needs.
The carrier class implements theΒ CarrierInterface
Β interface and retrieves all available shipping methods in theΒ getAllowedMethods
Β function.
TheΒ collectRates
Β function returns theΒ \Magento\Shipping\Model\Rate\Result
Β object if the carrier method is available on checkout. Otherwise, it returnsΒ false
βthe carrier method is not applicable to the shopping cart.
Source code ofΒ app/code/Thecoachsmb/CustomShipping/Model/Carrier/Customshipping.php
<?php namespace Thecoachsmb\CustomShipping\Model\Carrier; use Magento\Quote\Model\Quote\Address\RateRequest; use Magento\Shipping\Model\Rate\Result; class Customshipping extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements \Magento\Shipping\Model\Carrier\CarrierInterface { /** * @var string */ protected $_code = 'customshipping'; /** * @var \Magento\Shipping\Model\Rate\ResultFactory */ protected $_rateResultFactory; /** * @var \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory */ protected $_rateMethodFactory; /** * Shipping constructor. * * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory * @param \Psr\Log\LoggerInterface $logger * @param \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory * @param \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory * @param array $data */ public function __construct( \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory, \Psr\Log\LoggerInterface $logger, \Magento\Shipping\Model\Rate\ResultFactory $rateResultFactory, \Magento\Quote\Model\Quote\Address\RateResult\MethodFactory $rateMethodFactory, array $data = [] ) { $this->_rateResultFactory = $rateResultFactory; $this->_rateMethodFactory = $rateMethodFactory; parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data); } /** * get allowed methods * @return array */ public function getAllowedMethods() { return [$this->_code => $this->getConfigData('name')]; } /** * @return float */ private function getShippingPrice() { $configPrice = $this->getConfigData('price'); $shippingPrice = $this->getFinalPriceWithHandlingFee($configPrice); return $shippingPrice; } /** * @param RateRequest $request * @return bool|Result */ public function collectRates(RateRequest $request) { if (!$this->getConfigFlag('active')) { return false; } /** @var \Magento\Shipping\Model\Rate\Result $result */ $result = $this->_rateResultFactory->create(); /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_rateMethodFactory->create(); $method->setCarrier($this->_code); $method->setCarrierTitle($this->getConfigData('title')); $method->setMethod($this->_code); $method->setMethodTitle($this->getConfigData('name')); $amount = $this->getShippingPrice(); $method->setPrice($amount); $method->setCost($amount); $result->append($method); return $result; } }
In order to properly write php class for shipping method, you should respect some Magento 2 rules. Every Magento 2 shipping class should extend β\Magento\Shipping\Model\Carrier\AbstractCarrierβ and implement β\Magento\Shipping\Model\Carrier\CarrierInterfaceβ.
In shipping model you need to create at least two php methods: βgetAllowedMethodsβ and βcollectRatesβ. This methods are required by abstract class and interface. Also, you should define propertyΒ $_code with value. In our case, that is βcustomshippingβ. Itβs related to config.xml and node structure.
Php method βcollectRatesβ accepts parameter β$requestβ which is instance of class βMagento\Quote\Model\Quote\Address\RateRequestβ. This class contains all information about items in cart/quote, weight, shipping address and so on. In this method you can implement all logic for shipping calculation. From this method you can call other services for shipping price calculation but it depends about your integration.
Step 4: Enable the module
Run the commands below to register Thecoachsmb_CustomShipping
module:
php bin/magento module:enable Thecoachsmb_CustomShipping |
php bin/magento setup:upgrade && php bin/magento se:s:d -f && php bin/magento c:f |
After successful running the commands, you will see the output as below:

Conclusion
In this article, we are learningΒ the process of creating custom shipping method. Follow the article, step by step. Please feel free to comment below for any queries or concerns.