Magento 2 Payment Integration with Converge Elavon

If you are looking to learn how to develop a payment integration for the Magento / Adobe Commerce e-commerce platform, look no further. In this article, I cover in detail the first steps. By the end of the article, you will learn how to render a payment method on the checkout payments page, how to process a payment form on a server, and you will place your first order with the newly created payment integration.

Module Registration

Let's add the required module registration files for a new Magento 2 / Adobe Commerce module.

The registration.php file is responsible for adding the package. The MageMastery_Converge component package name should be added via the ComponentRegistrar::register() method so that the package is registered in Magento 2 / Adobe Commerce.

use Magento\Framework\Component\ComponentRegistrar;

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

The module.xml file is responsible for further Magento 2 module registration in the system. Place the module.xml file in the MageMastery/Converge/etc directory.

xml
<?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="MageMastery_Converge" />
</config>

Payment Adapter Configuration

The first thing that we should add to make a payment method module for Magento 2 / Adobe Commerce is the payment adapter configuration.

What is Payment Adapter Configuration, you ask? Well, it is a dependency injection (DI) configuration that is done for the Magento\Payment\Model\Method\Adapter class. The DI configuration should be located in the configuration file di.xml.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
// payment configuration
</config>

Let's start with the virtual type for the Magento\Payment\Model\Method\Adapter class. We should provide at least 4 arguments, 2 of them should be custom and the other 2 (formBlockType and infoBlockType) can be re-used from the Magento Payment module.

xml
<virtualType name="MageMasteryConvergeAdapter" type="Magento\Payment\Model\Method\Adapter">
<arguments>
<argument name="code" xsi:type="string">magemastery_converge</argument>
<argument name="valueHandlerPool" xsi:type="object">MageMasteryConvergeValueHandlerPool</argument>
<argument name="formBlockType" xsi:type="string">Magento\Payment\Block\Form\Cc</argument>
<argument name="infoBlockType" xsi:type="string">Magento\Payment\Block\Info</argument>
</arguments>
</virtualType>

The MageMasteryConvergeAdapter virtual type name is going to be used in the configuration file config.xml as the name of the model class for Payment implementation. We will cover the config.xml below. Let's get back to the di.xml implementation.

The magemastery_converge value for the code argument is a payment method code, that we are going to be using throughout the extension implementation. It is a unique code of the payment method, that should be unique even if you decide to install more than one payment method module per Magento 2 application.

The MageMasteryConvergeValueHandlerPool is the virtual type configuration of the valueHandlerPool argument. The value handler pool is responsible for providing a configuration class for all the configuration values that are used in a payment method implementation. It provides a class that knows where to read configuration settings for a particular payment method.

Value Handler Pool Configuration

The value handler pool argument configuration is named MageMasteryConvergeValueHandlerPool. This name represents the virtual type that is configured in the same di.xml configuration file of the module.

xml
<virtualType name="MageMasteryConvergeValueHandlerPool" type="Magento\Payment\Gateway\Config\ValueHandlerPool">
  <arguments>
      <argument name="handlers" xsi:type="array">
          <item name="default" xsi:type="string">MageMasteryConvergeConfigValueHandler</item>
      </argument>
  </arguments>
</virtualType>

The Magento\Payment\Gateway\Config\ValueHandlerPool class has to be configured in a custom way so that we can use a new instance of this class for our purposes. So the new default handler is added to the configured ValueHandlerPool class (that is a virtual type) and named MageMasteryConvergeConfigValueHandler. That's another virtual type configuration we are going to add shortly.

The MageMasteryConvergeConfigValueHandler virtual type is the configuration of the ConfigValueHandler class where we add a custom (yet another virtual type) value for the configInterface argument.

xml
<virtualType name="MageMasteryConvergeConfigValueHandler" type="Magento\Payment\Gateway\Config\ConfigValueHandler">
<arguments>
<argument name="configInterface" xsi:type="object">MageMasteryConvergeGatewayConfig</argument>
</arguments>
</virtualType>

