Select Page

How to add custom field for a payment method in Magento2

Radhika Garg
Published: June 16, 2022

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="" 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 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"/>

Step 3- Override the payment method js files or if using new 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:

    function (Component,
              rendererList) {
        'use strict';
                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:

    function ($,Component,url, errorProcessor,redirectOnSuccessAction, quote) {
        'use strict';
        return Component.extend({
            defaults: {
                template: 'Learning_PaymentMethod/payment/testmethod',
            /** @inheritdoc */
            initObservable: function () {
                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"
               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>
    <div class="payment-method-content">
        <!-- ko foreach: getRegion('messages') -->
        <!-- ko template: getTemplate() --><!-- /ko -->
        <div class="payment-method-billing-address">
            <!-- ko foreach: $parent.getRegion(getBillingAddressFormName()) -->
            <!-- ko template: getTemplate() --><!-- /ko -->
        <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>
                    <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 class="checkout-agreements-block">
            <!-- ko foreach: $parent.getRegion('before-place-order') -->
                <!-- ko template: getTemplate() --><!-- /ko -->
        <div class="actions-toolbar" id="review-buttons-container">
            <div class="primary">
                <button class="action primary checkout"
                        click: placeOrder,
                        attr: {title: $t('Place Order')},
                        enable: (getCode() == isChecked()),
                        css: {disabled: !isPlaceOrderActionAllowed()}
                    <span data-bind="i18n: 'Place Order'"></span>

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="" 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" />

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

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


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!!