Перенаправление платежного шлюза Magento 2 REST API для оформления заказа
Я создаю магазин Magento 2, который продает виртуальные товары, на этом сайте можно оформить заказ непосредственно на странице товара. Клиенты никогда не будут покупать более 1 товара за раз.
При оформлении заказа используется ajax calls
, предназначенный для API REST, для выполнения этапов покупки.
Пока я могу создать новую корзину, добавить товар, установить адрес выставления счета, установить способ оплаты, собрать итоговые данные и оформить заказ. но! Одним из способов оплаты, который мы используем, является внешний кредит карточный шлюз. Для этого я создал пользовательский модуль способа оплаты, и он настраивается через API. Однако ответ, который я получаю от вызова API "placeOrder()"
, содержит только идентификатор объекта заказа.
Моя интуиция подсказывает мне, что в зависимости от способа оплаты такой вызов API добавит что-то в ответ, чтобы сообщить интерфейсу, что должно произойти перенаправление.
Я, вероятно, что-то пропустил, возможно, вызов API, который следует выполнить до placeorder
для обработки платежа и затем в случае перенаправления сделайте placeorder
по URL обратного вызова?
У кого-нибудь есть представление о том, как это должно быть сделано?
2 answers
Тот факт, что эта конечная точка определяет результат вызова как одиночный int, а не как минимум объект с идентификатором заказа и атрибутом расширения, ужасен и, вероятно, не изменится. Мы увидим, что команда Magento придумает для 2.3, когда они представят свой собственный PWA и, надеюсь, создадут новую конечную точку с лучшей поддержкой сторонних расширений. Прямо сейчас, однако, есть 2 возможности сделать:
Предполагая, что вы делаете запрос /V1/guest-carts/:cartId/payment-information
или /V1/carts/mine/payment-information
вам может:
Если вашему платежному шлюзу требуется перенаправление
GET
, просто подключитесь послеMagento\Checkout\Api\PaymentInformationManagementInterface::savePaymentInformationAndPlaceOrder()
и установите URL-адрес перенаправления в соответствии с вашими потребностями. Magento должен распознавать заголовок перенаправления.Если ваше перенаправление должно быть на самом деле запросом
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 по умолчанию на страницу успеха, поэтому вам нужно убедиться, что вы отключили его при подготовке перенаправления. Также аналогичные перезаписи необходимо выполнить для классов, обрабатывающих гостевой выезд.
Я ответил на аналогичный вопрос здесь: https://magento.stackexchange.com/a/136782/29688
Таким образом, вам необходимо вручную вызвать внешнюю службу.