Finally, the MageMasteryConvergeGatewayConfig virtual type configuration allows us to provide the payment method code into the configured object of the Magento\Payment\Gateway\Config\Config class. The magemastery_converge payment method code is used as the value.

xml
<virtualType name="MageMasteryConvergeGatewayConfig" type="Magento\Payment\Gateway\Config\Config">
<arguments>
<argument name="methodCode" xsi:type="string">magemastery_converge</argument>
</arguments>
</virtualType>

Final Payment Configuration

The final configuration of the etc/di.xml file is provided below. As you can see from the configuration file, we've configured 4 virtual types so that the resulting MageMasteryConvergeAdapter reference name can be further used in the file config.xml.

xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="MageMasteryConvergeAdapter" type="Magento\Payment\Model\Method\Adapter">
<arguments>
<argument name="code" xsi:type="string">magemastery_converge</argument>
<argument name="valueHandlerPool" xsi:type="object">MageMasteryConvergeValueHandlerPool</argument>
<argument name="formBlockType" xsi:type="string">Magento\Payment\Block\Form\Cc</argument>
<argument name="infoBlockType" xsi:type="string">Magento\Payment\Block\Info</argument>
</arguments>
</virtualType>

<virtualType name="MageMasteryConvergeValueHandlerPool" type="Magento\Payment\Gateway\Config\ValueHandlerPool">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="default" xsi:type="string">MageMasteryConvergeConfigValueHandler</item>
</argument>
</arguments>
</virtualType>

<virtualType name="MageMasteryConvergeConfigValueHandler" type="Magento\Payment\Gateway\Config\ConfigValueHandler">
<arguments>
<argument name="configInterface" xsi:type="object">MageMasteryConvergeGatewayConfig</argument>
</arguments>
</virtualType>

<virtualType name="MageMasteryConvergeGatewayConfig" type="Magento\Payment\Gateway\Config\Config">
<arguments>
<argument name="methodCode" xsi:type="string">magemastery_converge</argument>
</arguments>
</virtualType>
</config>

Payment Configuration

The payment configuration for any payment method module for Magento 2 / Adobe Commerce requires not only the virtual types configured in the Payment Adapter Configuration section of this article but also the payment configuration that is located in the file config.xml.

The configuration file config.xml can be used for storing the default configuration settings of a Magento 2 / Adobe Commerce module. The file is also responsible for storing payment-related configuration settings.

Such settings include:

  • whether a payment method is enabled by default,
  • what model class should be instantiated when the payment method is loaded,
  • the title of the payment method when rendered on checkout or admin
  • other settings that affect how the payment method should behave in different scenarios e.g. creating invoices, credit memos, etc.

Below is the summary of all payment method configuration settings used for the MageMastery Converge extension.

Setting Name Comment
Active Tells whether the payment method is enabled in the system.
Title Payment method title.
Model The class or virtual type name that is instantiated.
Can Use Checkout Can the payment method be used for the checkout experience?
Can Capture Can the payment be captured during order creation?
Payment Action A payment processing flow, whether an amount should be authorized only or captured. 
Can Authorize Can the payment be authorized during order creation?
Credit Card Types Supported list of credit card types.

The config.xml file format for any payment method integration is below:

xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<payment>
<payment_method_code>
<setting_1>1</setting_1>
<!-- other settings -->
</payment_method_code>
</payment>
</default>
</config>

All the payment-related configuration settings are located under the default/payment XML path. Inside the payment XML node, there is a payment method code. So we should be using the <magemastery_converge> XML node. Payment settings are then added as a child to the payment method code node.

Below is the config.xml file with Converge Elavon payment method integration.

xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<payment>
<magemastery_converge>
<active>1</active>
<title>Mage Mastery Converge Payment</title>
<payment_action>authorize_capture</payment_action>
<model>MageMasteryConvergeAdapter</model>
<can_use_checkout>1</can_use_checkout>
<can_capture>1</can_capture>
<can_authorize>1</can_authorize>
<cctypes>AE,VI,MC,DI,JCB,DN</cctypes>

