Magento 2: Add Extra Fee To Order Totals

Sometimes we might need to add an extra fee ( Handling Fee ) to order total in Magento 2. Generally, the order totals comprise of subtotal, shipping cost, taxes, and discount, based on these values the grand total will be calculated. Now if we want to add an extra fee it will affect the order grand total. In this article, we will guide you how to add an extra fee to your Magento 2 order without any issues.

Here we are going to add a new order total called “Handling Fee” with a fixed cost of $10.

Step 1: Define new module,

Create module.xml file in the below file path to define the new module,

<Magento_2_Root>/app/code/MyModule/HandlingFee/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="MyModule_HandlingFee" setup_version="1.0.0">
    </module>
</config>

Next, create registration.php file in the below file path,

<Magento_2_Root>/app/code/MyModule/HandlingFee/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'MyModule_HandlingFee',
    __DIR__
);

Step 2: create a file sales.xml in the following path,

<Magento_2_Root>/app/code/MyModule/HandlingFee/etc/sales.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Sales:etc/sales.xsd">
    <section name="quote">
        <group name="totals">
            <item name="handlingfee" instance="MyModule\HandlingFee\Model\Quote\Address\Total\HandlingFee" sort_order="400"/>
        </group>
    </section>
</config>

Step 3: create a file HandlingFee.php in the following path

<Magento_2_Root> /app/code/MyModule/HandlingFee/Model/Quote/Address/Total/HandlingFee.php

<?php
namespace MyModule\HandlingFee\Model\Quote\Address\Total;
class HandlingFee extends \Magento\Quote\Model\Quote\Address\Total\AbstractTotal
{
    /**
     * @var \Magento\Framework\Pricing\PriceCurrencyInterface
     */
    protected $_priceCurrency;
 
    /**
     * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
     */
    public function __construct(
        \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
    ) {
        $this->_priceCurrency = $priceCurrency;
    }
 
    public function collect(
        \Magento\Quote\Model\Quote $quote,
        \Magento\Quote\Api\Data\ShippingAssignmentInterface $shippingAssignment,
        \Magento\Quote\Model\Quote\Address\Total $total
    ) {
        parent::collect($quote, $shippingAssignment, $total);
        $handlingFee = 10;
        $total->addTotalAmount('handlingfee', $handlingFee);
        $total->addBaseTotalAmount('handlingfee', $handlingFee);
        $quote->setHandlingFee($handlingFee);
        return $this;
    }
 
    public function fetch(
        \Magento\Quote\Model\Quote $quote,
        \Magento\Quote\Model\Quote\Address\Total $total
    ) {
        return [
            'code' => 'Handling_Fee',
            'title' => $this->getLabel(),
            'value' => 10
        ];
    }
 
    /**
     * get label
     * @return string
     */
    public function getLabel() {
        return __('Handling Fee');
    }
}

Step 4: create a file checkout_cart_index.xml in the following path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/layout/checkout_cart_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.cart.totals">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="block-totals" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="handlingfee" xsi:type="array">
                                    <item name="component" xsi:type="string">MyModule_HandlingFee/js/view/checkout/summary/handling-fee</item>
                                    <item name="sortOrder" xsi:type="string">20</item>
                                    <item name="config" xsi:type="array">
                                        <item name="handlingfee" xsi:type="string" translate="true">Handling Fee</item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Step 5: create a file checkout_index_index.xml in the same path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/layout/checkout_index_index.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="sidebar" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="summary" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="totals" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="handlingfee" xsi:type="array">
                                                            <item name="component" xsi:type="string">MyModule_HandlingFee/js/view/checkout/cart/totals/handling-fee</item>
                                                            <item name="sortOrder" xsi:type="string">20</item>
                                                            <item name="config" xsi:type="array">
                                                                <item name="template" xsi:type="string">MyModule_HandlingFee/checkout/cart/totals/handling-fee</item>
                                                                <item name="title" xsi:type="string" translate="true">Handling Fee</item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                                <item name="cart_items" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="details" xsi:type="array">
                                                            <item name="children" xsi:type="array">
                                                                <item name="subtotal" xsi:type="array">
                                                                    <item name="component" xsi:type="string">Magento_Tax/js/view/checkout/summary/item/details/subtotal</item>
                                                                </item>
                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Step 6: create a file handling-fee.js on path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/web/js/view/checkout/cart/totals/handling-fee.js

define(
    [
        'MyModule_HandlingFee/js/view/checkout/summary/handling-fee'
    ],
    function (Component) {
        'use strict';
        return Component.extend({
            /**
             * @override
             */
            isDisplayed: function () {
                return true;
            }
        });
    }
);

Step 7: create a file handling-fee.js in the following path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/web/js/view/checkout/summary/handling-fee.js

define(
    [
        'jquery',
        'Magento_Checkout/js/view/summary/abstract-total',
        'Magento_Checkout/js/model/quote',
        'Magento_Checkout/js/model/totals',
        'Magento_Catalog/js/price-utils'
    ],
    function ($,Component,quote,totals,priceUtils) {
        "use strict";
        return Component.extend({
            defaults: {
                template: 'MyModule_HandlingFee/checkout/summary/handling-fee'
            },
            totals: quote.getTotals(),
               isDisplayedHandlingfeeTotal : function () {
               return true;
            },
            getHandlingfeeTotal : function () {
                var price = 10;
                return this.getFormattedPrice(price);
            }
        });
    }
);

Step 8: create a file handling-fee.html in the follwing path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/web/template/checkout/cart/totals/handling-fee.html

<!-- ko if: isDisplayedHandlingfeeTotal() -->
<tr class="totals handling-fee excl">
    <th class="mark" colspan="1" scope="row" data-bind="text: title"></th>
    <td class="amount">
        <span class="price" data-bind="text: getHandlingfeeTotal()"></span>
    </td>
</tr>
<!-- /ko -->

Step 9: create a file handling-fee.html in the following path

<Magento_2_Root> /app/code/MyModule/HandlingFee/view/frontend/web/template/checkout/summary/handling-fee.html

<!-- ko if: isDisplayedHandlingfeeTotal() -->
<tr class="totals coupon-fee excl">
    <th class="mark" colspan="1" scope="row" data-bind="text: handlingfee"></th>
    <td class="amount">
        <span class="price" data-bind="text: getHandlingfeeTotal(), attr: {'data-th': handlingfee}"></span>
    </td>
</tr>
<!-- /ko -->

finally you can see the extra fee on your cart and checkout page.

Reference image:

Magento 2: Add Extra Fee To Order Totals

Hope this helps.

5 Comments on “Magento 2: Add Extra Fee To Order Totals”

  1. Hello,
    Great article ! Very helpful for adding extra fee at checkout page. However, can we modify this extension so that customer can choose whether they need to pay extra fee or not, it seems like the customer will not have a choice with this.


    Thank You

  2. Hi,

    Great article. I have implemented it. It works fine. But you have misses something.
    When order is complete. Then fee is not showing in customer’s order page (/sales/order/view/order_id/{{$id}}/ )
    Also additional fee section is not available in admin as well.

    It is missing in email too.

    But final calculation is correct.

  3. Helpful tutorial, but I would like to see the handling fee added to the cart totals/quote model responses from an API REST call. Only time I see this is from the total segments object array thanks to the Fetch method.

  4. I have build the same module.but not working.extra fees is not showing on frontend and the error is also not showing.

    1. Hi,

      Thanks for reading our blog. If you used and third party modules to override the checkout process, it may conflict. Can you please specify your Magento version and the third party modules if you used.

Leave a Reply

Your email address will not be published. Required fields are marked *