Back to Top

How to add custom field for a payment method in Magento 2?

Updated 9 May 2023

This topic describes how to add a custom field to a payment method in the payment step of the checkout. The custom field allows the buyer to enter the phone for an payment method.

Step 1 – Create a new module
Create a new module named Learning/PaymentMethod and register it.

Step 2 Add a db_schema.xml file.
Add the mpesanumber column in the quote_payment and sales_order_payment tables using the declarative schema method.

Create the app/code/Learning/PaymentMethod/etc/db_schema.xml and define the declarative schema as follows:

<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    
    <table name="quote_payment" resource="checkout" engine="innodb" comment="Sales Flat Quote Payment">
        <column xsi:type="text" name="mpesanumber" nullable="true" comment="Mpesa Mobile Number"/>
    </table>
    <table name="sales_order_payment" resource="sales" engine="innodb" comment="Sales Flat Order Payment">
        <column xsi:type="text" name="mpesanumber" nullable="true" comment="Mpesa Mobile Number"/>
    </table>
</schema>

Step 3- Override the payment method js files or if using new Magento 2 payment method then add the code in the below files.
Create the app/code/Learning/PaymentMethod/view/frontend/web/js/view/payment/testmethod.js file and add the following code:

Start your headless eCommerce
now.
Find out More
 define(
    [
        'uiComponent',
        'Magento_Checkout/js/model/payment/renderer-list'
    ],
    function (Component,
              rendererList) {
        'use strict';
        rendererList.push(
            {
                type: 'testmethod',
                component: 'Learning_PaymentMethod/js/view/payment/method-renderer/testmethod'
            }
        );
        return Component.extend({});
    }
);


Create the app/code/Learning/PaymentMethod/view/frontend/web/js/view/payment/method-renderer/testmethod.js file and add the following code:

define(
    [
        'jquery',
        'Magento_Checkout/js/view/payment/default',
        'mage/url',
        'Magento_Checkout/js/model/error-processor',
        'Magento_Checkout/js/action/redirect-on-success',
        'Magento_Checkout/js/model/quote',
        'mage/validation'
    ],
    function ($,Component,url, errorProcessor,redirectOnSuccessAction, quote) {
        'use strict';
        
        return Component.extend({
            defaults: {
                template: 'Learning_PaymentMethod/payment/testmethod',
                mpesaNumber:'',
            },
            /** @inheritdoc */
            initObservable: function () {
                this._super()
                    .observe(['mpesaNumber']);

                return this;
            },
            /**
         * @return {Object}
         */
        getData: function () {
            return {
                method: this.item.method,
                // 'mpesanumber': this.mpesaNumber(),
                'additional_data': {
                    'mpesanumber': $('#lipampesanumber').val()
                }
            };
        },
        /**
         * @return {jQuery}
         */
        validate: function () {
            var form = 'form[data-role=mpesanumber_form]';

            return $(form).validation() && $(form).validation('isValid');
        },
       
        });
    }
);


Create the app/code/Learning/PaymentMethod/view/frontend/web/template/payment/testmethod.html file and add the following code:

<div class="payment-method" data-bind="css: {'_active': (getCode() == isChecked())}">
    <div class="payment-method-title field choice">
        <input type="radio"
               name="payment[method]"
               class="radio"
               data-bind="attr: {'id': getCode()}, value: getCode(), checked: isChecked, click: selectPaymentMethod, visible: isRadioButtonVisible()"/>
        
        <label data-bind="attr: {'for': getCode()}" class="label">
            <span data-bind="text: getTitle()"></span>
        </label>
    </div>

    <div class="payment-method-content">
        <!-- ko foreach: getRegion('messages') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <!--/ko-->
        <div class="payment-method-billing-address">
            <!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
            <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <form id="mpesanumber_form" class="form form-mpsaisa-number" data-role="mpesanumber_form">
            <fieldset class="fieldset payment method" data-bind='attr: {id: "payment_form_" + getCode()}'>
                <div class="field field-number required">
                    <label for="mpesanumber" class="label">
                    <span><!-- ko i18n: 'Mpesa Mobile Number'--><!-- /ko --></span>
                    </label>
                    <div class="control">
                        <div class="name-info">
                            <input type="text" id="lipampesanumber" name="payment[mpesanumber]" 
                            placeholder="Mpesa Mobile Number" data-validate="{required:true}"        data-bind='attr: {title: $t("Mpaisa Mobile Number")}' class="input-text"/>
                        </div>
                    </div>
                </div>
            </fieldset>
        </form>
        <div class="checkout-agreements-block">
            <!-- ko foreach: $parent.getRegion('before-place-order') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
            <!--/ko-->
        </div>
        <div class="actions-toolbar" id="review-buttons-container">
            <div class="primary">
                <button class="action primary checkout"
                        type="submit"
                        data-bind="
                        click: placeOrder,
                        attr: {title: $t('Place Order')},
                        enable: (getCode() == isChecked()),
                        css: {disabled: !isPlaceOrderActionAllowed()}
                        "
                        data-role="review-save">
                    <span data-bind="i18n: 'Place Order'"></span>
                </button>
            </div>
        </div>
    </div>