<xml_api_endpoint_demo>https://api.demo.convergepay.com/VirtualMerchantDemo/processxml.do</xml_api_endpoint_demo>
<xml_api_endpoint_production>https://api.convergepay.com/VirtualMerchant/processxml.do</xml_api_endpoint_production>
<mode>demo</mode>
</magemastery_converge>
</payment>
</default>
</config>

You may notice 2 additional XML nodes that we haven't talked about. These are xml_api_endpoint_demoxml_api_endpoint_production, and mode. These are custom payment settings that are going to be used for the MageMastery_Converge payment integration module. You can add any settings for your convenience.

Custom payment settings from explained:

Setting Name Comment
mode Tells whether it is the demo or production mode selected. Affects endpoint URL selection.
xml_api_endpoint_demo Demo Converge Elavon API endpoint.
xml_api_endpoint_production Production Converge Elavon API endpoint.

Registering Payment Method with Config Provider Class

Before we can actually see the configured payment method on the checkout payments page, the payment method code should be added to the config providers list. The Magento\Payment\Model\CcGenericConfigProvider class is responsible for holding and processing all payment methods for rendering and retrieving configuration settings per method. We should register the magemastery_converge payment method code in the CcGenericConfigProvider class with the help of the DI configuration.

The file should be placed in the etc/frontend/di.xml location:

<?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\Payment\Model\CcGenericConfigProvider">
        <arguments>
            <argument name="methodCodes" xsi:type="array">
                <item name="magemastery_converge" xsi:type="string">magemastery_converge</item>
            </argument>
        </arguments>
    </type>
</config>

The methodCodes argument holds an array of payment method codes.

How does rendering work?

In order to render a payment method on a checkout payments page, the layout should be added with the component registration. It is usually a straightforward process if you know where to add the configuration. Usually, when it comes to checkout layout files (here I refer to the checkout_index_index.xml file), the custom configuration is added in a nested way. Basically, like everything in Magento / Adobe Commerce when it comes to adding blocks or UI Components.

Next, we should create a JavaScript file to register the Payment Method Renderer JavaScript component and the HTML template to render any payment form markup as per the requirements of a business owner. Any additional actions or payment validators may be placed directly in the Payment Method Renderer or a separate JavaScript component can be created and injected via the define([], function () {}); dependency injection. It is important to mention that the default Magento / Adobe Commerce storefront uses the RequireJs JavaScript library for dependency management of JavaScript components. On top of that, Magento has its own UI Components that are based on RequireJs and KnockoutJs libraries.

All layout configuration files, JavaScript components, and templates are located in the view/frontend directory of the Magento 2 module.

Layout Configuration

Let's focus on what we should add to the layout configuration file. Here we register a new payment method with the code magemastery_converge, we also provide a JavaScript component location, so that the MageMastery_Converge/js/view/payment/converge component is converted into a file path location and is like MageMastery/Converge/view/frontend/js/view/payment/converge.js. Finally, there is an additional configuration for the isBillingAddressRequired setting inside the XML node with the name methods... just because Magento has a bug and it wouldn't allow to render the payment method without this configuration.

xml
<item name="magemastery_converge" xsi:type="array">
<item name="component" xsi:type="string">MageMastery_Converge/js/view/payment/converge</item>
<item name="methods" xsi:type="array">
<item name="magemastery_converge" xsi:type="array">
<item name="isBillingAddressRequired" xsi:type="boolean">true</item>
</item>
</item>
</item>

The above configuration should be placed inside the new configuration file called checkout_index_index.xml which is located in the view/frontend/layout directory of the MageMastery_Converge module. There is a place in the checkout_index_index.xml that is allocated for payment method renderers. The vendor/magento/module-checkout/view/frontend/layout/checkout_index_index.xml can give you a hint.

