preloder

Setting Payment Additional Data in Magento 2

Prev Next

Magento team constantly addresses security issues when it comes to payment integrations. One of the improvements were changes into the Magento\Payment module. The Magento\Payment module is no longer responsible for setting Payment data for the Magento\Sales\Model\Order\Payment object. Starting from the Magento 2.0.6 release and further Magento 2.1 release custom payment module should be responsible for adding all necessary Payment information into the Payment object.

In this article, I am going to explain these changes and share advice on how to properly set Additional Payment Data as part of custom integration with a Payment Service Provider.

Overview

Up to Magento 2.0.6 release, the Payment module was responsible for setting Additional Payment Data. Usually, these data come from a payment form which is located on the checkout billing page. Payment form can be found in the vendor/magento/module-payment/view/frontend/web/template/payment/cc-form.html template file.

JavaScript Component prepares Payment data and puts it inside the array with additional_data key together with other quote related data once customer submits the payment form.

The vendor/magento/module-payment/view/frontend/web/js/view/payment/cc-form.js JavaScript Component’s getData method listed below.

getData: function() {
    return {
        'method': this.item.method,
        'additional_data': {
            'cc_cid': this.creditCardVerificationNumber(),
            'cc_ss_start_month': this.creditCardSsStartMonth(),
            'cc_ss_start_year': this.creditCardSsStartYear(),
            'cc_type': this.creditCardType(),
            'cc_exp_year': this.creditCardExpYear(),
            'cc_exp_month': this.creditCardExpMonth(),
            'cc_number': this.creditCardNumber()
        }
    };
}

Once prepared, payment data is sent via HTTP POST using vendor/magento/module-checkout/view/frontend/web/js/action/place-order.js JavaScript Component.

The Magento\Payment\Model\Method\Adapter::assignData() method used to add all data from the $data argument (payment form) directly to the Magento\Sales\Model\Order\Payment object.

public function assignData(\Magento\Framework\DataObject $data)
{
    $this->eventManager->dispatch(
        'payment_method_assign_data_' . $this->getCode(),
        [
            'method' => $this,
            'data' => $data
        ]
    );

    $this->getInfoInstance()->addData($data->getData());
    return $this;
}

By using $this->getInfoInstance()->addData($data->getData()); call payment modules (e.g. Magento\Authorize.net, Magento\Braintree, Magento\Paypal and custom ones) were using data assigned by the Adapter::assignData() method to set it as part of the Payment Gateway request calls.

Magento 2.0.6 and later versions has no $this->getInfoInstance()->addData($data->getData()); call in the Adapter::assignData()method. Now, Payment module which integrates Magento 2 with Payment Service Provider is responsible for assigning Additional Payment Data.

The following is a Adapter::assignData() method from the Magento 2.0.6 release.

public function assignData(\Magento\Framework\DataObject $data)
{
    $this->eventManager->dispatch(
        'payment_method_assign_data_' . $this->getCode(),
        [
            'method' => $this,
            'data' => $data
        ]
    );

    return $this;
}

Assigning Additional Payment Data

In order to assign Additional Payment Data to the Magento\Sales\Model\Order\Payment object we have to create an observer in our payment module and listen to the payment_method_assign_data_<payment_code> event from the Adapter class.

The app/code/Pronko/Payment/etc/events.xml configuration file should look like the following:

<config>
    <event name="payment_method_assign_data_pronko_custom">
        <observer name="pronko_custom_gateway_data_assign" instance="Pronko\Payment\Observer\DataAssignObserver" />
    </event>
</config>

The pronko_custom is a code of your payment method.

The AbstractDataAssignObserver class

The Magento\Payment\Observer\AbstractDataAssignObserver class provides useful methods for reading variables from the Event object. The class also responsible for verification of the argument provided as part of dispatching payment_method_assign_data_<payment> event.

Method Description Magento Version
readMethodArgument Returns current Magento\Payment\Model\Method\Adater instance 2.0.x and 2.1
readPaymentModelArgument Returns Magento\Sales\Model\Order\Payment instance 2.1
readDataArgument Returns Magento\Framework\DataObject instance with all Additional Payment Data 2.0.x and 2.1

The DataAssignObserver class

The DataAssignObserver observer class extends the AbstractDataAssignObserver class and listens for the payment_method_assign_data_pronko_custom event. The following example shows one of ways setting payment data into the Magento\Sales\Model\Order\Payment object.

namespace Pronko\Payment\Observer;

use Magento\Framework\DataObject;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\Exception\LocalizedException;
use Magento\Payment\Observer\AbstractDataAssignObserver;
use Magento\Quote\Api\Data\PaymentInterface;
use Magento\Payment\Model\InfoInterface;

class DataAssignObserver extends AbstractDataAssignObserver
{
    /**
     * @param Observer $observer
     * @throws LocalizedException
     */
    public function execute(Observer $observer)
    {
        $data = $this->readDataArgument($observer);

        $additionalData = $data->getData(PaymentInterface::KEY_ADDITIONAL_DATA);
        if (!is_array($additionalData)) {
            return;
        }

        $additionalData = new DataObject($additionalData);
        $paymentMethod = $this->readMethodArgument($observer);

        $payment = $observer->getPaymentModel();
        if (!$payment instanceof InfoInterface) {
            $payment = $paymentMethod->getInfoInstance();
        }

        if (!$payment instanceof InfoInterface) {
            throw new LocalizedException(__('Payment model does not provided.'));
        }

        $payment->setCcLast4(substr($additionalData->getData('cc_number'), -4));
        $payment->setCcType($additionalData->getData('cc_type'));
        $payment->setCcExpMonth($additionalData->getData('cc_exp_month'));
        $payment->setCcExpYear($additionalData->getData('cc_exp_year'));
    }
}

You may notice the usage of the getPaymentModel() method. This is the main difference between Magento 2.0.6 and Magento 2.1 versions. Starting with Magento 2.1 the Adapter::assignData() method dispatches the payment_method additional parameter.

The Magento\Payment\Model\Method\Adapter class.

    public function assignData(\Magento\Framework\DataObject $data)
    {
        $this->eventManager->dispatch(
            'payment_method_assign_data_' . $this->getCode(),
            [
                AbstractDataAssignObserver::METHOD_CODE => $this,
                AbstractDataAssignObserver::MODEL_CODE => $this->getInfoInstance(),
                AbstractDataAssignObserver::DATA_CODE => $data
            ]
        );

        $this->eventManager->dispatch(
            'payment_method_assign_data',
            [
                AbstractDataAssignObserver::METHOD_CODE => $this,
                AbstractDataAssignObserver::MODEL_CODE => $this->getInfoInstance(),
                AbstractDataAssignObserver::DATA_CODE => $data
            ]
        );

        return $this;
    }

If you want to have a backward compatible implementation with Magento 2.0.x releases you have to consider using payment_modelkey in the AssignDataObserver class as optional.

Max with his team of Magento enthusiasts won the Best Magento 1 to Magento 2 Migration Award at the Magento Imagine 2017. As CEO at Pronko Consulting, he is actively working with Magento 2, delivering superior customer experience. As one of the most famous developers on a market and in the Magento Community, he launched Magento 2 blog, Magento DevChannel – YouTube channel in 2017 where he shares his knowledge, experience and the best practices in the Magento field.

Posted on Jun 12, 2016

The Devletter

Subscribe to my Magento 2 tips and tutorials content.
Every week I send an interesting email directly to your inbox

Related Posts

Show
Hide
Pre-order the Magento 2 payment integration course TODAY PRE-ORDER
Hello. Add your message here.