Magento – Magento 2 : Event Observer For Payment Method Selection

event-observermagento2payment-methods

I am working on a custom extension where I need to call an observer when any payment method is select from the available payment method list on the frontend checkout page.

Can anyone tell me which event observer should I used for this?
I need to call a custom function and add fee to the cart subtotal.

enter image description here

Best Answer

Unfortunately, observers are useful only within php functions. This means that for an event to be triggered, it has to be initially dispatched dispatch() either by a native or custom event dispatcher. In this particular case, the action taken is a click over a payment method button. This click does not trigger any php code execution, only Javascript code gets executed.

As the checkout process in Magento 2 is mostly built around Knockout JS, most action happens on the frontend using Javascript instead of server side php.

Knockout JS is very flexible and it is possible to bind events and observe variables. On the other side, it may require a steep learning curve.

A good angle to look at for your project would be to use a controller instead of an observer:

1. Let's begin by creating a module...

2. Create a controller that does your logic when triggered

Controller structure: http://www.example.com/route/controller_folder/action

2.1 Create the controller Action class:

app/code/NameSpace/Module/Controller/Test/Action.php

namespace NameSpace\Module\Controller\Test;

class Action extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        $request = $this->getRequest();
        //EXECUTE YOUR LOGIC HERE
    }
}

2.2 Register a route for your controllers

app/code/NameSpace/Module/etc/adminhtml/routes.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
  <router id="standard">
     <route id="route" frontName="route">
        <module name="NameSpace_Module" />
    </route>
  </router>
</config>

2.3 As this will be used on checkout, add your route to the secure URL list [EDIT]

app/code/NameSpace/Module/etc/di.xml

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Url\SecurityInfo">
        <arguments>
            <argument name="secureUrlList" xsi:type="array">
                <item name="route" xsi:type="string">/route/</item>
            </argument>
        </arguments>
    </type>
</config>

3. Add a javascript file on the checkout page using the following layout file:

app/code/NameSpace/Module/view/frontend/layout/checkout_index_index.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
       <link src="NameSpace_Module::js/payment-method-trigger.js"/>
    </head>
</page>

4. In this script you might simply add a function to send an ajax post request each time a payment method tab has been clicked.


Best Method - Knockout: Subscribing to observable

The best way to trigger the click event without extending/overriding core file or impacting the original click function would involve subscribing an observable with the help of Knockout.

Method 2 - Extend JS class [EDIT]

There should also be a way to extend the initial JS class

define([
    'NameSpace_Module/path/to/original/file', //JS FILE TO EXTEND
], function (originalFile) { //PASS AS A PARAMETER
    'use strict';

    return originalFile.extend({ //EXTEND
        //FUNCTIONS ADDED HERE WILL OVERRIDE FUNCTIONS
        //FROM ORIGINAL CLASS IF ALREADY EXISTS
        someFunction: {
            someLogic();
        },


    });
});

Method 3 - Overriding select-payment-method.js

Playing with Knockout JS can be delicate sometimes and for the purpose of this answer we will simply override the function responsible for registering the payment method in the quote which is triggered by the selectPaymentMethod function. It may not be the most elegant solution compared to use 100% Knockout JS but it should work as intended without affecting any functionnality unless a future Magento update would interfere by modifying the original function.

To better understand you may find the function selectPaymentMethod on line 139 of this file:

Magento_Checkout/js/view/payment/default.js

1. Now we have to declare our function override:

app/code/NameSpace/Module/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            'Magento_Checkout/js/action/select-payment-method':
                'NameSpace_Module/js/action/payment/select-payment-method'
        }
    }
};

2. Finally, we reuse the function responsible for selecting the payment method with a little addition to make our ajax call!

app/code/NameSpace/Module/view/frontend/web/js/action/payment/select-payment-method.js

define(
    [
    'jquery',
    'uiComponent',
    'ko',
    'Magento_Checkout/js/model/quote',
    ], function ($, uiComponent, ko, quote) {
        'use strict';

        function () {
            $.ajax({
                showLoader: true,
                url: 'http://www.example.com/route/controller_folder/action',
                data: { action : 1, param : 2},
                type: "POST",
                dataType: 'json'
            }).done(function (data) {
                alert('Request Sent');
            });
        };

        return function (paymentMethod) {
            quote.paymentMethod(paymentMethod);
        }
});

Each time a customer will click on a payment method tab, your Javascript method will send a post ajax request to your controller which will execute the php code with your logic.

This touches several different Magento 2 aspects. Although I would like to provide a quick and easy solution to your question, that's just the way Magento 2 has been built. Now, a large part of the logic is implemented client side and even more when approaching the checkout system.

Remember to always be careful when dealing with the checkout system, a bug on a checkout page can hurt a store very badly.

NOTE: All code above is untested