xml
<item name="renders" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
<item name="children" xsi:type="array">
<!-- merge payment method renders here -->
</item>
</item>

We are going to place our configuration in the "merge payment method renders here" section.

Here is the final checkout_index_index.xml file for MageMastery Converge payment method registration.

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="billing-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="payment" xsi:type="array">
<item name="children" xsi:type="array">
<item name="renders" xsi:type="array">
<item name="children" xsi:type="array">
<item name="magemastery_converge" xsi:type="array">
<item name="component" xsi:type="string">MageMastery_Converge/js/view/payment/converge</item>
<item name="methods" xsi:type="array">
<item name="magemastery_converge" xsi:type="array">
<item name="isBillingAddressRequired" xsi:type="boolean">true</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>

Renderer List

Moving on to the renderer file that is mentioned in the layout configuration file. The converge.js file is responsible for adding the path to the UI Component JavaScript file which will then render the payment method. Sounds confusing, right?

Here is an example of the file view/frontend/web/js/view/payment/converge.js.

define([
'uiComponent',
'Magento_Checkout/js/model/payment/renderer-list'
], function (Component, rendererList) {
'use strict';

rendererList.push({
type: 'magemastery_converge',
component: 'MageMastery_Converge/js/view/payment/method-renderer/cc-form'
});

return Component.extend({});
});

The rendererList.push(); method is used to register the new JavaScript component that is responsible for rendering.

Configuration object includes type (payment method code) and component location:

{
type: 'magemastery_converge',
component: 'MageMastery_Converge/js/view/payment/method-renderer/cc-form'
}

Payment Method Renderer

The payment method renderer is responsible for rendering an HTML template and providing the necessary logic and data for rendering. The cc-form.js UI component extends the existing Magento_Payment/js/view/payment/cc-form component and includes additional logic. With the help of the Component.extend(); function call, we can add custom functions.

define([
'Magento_Payment/js/view/payment/cc-form',
'jquery',
'Magento_Payment/js/model/credit-card-validation/validator'
], function (Component, $) {
'use strict';

return Component.extend({
defaults: {
template: 'MageMastery_Converge/payment/cc-form',
code: 'magemastery_converge'
},

getCode: function () {
return this.code;
},

isActive: function () {
return this.getCode() === this.isChecked();
},

getSelector: function (field) {
return '#' + this.getCode() + '_' + field;
},

validate: function () {
const form = $(this.getSelector('payment-form'));
form.validation();

return form.valid();
}
});
});

The Payment Method Renderer defines custom functions such as isActive(), getCode(), getSelector(), and validate();. Both isActive() and getCode() functions are directly used in the Payment Form. The other two functions are responsible for the form validation when a customer clicks the "Place Order" button.

The defaults section of the Payment Method Renderer component provides the payment method code and the path to the Payment Form.

Payment Form

The payment form is an HTML file that provides form markup and some placeholders if we want to render custom messages and register new UI Components. The cc-form.html file is placed in the view/frontend/web/template/payment location. The file format follows the KnockoutJs templating and provides additional capabilities for rendering and logic processing. 

