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.