Перенаправление платежного шлюза Magento 2 REST API для оформления заказа


Я создаю магазин Magento 2, который продает виртуальные товары, на этом сайте можно оформить заказ непосредственно на странице товара. Клиенты никогда не будут покупать более 1 товара за раз.

При оформлении заказа используется ajax calls, предназначенный для API REST, для выполнения этапов покупки.

Пока я могу создать новую корзину, добавить товар, установить адрес выставления счета, установить способ оплаты, собрать итоговые данные и оформить заказ. но! Одним из способов оплаты, который мы используем, является внешний кредит карточный шлюз. Для этого я создал пользовательский модуль способа оплаты, и он настраивается через API. Однако ответ, который я получаю от вызова API "placeOrder()", содержит только идентификатор объекта заказа.

Моя интуиция подсказывает мне, что в зависимости от способа оплаты такой вызов API добавит что-то в ответ, чтобы сообщить интерфейсу, что должно произойти перенаправление.

Я, вероятно, что-то пропустил, возможно, вызов API, который следует выполнить до placeorder для обработки платежа и затем в случае перенаправления сделайте placeorder по URL обратного вызова?

У кого-нибудь есть представление о том, как это должно быть сделано?

Author: Tine, 2016-01-28

2 answers

Тот факт, что эта конечная точка определяет результат вызова как одиночный int, а не как минимум объект с идентификатором заказа и атрибутом расширения, ужасен и, вероятно, не изменится. Мы увидим, что команда Magento придумает для 2.3, когда они представят свой собственный PWA и, надеюсь, создадут новую конечную точку с лучшей поддержкой сторонних расширений. Прямо сейчас, однако, есть 2 возможности сделать:

Предполагая, что вы делаете запрос /V1/guest-carts/:cartId/payment-information или /V1/carts/mine/payment-information вам может:

  1. Если вашему платежному шлюзу требуется перенаправление GET, просто подключитесь после Magento\Checkout\Api\PaymentInformationManagementInterface::savePaymentInformationAndPlaceOrder() и установите URL-адрес перенаправления в соответствии с вашими потребностями. Magento должен распознавать заголовок перенаправления.

  2. Если ваше перенаправление должно быть на самом деле запросом POST, вам необходимо переопределить определение конечной точки и предоставить свой собственный интерфейс, который объявит более разумный результат, который вы сможете обработать в браузере. Однако для этого потребуется убедиться, что родной Magento занятия также охватываются. Если вы работаете над проектом для одного клиента, это может сработать.

Создайте свой собственный модуль, т.е. Vendor_CheckoutExt как отдельный модуль композитора или в app/code/Vendor/CheckoutExt. Обязательно добавьте Magento_Checkout в sequence тег в module.xml, чтобы ваше определение было прочитано после magento one.

В etc/webapi.xml введите определение, например:

<route url="/V1/carts/mine/payment-information" method="POST">
    <service class="Vendor\CheckoutExt\Api\PaymentInformationManagementInterface" method="savePaymentInformationAndPlaceOrder"/>
    <resources>
        <resource ref="self" />
    </resources>
    <data>
        <parameter name="cartId" force="true">%cart_id%</parameter>
    </data>
</route>

Создайте интерфейс Vendor\CheckoutExt\Api\PaymentInformationManagementInterface, чтобы он выглядел как

namespace Vendor\CheckoutExt\Api;

use Magento\Checkout\Api\PaymentInformationManagementInterface as MagentoPaymentInformationManagementInterface;

interface PaymentInformationManagementInterface extends MagentoPaymentInformationManagementInterface
{

    /**
     * Set payment information and place order for a specified cart.
     *
     * @param int $cartId
     * @param \Magento\Quote\Api\Data\PaymentInterface $paymentMethod
     * @param \Magento\Quote\Api\Data\AddressInterface|null $billingAddress
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     * @return \Vendor\CheckoutExt\Api\Data\ResultInterface place order result data.
     */
    public function savePaymentInformationAndPlaceOrder(
        $cartId,
        \Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
        \Magento\Quote\Api\Data\AddressInterface $billingAddress = null
    );
}

