Add a custom field for an offline payment method
This topic describes how to add a custom field to an offline payment method in the payment step of the checkout. The custom field allows the buyer to enter a comment about a purchase order.
Prerequisites
The Purchase Order
payment method must be enabled in the storefront for this task. Ensure this payment method is enabled by navigating to Stores > Settings > Configuration > Sales > Payment Methods > Other Payment Methods > Purchase Order in the Admin.
You must perform following steps to add a custom field to an offline payment method:
- Create a new module.
- Add a
db_schema.xml
file. - Add a
requirejs
file to the module. - Override the vendor files.
- Add an Observer.
- Compile and deploy the module.
- Verify that the module works.
Let's go through each step.
Step 1: Create a new module
Create a new module named Learning/CustomField
and register it.
Step 2 Add a db_schema.xml
file
Add the paymentpocomment
column in the quote_payment
and sales_order_payment
tables using the declarative schema method.
Create the app/code/Learning/CustomField/etc/db_schema.xml
and define the declarative schema as follows:
Copied to your clipboard<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="paymentpocomment" nullable="true" comment="PO Comment"/></table><table name="sales_order_payment" resource="sales" engine="innodb" comment="Sales Flat Order Payment"><column xsi:type="text" name="paymentpocomment" nullable="true" comment="PO Comment"/></table></schema>
Step 3: Add a requirejs file to the module
Create the app/code/Learning/CustomField/view/frontend/requirejs-config.js
file and add the following code:
Copied to your clipboardvar config = {map: {'*': {'Magento_OfflinePayments/js/view/payment/offline-payments':'Learning_CustomField/js/view/payment/offline-payments',}}}
Step 4: Override the vendor files
We must override the behavior of the following files to display the custom field:
- Magento_OfflinePayments/view/frontend/web/js/view/payment/offline-payments.js
- Magento_OfflinePayments/view/frontend/web/js/view/payment/method-renderer/purchaseorder-method.js
- Magento_OfflinePayments/view/frontend/web/template/payment/purchaseorder-form.html
Override the offline-payments.js
Override the Magento_OfflinePayments/view/frontend/web/js/view/payment/offline-payments.js
file to change the renderer of the Purchase Order payment method.
Create the app/code/Learning/CustomField/view/frontend/web/js/view/payment/offline-payments.js
file and add the following code:
Copied to your clipboarddefine(['uiComponent','Magento_Checkout/js/model/payment/renderer-list'],function (Component,rendererList) {'use strict';rendererList.push({type: 'checkmo',component: 'Magento_OfflinePayments/js/view/payment/method-renderer/checkmo-method'},{type: 'banktransfer',component: 'Magento_OfflinePayments/js/view/payment/method-renderer/banktransfer-method'},{type: 'cashondelivery',component: 'Magento_OfflinePayments/js/view/payment/method-renderer/cashondelivery-method'},{type: 'purchaseorder',component: 'Learning_CustomField/js/view/payment/method-renderer/purchaseorder-method'});/** Add view logic here if needed */return Component.extend({});});
Override the purchaseorder-method.js
It is also necessary to override the Magento_OfflinePayments/view/frontend/web/js/view/payment/method-renderer/purchaseorder-method.js
file.
The template
path value used in this file must be altered to use the custom template. Also, the logic to get the additional_data
is implemented in this file.
Create the app/code/Learning/CustomField/view/frontend/web/js/view/payment/method-renderer/purchaseorder-method.js
file and add the following code:
Copied to your clipboarddefine(['Magento_Checkout/js/view/payment/default','jquery','mage/validation'], function (Component, $) {'use strict';return Component.extend({defaults: {template: 'Learning_CustomField/payment/purchaseorder-form',purchaseOrderNumber: ''},/** @inheritdoc */initObservable: function () {this._super().observe('purchaseOrderNumber');return this;},/*** @return {Object}*/getData: function () {return {method: this.item.method,'po_number': this.purchaseOrderNumber(),'additional_data': {'po_number': $('#po_number').val(),'paymentpocomment': $('#purchaseorder_paymentpocomment').val(),}};},/*** @return {jQuery}*/validate: function () {var form = 'form[data-role=purchaseorder-form]';return $(form).validation() && $(form).validation('isValid');}});});
Override the purchaseorder-form.html
We must override the Magento_OfflinePayments/view/frontend/web/template/payment/purchaseorder-form.html
template file to add the custom input field (Purchase Order Comment).
Create the app/code/Learning/CustomField/view/frontend/web/template/payment/purchaseorder-form.html
file and add the following code:
Copied to your clipboard<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="purchaseorder-form" class="form form-purchase-order" data-role="purchaseorder-form"><fieldset class="fieldset payment method" data-bind='attr: {id: "payment_form_" + getCode()}'><div class="field field-number required"><label for="po_number" class="label"><span><!-- ko i18n: 'Purchase Order Number'--><!-- /ko --></span></label><div class="control"><div class="name-info"><input type="text" id="po_number" name="payment[po_number]" placeholder="Purchase Order Number" data-validate="{required:true}" data-bind='attr: {title: $t("Purchase Order Number")},value: purchaseOrderNumber' class="input-text"/><input type="text" id="purchaseorder_paymentpocomment" name="payment[paymentpocomment]" class="input-text" placeholder="Purchase Order Comment" value="" data-bind="attr: {title: $t('Purchase Order Comment'),'data-container': getCode() + '-paymentpocomment'},valueUpdate: 'keyup'"/></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 checkout_onepage_controller_success_action
event is used.
Create the app/code/Learning/CustomField/etc/frontend/events.xml
file and add the following code:
Copied to your clipboard<?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="checkout_onepage_controller_success_action"><observer name="paymentfields_paymentfields_observer_frontend_sales_orderpaymentsavebefore" instance="Learning\CustomField\Observer\Frontend\Sales\OrderPaymentSaveBefore" /></event></config>
Then create the app/code/Learning/CustomField/Observer/Frontend/Sales/OrderPaymentSaveBefore.php
file.
Copied to your clipboard<?phpnamespace Learning\CustomField\Observer\Frontend\Sales;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{protected $order;protected $logger;protected $_serialize;protected $quoteRepository;public function __construct(\Magento\Sales\Api\Data\OrderInterface $order,\Magento\Quote\Api\CartRepositoryInterface $quoteRepository,\Psr\Log\LoggerInterface $logger,\Magento\Framework\Serialize\Serializer\Serialize $serialize) {$this->order = $order;$this->quoteRepository = $quoteRepository;$this->logger = $logger;$this->_serialize = $serialize;}/*** Execute observer** @param \Magento\Framework\Event\Observer $observer* @return void*/public function execute(\Magento\Framework\Event\Observer $observer){$orderids = $observer->getEvent()->getOrderIds();if(!$orderids){foreach ($orderids as $orderid) {$order = $this->_order->load($orderid);$method = $order->getPayment()->getMethod();if($method == 'purchaseorder') {$quote_id = $order->getQuoteId();$quote = $this->quoteRepository->get($quote_id);$paymentQuote = $quote->getPayment();$paymentOrder = $order->getPayment();$paymentOrder->setData('paymentpocomment',$paymentQuote->getPaymentpocomment());$paymentOrder->save();}}}}}
Step 6: Compile and deploy the module
Run the following sequence of commands to compile and deploy your custom module.
Enable the new module.
Copied to your clipboardbin/magento module:enable Learning_CustomFieldInstall the new module.
Copied to your clipboardbin/magento setup:upgradeCompile the code.
Copied to your clipboardbin/magento setup:di:compileDeploy the static files.
Copied to your clipboardbin/magento setup:static-content:deploy
Step 7: Verify that the module works
Use the following steps to verify your changes work as expected.
Go to the storefront as a guest user and add a product to the cart.
Go to the checkout page and select the Purchase Order payment.
Verify that the Purchase Order Comment field is visible.
Fill the purchase order comment field in the checkout and place an order.
Verify that the entered value is stored in the
paymentpocomment
column of thesales_order_payment
table.