</div>


Step 5: Add an Observer
Create an Observer file to save the custom field data to the order. For the Observer file an events.xml file is required to call the observer for a particular event. For this example, the sales_model_service_quote_submit_before event is used.

Create the app/code/Learning/PaymentMethod/etc/frontend/events.xml file and add the following code:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
	
    <event name="sales_model_service_quote_submit_before">
        <observer name="mpesa_field_observer_frontend_sales_orderpaymentsavebefore" instance="Learning\PaymentMethod\Observer\OrderPaymentSaveBefore" />
    </event>
    
</config>


Then create the app/code/Learning/PaymentMethod/Observer/OrderPaymentSaveBefore.php file.

<?php

namespace Learning\PaymentMethod\Observer;

use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Event\ObserverInterface;
use Magento\OfflinePayments\Model\Purchaseorder;
use Magento\Framework\App\Request\DataPersistorInterface;

class OrderPaymentSaveBefore implements \Magento\Framework\Event\ObserverInterface
{

    /**
     * Construct
     *
     * @param \Magento\Sales\Api\Data\OrderInterface $order
     * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Serialize\Serializer\Serialize $serialize
     * @param \Magento\Webapi\Controller\Rest\InputParamsResolver $inputParamsResolver
     * @param \Magento\Framework\App\State $state
     */
    public function __construct(
        \Magento\Sales\Api\Data\OrderInterface $order,
        \Magento\Quote\Api\CartRepositoryInterface $quoteRepository,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Serialize\Serializer\Serialize $serialize,
        \Magento\Webapi\Controller\Rest\InputParamsResolver $inputParamsResolver,
        \Magento\Framework\App\State $state
    ) {
        $this->order = $order;
        $this->quoteRepository = $quoteRepository;
        $this->logger = $logger;
        $this->_serialize = $serialize;
        $this->inputParamsResolver = $inputParamsResolver;
        $this->_state = $state;
    }
    
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $order = $observer->getOrder();
        $inputParams = $this->inputParamsResolver->resolve();
        if ($this->_state->getAreaCode() != \Magento\Framework\App\Area::AREA_ADMINHTML) {
            foreach ($inputParams as $inputParam) {
                if ($inputParam instanceof \Magento\Quote\Model\Quote\Payment) {
                    $paymentData = $inputParam->getData('additional_data');
                    
                    $paymentOrder = $order->getPayment();
                    $order = $paymentOrder->getOrder();
                    $quote = $this->quoteRepository->get($order->getQuoteId());
                    $paymentQuote = $quote->getPayment();
                    $method = $paymentQuote->getMethodInstance()->getCode();
                    if ($method == 'testmethod') {
                        if (isset($paymentData['mpesanumber'])) {
                            $paymentQuote->setData('mpesanumber', $paymentData['mpesanumber']);
                            $paymentOrder->setData('mpesanumber', $paymentData['mpesanumber']);
                        }
                        
                    }
                }
            }
        }
    }
}


Step 6: Compile and deploy the module
Run the following sequence of commands to compile and deploy your custom module.

Enable the new module.

bin/magento module:enable Learning_PaymentMethod
Install the new module.

bin/magento setup:upgrade
Compile the code.

bin/magento setup:di:compile
Deploy the static files.

bin/magento setup:static-content:deploy
Step 7: Verify that the module works

payment_method

We hope it will help you. Thank you!!

If any issue or doubt please feel free to mention in comment section.

We would be happy to help. Happy Coding!!

. . .

Leave a Comment

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


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home