<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 class="label" data-bind="attr: {'for': getCode()}">
<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 class="form" data-bind="attr: {'id': getCode() + '_payment-form'}">
<fieldset data-bind="attr: {class: 'fieldset payment items ccard ' + getCode(), id: 'payment_form_' + getCode()}">
<!-- ko if: (isShowLegend())-->
<legend class="legend">
<span><!-- ko i18n: 'Credit Card Information'--><!-- /ko --></span>
</legend><br />
<!-- /ko -->
<div class="field type">
<div class="control">
<ul class="credit-card-types">
<!-- ko foreach: {data: getCcAvailableTypesValues(), as: 'item'} -->
<li class="item" data-bind="css: {
_active: $parent.selectedCardType() == item.value,
_inactive: $parent.selectedCardType() != null && $parent.selectedCardType() != item.value
} ">
<!--ko if: $parent.getIcons(item.value) -->
<img data-bind="attr: {
'src': $parent.getIcons(item.value).url,
'alt': item.type,
'width': $parent.getIcons(item.value).width,
'height': $parent.getIcons(item.value).height
}">
<!--/ko-->
</li>
<!--/ko-->
</ul>
<input type="hidden"
name="payment[cc_type]"
class="input-text"
value=""
data-bind="attr: {id: getCode() + '_cc_type', 'data-container': getCode() + '-cc-type'},
value: creditCardType
">
</div>
</div>
<div class="field number required">
<label data-bind="attr: {for: getCode() + '_cc_number'}" class="label">
<span><!-- ko i18n: 'Credit Card Number'--><!-- /ko --></span>
</label>
<div class="control">
<input type="number" name="payment[cc_number]" class="input-text" value=""
oncopy="return false;"
oncut="return false;"
onpaste="return false;"
data-bind="attr: {
autocomplete: off,
id: getCode() + '_cc_number',
title: $t('Credit Card Number'),
'data-container': getCode() + '-cc-number',
'data-validate': JSON.stringify({'required-number':true, 'validate-card-type':getCcAvailableTypesValues(), 'validate-card-number':'#' + getCode() + '_cc_type', 'validate-cc-type':'#' + getCode() + '_cc_type'})},
enable: isActive($parents),
value: creditCardNumber,
valueUpdate: 'keyup' "/>
</div>
</div>
<div class="field date required" data-bind="attr: {id: getCode() + '_cc_type_exp_div'}">
<label data-bind="attr: {for: getCode() + '_expiration'}" class="label">
<span><!-- ko i18n: 'Expiration Date'--><!-- /ko --></span>
</label>
<div class="control">
<div class="fields group group-2">
<div class="field no-label month">
<div class="control">
<select name="payment[cc_exp_month]"
class="select select-month"
data-bind="attr: {id: getCode() + '_expiration', 'data-container': getCode() + '-cc-month', 'data-validate': JSON.stringify({required:true, 'validate-cc-exp':'#' + getCode() + '_expiration_yr'})},
enable: isActive($parents),
options: getCcMonthsValues(),
optionsValue: 'value',
optionsText: 'month',
optionsCaption: $t('Month'),
value: creditCardExpMonth">
</select>
</div>
</div>
<div class="field no-label year">
<div class="control">
<select name="payment[cc_exp_year]"
class="select select-year"
data-bind="attr: {id: getCode() + '_expiration_yr', 'data-container': getCode() + '-cc-year', 'data-validate': JSON.stringify({required:true})},
enable: isActive($parents),
options: getCcYearsValues(),
optionsValue: 'value',
optionsText: 'year',
optionsCaption: $t('Year'),
value: creditCardExpYear">
</select>
</div>
</div>
</div>
</div>
</div>
<!-- ko if: (hasVerification())-->
<div class="field cvv required" data-bind="attr: {id: getCode() + '_cc_type_cvv_div'}">
<label data-bind="attr: {for: getCode() + '_cc_cid'}" class="label">
<span><!-- ko i18n: 'Card Verification Number'--><!-- /ko --></span>
</label>
<div class="control _with-tooltip">
<input type="number"
autocomplete="off"
class="input-text cvv"
name="payment[cc_cid]"
value=""
oncopy="return false;"
oncut="return false;"
onpaste="return false;"
data-bind="attr: {id: getCode() + '_cc_cid',
title: $t('Card Verification Number'),
'data-container': getCode() + '-cc-cvv',
'data-validate': JSON.stringify({'required-number':true, 'validate-card-cvv':'#' + getCode() + '_cc_type'})},
enable: isActive($parents),
value: creditCardVerificationNumber" />
<div class="field-tooltip toggle">
<span class="field-tooltip-action action-cvv"
tabindex="0"
data-toggle="dropdown"
data-bind="attr: {title: $t('What is this?')}, mageInit: {'dropdown':{'activeClass': '_active'}}">
<span><!-- ko i18n: 'What is this?'--><!-- /ko --></span>
</span>
<div class="field-tooltip-content"
data-target="dropdown"
data-bind="html: getCvvImageUnsanitizedHtml()"></div>
</div>
</div>
</div>
<!-- /ko -->
</fieldset>
</form>

