How to Use Payment Extension Attributes in Magento 2

Magento 2 is the leading E-commerce platform that provides improved scalability and flexibility for an online store.

An example of Magento 2’s flexibility is that store owners can customize and overwrite the core features of its framework according to the business requirements.

One such requirement is to add custom fields into our entities. To serve this purpose, Magento 2 offers a flexible and declarative way to extend functionality that is called extension attributes.

Magento 2 extension attributes are primarily containers used for adding an additional piece of information to our entities that allow adding additional custom data into an entity such as Product, Customer, Order, etc.

The below solution is the method to use payment extension attributes in Magento 2 that allows saving the custom field values in the database.

For example, the customers enter custom messages or comments while placing the order, and to save these comments, you need to pass it with the payment details using knockout JS.

The below method shows the same:

Method to Use Payment Extension Attributes in Magento 2

1. Create registration.php file at app\code\Vendor\Module

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Vendor_Module', __DIR__);

2. Create module.xml file at app\code\Vendor\Module\etc

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_Module" setup_version="1.0.0"/>
</config>

3. Create an extension_attributes.xml file at app\code\Vendor\Module\etc

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Quote\Api\Data\PaymentInterface">
        <attribute code="custom" type="string"/>
    </extension_attributes>
</config>

4. Create a requirejs-config.js file at app\code\Vendor\Module\view\frontend

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/place-order': {
                'Vendor_Module/js/order/place-order-mixin': true
            },
            'Magento_Checkout/js/action/set-payment-information': {
                'Vendor_Module/js/order/set-payment-information-mixin': true
            }
        }
    };

5. Create place-order-mixin.js file at app\code\Vendor\Module\view\frontend\web\js\order

define([
    'jquery',
    'mage/utils/wrapper',
    'Vendor_Module/js/order/custom-assigner'
], function ($, wrapper, ordercommentAssigner) {
    'use strict';

    return function (placeOrderAction) {

        return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) {
            ordercommentAssigner(paymentData);

            return originalAction(paymentData, messageContainer);
        });
    };
});

6. Create custom-assigner.js at app\code\Vendor\Module\view\frontend\js\order

define([
    'jquery'
], function ($) {
    'use strict';

    return function (paymentData) {

        if (paymentData['extension_attributes'] === undefined) {
            paymentData['extension_attributes'] = {};
        }

        paymentData['extension_attributes']['custom'] = jQuery('[name="ordercomment[custom]"]').val();
    };
});

7. Create set-payment-information-mixin.js at app\code\Vendor\Module\view\frontend\js\order

define([
    'jquery',
    'mage/utils/wrapper',
    'Vendor_Module/js/order/custom-assigner'
], function ($, wrapper, ordercommentAssigner) {
    'use strict';

    return function (placeOrderAction) {
        return wrapper.wrap(placeOrderAction, function (originalAction, messageContainer, paymentData) {
            ordercommentAssigner(paymentData);

            return originalAction(messageContainer, paymentData);
        });
    };
});

8. Create di.xml at app\code\Vendor\Module\etc

<?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\Checkout\Model\PaymentInformationManagement">
        <plugin name="set_payment_data_before_save"
                type="Vendor\Module\Plugin\Model\SavePaymentPlugin" sortOrder="10"/>
    </type>

    <type name="Magento\Checkout\Model\GuestPaymentInformationManagement">
        <plugin name="guest_set_payment_data_before_save"
                type="Vendor\Module\Plugin\Model\GuestSavePaymentPlugin" sortOrder="10"/>
    </type>
</config>

9. Create SavePaymentPlugin.php at Vendor\Module\Plugin\Model

<?php

namespace Vendor\Module\Plugin\Model;

use Magento\Checkout\Model\PaymentInformationManagement;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Api\Data\PaymentInterface;

class SavePaymentPlugin
{
    protected $quoteRepository;

    public function beforeSavePaymentInformationAndPlaceOrder(PaymentInformationManagement $subject,
                                                              $cartId, PaymentInterface $paymentMethod,
                                                              AddressInterface $billingAddress = null)
    {
        $orderCustom = $paymentMethod->getExtensionAttributes();

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $quoteRepository = $objectManager->create('Magento\Quote\Api\CartRepositoryInterface');

        $quote = $quoteRepository->getActive($cartId);
        $custom = $orderComment->getCustom();
        $quote->setCustom(custom);
        $quote->save();
    }
}

10. Create GuestSavePaymentPlugin.php at Vendor\Module\Plugin\Model

<?php

namespace Vendor\Module\Plugin\Model;

use Magento\Checkout\Model\PaymentInformationManagement;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Api\Data\PaymentInterface;

class GuestSavePaymentPlugin
{
    protected $quoteRepository;

    public function beforeSavePaymentInformationAndPlaceOrder(PaymentInformationManagement $subject,
                                                              $cartId, $email, PaymentInterface $paymentMethod,
                                                              AddressInterface $billingAddress = null)
    {
        $quoteIdMask = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id');
        $quoteId = $quoteIdMask->getQuoteId();
        $orderCustom = $paymentMethod->getExtensionAttributes();

        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $quoteRepository = $objectManager->create('Magento\Quote\Api\CartRepositoryInterface');

        $quote = $quoteRepository->get($quoteId);
        $custom = $orderComment->getCustom();
        $quote->setCustom(custom);
        $quote->save();
    }
}

Now you can easily save your additional field’s data after placing an order.

Done!

Feel free to share the solution with Magento Community via social media.

Thank You.

Sanjay Jethva

Article by

Sanjay Jethva

Sanjay is the co-founder and CTO of Meetanshi with hands-on expertise with Magento since 2011. He specializes in complex development, integrations, extensions, and customizations. Sanjay is one the top 50 contributor to the Magento community and is recognized by Adobe. His passion for Magento 2 and Shopify solutions has made him a trusted source for...