custom shipping method

Create Custom Shipping Method in Magento 2

Posted by

Create custom shipping method in Magento 2 allows you to offer unique shipping options tailored to your e-commerce business’s specific needs. This customization can differentiate your store, potentially reduce shipping costs, and improve customer satisfaction. In this tutorial, you will learn how to create a custom shipping method in Magento 2.

Why need a custom Shipping Method

As a store owner sometimes you may need to use your own shipping method to ship by your own and not using any 3rd party providers. There can be also cases that the current shipping method which is inbuilt in Magento/Adobe commerce doesn’t fit your requirements. If you are listing your products on marketplaces like Amazon or Ebay and you are using 3rd party plugins to import orders inside Magento then you will need a shipping method for it. In such case you can create a custom shipping method and configure it in either code or configuration base on the 3rd party plugins.

Developing a custom shipping method

We have previously developed a custom payment method in our blog same way we will be creating a custom shipping method. We will show till how much part of code need just to show on admin and then later will show it on frontend.

Basic files for a module

For basic files you can follow own article Create Magento 2 Custom Module Development. In this article we have explained which files are need for setting up a basic Adobe commerce/Magento module. We won’t be writing the same code here for file module.xml, registration.php and composer.json file. Add the code according to your module name. In this case our module name will be Learningmagento_CustomShipping.

Configuration files

system.xml

Create a file name system.xml under app/code/Learningmagento/CustomShipping/etc/adminhtml/system.xml. Add the following code to add an enable disable dropdown to enable and disable Shipping method. Add name, title and a price field as a textbox. This settings need to be under the section carriers which is important to become a shipping method. The following code will add the configurational setting for our Shipping method.

<?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="100" showInDefault="1" showInWebsite="1" showInStore="1">
            <group id="store_shipping" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Store Shipping Method</label>
                <field id="active" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="name" translate="label" type="text" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Name</label>
                </field>
                <field id="title" translate="label" type="text" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Title</label>
                </field>
                <field id="price" translate="label" type="text" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="0">
                    <label>Price</label>
                    <validate>validate-number validate-zero-or-greater</validate>
                </field>
            </group>
        </section>
    </system>
</config>

config.xml

In this file we will be adding default values for our configurational settings. To add a default value you will need to add a config.xml file which is responsible to add default values for the configurational settings. Create a file under  app/code/Learningmagento/CustomShipping/etc/config.xml, here there is an additional field which was not there in system.xml a model field.

This model class will be used to add the shipping method on frontend as well as on admin order. So this is an important file for the linking of settings and other shipping methods. Add the below code in the newly created config.xml file.

<?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>
            <store_shipping>
                <active>1</active>
                <model>Learningmagento\CustomShipping\Model\Carrier\Storeship</model>
                <name>Local Shipping</name>
                <title>Local Shipping</title>
                <price>5</price>
            </store_shipping>
        </carriers>
    </default>
</config>
custom shipping method configuration

Model class

As you see the next and the most important file we will need to create is Storeship.php file. Create this file on path Learningmagento\CustomShipping\Model\Carrier\Storeship and add the below code.

<?php

/**
 *
 * @category  Custom Development
 * @email     contactus@learningmagento.com
 * @author    Learning Magento
 * @website   learningmagento.com
 * @Date      10-04-2024
 */
namespace Learningmagento\CustomShipping\Model\Carrier;

use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;
use Psr\Log\LoggerInterface;
use Magento\Shipping\Model\Rate\ResultFactory;
use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;

class Storeship extends AbstractCarrier implements CarrierInterface
{
    const CODE = 'store_shipping';

    const ORDER_CODE = 'store_shipping_store_shipping';

    protected $_code = self::CODE;

    protected $rateResultFactory;

    protected $rateMethodFactory;

    protected $config;

    public function __construct(
        ScopeConfigInterface $scopeConfig,
        ErrorFactory $rateErrorFactory,
        LoggerInterface $logger,
        ResultFactory $rateResultFactory,
        MethodFactory $rateMethodFactory,
        array $data = []
    )
    {
        $this->rateResultFactory = $rateResultFactory;
        $this->rateMethodFactory = $rateMethodFactory;
        parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);
    }

    public function getAllowedMethods()
    {
        return [$this->_code => $this->getConfigData('name')];
    }

    public function collectRates(RateRequest $request)
    {
        if (!$this->getConfigData('active')) {
            return false;
        }

        $result = $this->rateResultFactory->create();
        $method = $this->rateMethodFactory->create();

        $method->setCarrier($this->_code);
        $method->setCarrierTitle($this->getConfigData('title'));

        $method->setMethod($this->_code);
        $method->setMethodTitle($this->getConfigData('name'));

        $shippingPrice = $this->getConfigData('price');
        $method->setPrice($shippingPrice);

        $result->append($method);

        return $result;
    }
}

As a model class for our custom shipping method this file need to implement CarrierInterface interface and extend the AbstractCarrier as parent class which is done by all shipping method. The Carrier class follows rules set by the CarrierInterface interface. It finds all shipping options using the getAllowedMethods function. When you use the collectRates function, it gives back a list of shipping rates if the carrier works for checkout. If not, it says ‘false’, meaning it doesn’t work for the shopping cart.

We are using two classes Magento\Shipping\Model\Rate\ResultFactory class acts as a container for rates where all shipping methods are added to this class using the append method as seen at the bottom and Magento\Quote\Model\Quote\Address\RateResult\MethodFactory class.

In the method collectRates we are first checking the configurational settings for our custom shipping method if its enabled or disabled. We then are creating two objects of ResultFactory and MethodFactory. Next we are setting the title and name from configuration. At last we are adding our price from configuration value which will be shown when the customer is on checkout page. Last we append our settings to the list of other enabled methods and returning.

custom shipping method on admin

Conclusion of Custom shipping method

After writing the above code you will need to enable the plugin. Just run php bin/magento setup:upgrade and php bin/magento setup:static-content:deploy as this will do the work. For testing purpose check on both frontend order and backend order also do check under the configuration if the custom shipping method is shown. Configuration -> Sales -> Delivery method. You can download the code from our git repo Magento 2 custom shipping method. We will be using this method in creating an order programmatically.