Creating a custom shipping method in Magento 2

16 Aug, 2021

Today we are going to create a new custom shipping in Magento 2.x

Custom shipping is usefull to extends checkout process as per business needs. We will going to create new shipping method in magento 2.x. Magento 2 provide verius shipping methods, We will add our custom shipping method in Magento 2. It's very simple to add our custom shipping method in Magento 2.

Let's start by creating a new custom shipping method.

Step1. Create module directory

Go to Magento app/code directory and create a directory with your vendor name. My vendor name is "RS". After creating the vendor directory, need to create a directory for the module. My module name is "CustomShip". My custom module directory path is like "MAGENTO_ROOT/app/code/RS/CustomShip"

Step2. Create module registration file

	<?php
	//file path: MAGENTO_ROOT/app/code/RS/CustomShip/registration.php

	\Magento\Framework\Component\ComponentRegistrar::register(
	    \Magento\Framework\Component\ComponentRegistrar::MODULE,
	    "RS_CustomShip",
	    __DIR__
	);	

Step3. Create module config file

	
<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/CustomShip/etx/module.xml -->

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="RS_CustomShip" setup_version="1.0.0" />
</config>	

Step4. Create system config file for config our custom shipping method.

	
	<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/CustomShip/etc/adminhtml/system.xml -->
<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="customship" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Custom Shipping Method</label>
                <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1" >
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="title" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" >
                    <label>Title</label>
                </field>
                <field id="name" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" >
                    <label>Method Name</label>
                </field>
                <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="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="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="1">
                    <label>Handling Fee</label>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
                <field id="sort_order" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Sort Order</label>
                </field>
                <field id="sallowspecific" translate="label" type="select" sortOrder="12" showInDefault="1" showInWebsite="1" showInStore="0" >
                    <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="14" 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="16" 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="18" showInDefault="1" showInWebsite="1" showInStore="1" >
                    <label>Displayed Error Message</label>
                </field>
            </group>
        </section>
	</system>
</config>

Step5. Create config file for set default value in our custom shipping method.

	
	<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/CustomShip/etc/config.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
    	<carriers>
            <customship>
                <active>0</active>
                <model>RS\CustomShip\Model\Carrier\CustomShip</model>
                <title>RS Custom Rates</title>
                <name>Custom Shipping</name>
                <handling_type>F</handling_type>
                <price>00.00</price>
                <sallowspecific>0</sallowspecific>
                <specificerrmsg>This shipping method is not available. To use this shipping method, please contact us.</specificerrmsg>
            </customship>
        </carriers>
    </default>
</config>

Step6. Create shipping logic file for when you have to show this custom shipping method.

	
	<?php

namespace RS\CustomShip\Model\Carrier;

use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Rate\Result;

class CustomShip extends \Magento\Shipping\Model\Carrier\AbstractCarrier implements
    \Magento\Shipping\Model\Carrier\CarrierInterface
{
    /**
     * @var string
     */
    protected $_code = "customship";

    /**
     * @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;
    }
}
Step7. Install your module to magento
	
	
php bin/magento module:enable RS_CustomShip
php bin/magento setup:di:compile
php bin/magento setup:upgrade
php bin/magento setup:static-content:deploy

That's the steps to create a custom shipping method in Magento 2.x

RAJU SADADIYA (MAGENTO DEVELOPER)