Jun 12, 2016

Setting Payment Additional Data in Magento 2

Setting Payment Additional Data in Magento 2
I explain changes and share advice on how to properly set Additional Payment Data as part of custom integration with the Payment Service Provider

Magento team constantly addresses security issues when it comes to payment integrations. One of 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 comes 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 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 usage of the getPaymentModel() method. This is a main difference between Magento 2.0.6 and Magento 2.1 versions. Starting from Magento 2.1 the Adapter::assignData() method dispatches the payment_method additional parameter.

// 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 backward compatible implementation with Magento 2.0.x releases you have to consider using payment_model key in the AssignDataObserver class as optional.

magento 2 payment

Share Article

Related Articles

Comments

Next Article Previous Article

Join Devletter Community

Subscribe to FREE Magento 2 tips and tutorials email I send every week.

I will send you my 3 best tutorials with interesting content immediately.

LinkedIn Twitter Facebook