Теперь создайте интерфейс для ответа

namespace Vendor\CheckoutExt\Api\Data;

interface ResultInterface
{
    /**
     * @param string $orderId
     * @return Vendor\CheckoutExt\Api\Data\ResultInterface 
     */
    public function setOrderId($orderId);

    /**
     * @return string
     */
    public function getOrderId();

    /**
     * Retrieve existing extension attributes object or create a new one.
     *
     * @return Vendor\CheckoutExt\Api\Data\ResultInterface 
     */
    public function getExtensionAttributes();

    /**
     * Set an extension attributes object.
     *
     * @param Vendor\CheckoutExt\Api\Data\ResultExtensionInterface $extensionAttributes
     * @return $this
     */
    public function setExtensionAttributes(
        Vendor\CheckoutExt\Api\Data\ResultExtensionInterface $extensionAttributes
    );

}

У нас есть интерфейсы готовы, поэтому модуль rest api сможет использовать его для подготовки выходных данных. Теперь нам нужно как-то реализовать их, чтобы действительно заставить запрос работать. Здесь есть 2 возможности, вы можете либо просто отдать предпочтение оригиналу и подготовить вывод с помощью плагина, либо реализовать его самостоятельно. Давайте остановимся на первом варианте, в etc/di.xml или лучше в etc/webapi/di.xml определите предпочтение

<preference for="Vendor\CheckoutExt\Api\PaymentInformationManagementInterface" type="\Magento\Checkout\Model\PaymentInformationManagement" />

Это будет работать, потому что наш интерфейс расширяет собственный интерфейс Magento, и мы не изменили функцию определение, только то, что должно быть возвращено из функции. Но класс Magento возвращает простое целое число, и только потому, что мы определили другой вывод, Magento не будет его генерировать. Нам нужно это сделать. Итак, сначала давайте реализуем класс, который мы будем использовать в ответе

<preference for="Vendor\CheckoutExt\Api\Data\ResultInterface" type="Vendor\CheckoutExt\Model\Data\OrderResponse" />

namespace Vendor\CheckoutExt\Model\Data;

use Vendor\CheckoutExt\Api\Data\ResultInterface;
use Vendor\CheckoutExt\Api\Data\ResultExtensionInterface;
use Magento\Framework\Model\AbstractExtensibleModel;

class Result extends AbstractExtensibleModel implements ResultInterface
{
    /**
     * @param string $orderId
     * @return \Vendor\CheckoutExt\Api\Data\ResultInterface
     */
    public function setOrderId($orderId)
    {
        return $this->setData(self::ORDER_ID, $orderId);
    }

    /**
     * @return string
     */
    public function getOrderId()
    {
        return $this->_getData(self::ORDER_ID);
    }

    /**
     * @param string $incrementId
     * @return \Vendor\CheckoutExt\Api\Data\ResultInterface
     */
    public function setOrderRealId($incrementId)
    {
        return $this->setData(self::ORDER_REAL_ID, $incrementId);
    }

    /**
     * @return string
     */
    public function getOrderRealId()
    {
        return $this->_getData(self::ORDER_REAL_ID);
    }

    /**
     * @return \Vendor\CheckoutExt\Api\Data\ResultExtensionInterface
     */
    public function getExtensionAttributes()
    {
        $extensionAttributes = $this->_getExtensionAttributes();
        if (!$extensionAttributes) {
            /** @var ResultExtensionInterface $extensionAttributes */
            $extensionAttributes = $this->extensionAttributesFactory->create(ResultInterface::class);
        }

        return $extensionAttributes;
    }

    /**
     * @param \Vendor\CheckoutExt\Api\Data\ResultExtensionInterface $extensionAttributes
     * @return \Vendor\CheckoutExt\Api\Data\ResultInterface
     */
    public function setExtensionAttributes(ResultExtensionInterface $extensionAttributes)
    {
        return $this->_setExtensionAttributes($extensionAttributes);
    }
}

И последняя часть головоломки - это плагин, который подключится после вызова оригинального метода заказа места, чтобы изменить возвращаемое целое число в нужный нам объект. Снова в di.xml определите

