Reading list Switch to dark mode

    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