<div class="checkout-agreements-block">
<!-- ko foreach: $parent.getRegion('before-place-order') -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!--/ko-->
</div>
<div class="actions-toolbar">
<div class="primary">
<button data-role="review-save"
type="submit"
data-bind="
attr: {title: $t('Place Order')},
enable: (getCode() == isChecked()),
click: placeOrder,
css: {disabled: !isPlaceOrderActionAllowed()}
"
class="action primary checkout"
disabled>
<span data-bind="i18n: 'Place Order'"></span>
</button>
</div>
</div>
</div>
</div>

Data Assign Observer Class

The Data Assign Observer class is responsible for transferring data from the Converge payment form into the payment object. In a nutshell, the DataAssignObserver class must implement the interface Magento\Framework\Event\ObserverInterface and implement the method execute(Observer $observer): void. The AbstractDataAssignObserver class, however, adds additional methods such as readDataArgument() or readPaymentModelArgument() that are good when it comes to retrieving data from the observer object and validating it before use.

Here is a sample of a Data Assign Observer class that will be used for assigning data from the payment form to the order payment object.

namespace MageMastery\Converge\Observer;

use Magento\Framework\DataObject;
use Magento\Framework\DataObjectFactory;
use Magento\Framework\Event\Observer;
use Magento\Payment\Observer\AbstractDataAssignObserver;

class DataAssignObserver extends AbstractDataAssignObserver
{
public function execute(Observer $observer): void
{

}
}

The method execute() accepts an argument $observer. This argument should be used to receive the data that is sent to us from the checkout payment form. The variable $data is an instance of the DataObject and we can use the method called getData() to retrieve an additional_data associate array.

public function execute(Observer $observer): void
{
$data = $this->readDataArgument($observer)->getData('additional_data');

if (empty($data)) return;

$payment = $this->readPaymentModelArgument($observer);

$payment->setData('cc_number', $data['cc_number']);
$payment->setData('cc_type', $data['cc_type']);
$payment->setData('cc_exp_month', $data['cc_exp_month']);
$payment->setData('cc_exp_year', $data['cc_exp_year']);
$payment->setData('cc_cid', (int) $data['cc_cid']);
$payment->setData('cc_last_4', substr($data['cc_number'], -4));
}

The assumption here is that all form fields that are stored in the $data variable are available for use. Otherwise, an additional check should be added.

Registering for the Observer Class

The configuration file events.xml provides information on the registered observers for a particular event. The MageMastery\Converge\Observer\DataAssignObserver class is triggered for the payment_method_assign_data_magemastery_converge event. Place the events.xml file in the MageMastery/Converge/etc directory.

xml
<?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="payment_method_assign_data_magemastery_converge">
<observer name="MageMastery\Converge\Observer\DataAssignObserver"
instance="MageMastery\Converge\Observer\DataAssignObserver"/>
</event>
</config>

The payment_method_assign_data_magemastery_converge event name is not available when you try and search in the Magento 2 source code. It is auto-generated and based on the prefix payment_method_assign_data and the payment method code magemastery_converge. Take it into consideration when you build your own custom payment integration with Magento 2.

Show Time

If the above code has been added correctly, you should have the following payment method functionality:

  1. Converge Elavon payment method is visible on the checkout payments page
  2. Payment card data submitted via the Converge Elavon payment form should be transferred to a Payment object
  3. Order is successfully created with Converge Elavon as the payment

By the way, you can join an online course where I show how to create a payment method integration with Authorize.NET and Magento / Adobe Commerce. The course is called Magento 2 Payment Method and includes over 3 hours of video lessons that will show you step-by-step instructions on how to create a payment method module for Magento 2.