<type name="Magento\Checkout\Api\PaymentInformationManagementInterface">
    <plugin name="vendorCheckoutExtSaveOrderResultPlugin" type="Vendor\CheckoutExt\Plugin\Checkout\PaymentInformationManagement" sortOrder="1" />
</type>

И плагин код

namespace Vendor\CheckoutExt\Plugin\Checkout;

use Vendor\CheckoutExt\Api\Data\ResultInterface;
use Vendor\CheckoutExt\Api\Data\ResultInterfaceFactory;
use Magento\Checkout\Api\PaymentInformationManagementInterface;

class PaymentInformationManagement
{
    /** @var ResultFactory */
    private $resultFactory;

    /**
     * @param ResultInterfaceFactory $resultFactory
     */
    public function __construct(ResultInterfaceFactory $resultFactory)
    {
        $this->resultFactory = $resultFactory;
    }

    /**
     * @param PaymentInformationManagementInterface $subject
     * @param int $orderId
     * @return ResultInterface
     */
    public function afterSavePaymentInformationAndPlaceOrder(
        PaymentInformationManagementInterface $subject,
        $orderId
    ) {
        /** @var ResultInterface $obj */
        $obj = $this->resultFactory->create();
        $obj->setOrderId($orderId);

        return $obj;
    }
}

Итак, теперь результатом является объект, реализующий атрибуты extension_attributes. В своем пользовательском модуле api платежей вы можете определить аналогичный плагин, просто убедитесь, что сортировщик выше, чем указанный выше, поэтому вы уже получаете объект Vendor\CheckoutExt\Api\Data\ResultInterface в качестве аргумента.

В модуле оплаты создайте файл etc/extension_attributes.xml с помощью

<?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="Vendor\ChecoutExt\Api\Data\ResultInterface">
        <attribute code="my_payment_gateway_data" type="Vendor\CustomPayment\Api\Data\RedirectInterface" />
    </extension_attributes>
</config>

Создайте интерфейс и его реализацию в соответствии с вашими потребностями и в плагине

namespace Vendor\CustomPayment\Plugin\Checkout;

use Vendor\CustomPayment\Api\Data\RedirectInterface;
use Vendor\CustomPayment\Api\Data\RedirectInterfaceFactory;
use Vendor\CheckoutExt\Api\Data\ResultInterface;
use Magento\Checkout\Api\PaymentInformationManagementInterface;

class PaymentInformationManagement
{
    /** @var RedirectInterfaceFactory */
    private $redirectFactory;

    /**
     * @param RedirectInterfaceFactory $redirectFactory
     */
    public function __construct(RedirectInterfaceFactory $redirectFactory)
    {
        $this->redirectFactory = $redirectFactory;
    }

    /**
     * @param PaymentInformationManagementInterface $subject
     * @param ResultInterface $orderId
     * @return ResultInterface
     */
    public function afterSavePaymentInformationAndPlaceOrder(
        PaymentInformationManagementInterface $subject,
        $orderId
    ) {
        /** @var ResultInterface $obj */
        $redirect = $this->redirectFactory->create();

        $extensionAttributes = $orderId->getExtensionAttributes();
        $extensionAttributes->setMyPaymentGatewayData($redirect);
        $orderId->setExtensionAttributes($extensionAttributes);

        return $orderId;
    }
}

Теперь ответ является правильным json с extension_attributes в нем, которые вы можете проверить и подключить. Возможно, есть некоторые другие элементы, которые следует использовать в качестве перенаправления magento по умолчанию на страницу успеха, поэтому вам нужно убедиться, что вы отключили его при подготовке перенаправления. Также аналогичные перезаписи необходимо выполнить для классов, обрабатывающих гостевой выезд.

 4
Author: Zefiryn, 2018-02-17 15:39:07

Я ответил на аналогичный вопрос здесь: https://magento.stackexchange.com/a/136782/29688

Таким образом, вам необходимо вручную вызвать внешнюю службу.

 0
Author: Mahmoud Tantawy, 2017-04-13 12:55:01