25 декабря 2023

Интеграция платежного шлюза с WooCommerce Checkout Block — часть 1 «Простой вариант»

6 минут

WooCommerce – одна из самых востребованных и гибких платформ электронной коммерции в мире, продолжает завоевывать сердца онлайн-предпринимателей и многомиллионных сообществ пользователей. Ее успех не случаен, и одной из важнейших составляющих этого успеха является способность интегрировать различные инструменты и сервисы, обеспечивая таким образом беспрепятственное функционирование онлайн-магазинов. 

Одним из важнейших типов интеграции является добавление платежных шлюзов для простой оплаты заказов. Эта тема подробно раскрыта, но с недавних пор WooCommerce активно развивает поддержку редактора Gutenberg, и появился отдельный блок для создания страницы оплаты

Для этого блока нельзя просто использовать уже созданный платежный шлюз. Требуются доработки связанные с тем, что Checkout Block написан при помощи JS библиотеки React, а значит и нам нужно написать прослойку на React для работы платежного шлюза и правильно её подключить в PHP

Простой вариант подразумевает, что в плагине есть только один платежный шлюз и его надо быстро и просто зарегистрировать в WooCommerce Checkout block.

Для начала поймем как проверить, что шлюз интегрирован с Checkout block. Это можно увидеть в блоке Checkout. При выборе блока справа будет выводиться предупреждение о неподдерживаемых шлюзах. В нашем случае это шлюз «Example Gateway»:

Теперь рассмотрим как это исправить.

Backend

Сначала разберем интеграцию на стороне Backend’а. Для этого нужно создать класс, наследующийся от класса AbstractPaymentMethodType и зарегистрировать его во время события woocommerce_blocks_payment_method_type_registration:

Вот пример минимального класса

/**
 * Payment method integration
 */
final class ExampleGatewayBlocksIntegration extends AbstractPaymentMethodType {
	/**
	 * Payment method name/id/slug.
	 *
	 * @var string
	 */
	protected $name = 'example_gateway';

	/**
	 * Returns an array of script handles to enqueue for this payment method in
	 * the frontend context
	 *
	 * @return string[]
	 */
	public function get_payment_method_script_handles(): array {
		$handle = 'script-handle';

		wp_register_script(
			$handle,
			'path_to_js'
		);

		return array( $handle );
	}
	
	/**
	 * Initializes the payment method type.
	 */
	public function initialize() {
		$this->settings = get_option( "woocommerce_{$this->name}_settings", array() );
	}

	/**
	 * Returns if this payment method should be active. If false, the scripts will not be enqueued.
	 *
	 * @return boolean
	 */
	public function is_active(): bool {
		return filter_var( $this->get_setting( 'enabled', false ), FILTER_VALIDATE_BOOLEAN );
	}

	/**
	 * Returns an array of key=>value pairs of data made available to the payment methods script.
	 *
	 * @return array
	 */
	public function get_payment_method_data(): array {
		return array(
			'title'       => $this->get_setting( 'title' ),
			'description' => $this->get_setting( 'description' ),
			'supports'    => $this->get_supported_features(),
		);
	}
}

Здесь наиболее интересен метод get_payment_method_script_handles. Он должен зарегистрировать JS скрипт и вернуть его handle в виде массива. Раз возвращается массив, то можно зарегистрировать и больше одного скрипта, но нам это не понадобится. Остальные методы:

  • initialize — вызывается при инициализации WooCommerce интеграций. В случае со шлюзом он должен установить настройки шлюза
  • is_active — проверяет активирован или нет шлюз. В нашем случае работает на основе настроек шлюза полученных в методе initialize
  • get_payment_method_data — возвращает настройки для js скрипта. Нам потребуются title, description и supports.

Теперь осталось только зарегистрировать этот класс в WooCommerce интеграциях:

add_action( 'woocommerce_blocks_payment_method_type_registration', function ( PaymentMethodRegistry $payment_method_registry ) {
	$payment_method_registry->register( new ExampleGatewayBlocksIntegration() );
} );

На этом Backend настройку можно считать завершенной

Frontend

Теперь создадим JS скрипт для работы со шлюзом. Этот скрипт должен вызывать функцию registerPaymentMethod из пакета @woocommerce/blocks-registry, в которую нужно передать информацию о шлюзе и React компоненты для отображения шлюза в списке. Так как мы идем по быстрому и простому пути, то упростим пару моментов:

  • Без JSX —для использования библиотеки React воспользуемся ее методом createElement, вместо настройки JSX
  • Без установки библиотек — все требуемые инструменты для работы интеграции шлюза находятся в глобальной области видимости страницы оплаты, то есть мы можем их легко получить в JS скрипте

Здесь рассмотрим сначала составляющие скрипта, а потом соберем всё в единое целое. В первую очередь проверим, что все требуемые библиотеки существуют в глобальной области видимости:

if (window.wc && window.wc.wcBlocksRegistry && window.React) {...}

Если существует функция wc.wcBlocksRegistry и библиотека React, то и все остальное точно подключено.

Далее импортируем все нужные функции. В примере используется деструктуризация, но можно использовать и обычное присвоение переменным или вообще использовать функции по полному пути:

const {registerPaymentMethod} = wc.wcBlocksRegistry; // const registerPaymentMethod = wc.wcBlocksRegistry.registerPaymentMethod;
const {createElement} = React; // const createElement = React.createElement;
const {__} = wp.i18n; // const __ = wp.i18n.__;
const {getSetting} = wc.wcSettings; // const getSetting = wc.wcSettings.getSetting;
const {decodeEntities} = wp.htmlEntities; // const decodeEntities = wc.htmlEntities.decodeEntities;

Теперь получим нужные настройки:

const PAYMENT_METHOD_NAME = 'example_gateway' // Строка указанная в свойстве name класса интеграции
const settings = getSetting(PAYMENT_METHOD_NAME, {}); // Получение всех настроек
const label = decodeEntities(settings.title); // Получение названия шлюза для отображения
const description = decodeEntities(settings.description); // Получение описания шлюза для отображения

Перейдем к созданию React компонентов для отображения. Их понадобится два и создадим мы их с помощью функции createElement, импортированной ранее. Первый Content — контент отображаемый при выборе шлюза. В нашем случае будет отображаться описание шлюза:

const Content = createElement(() => {
    return description;
}, null);

Второй Label — элемент, который выводится в списке доступных методов оплате.

const Label = createElement(props => {
    const {PaymentMethodLabel} = props.components; //Стандартный элемент для отображения заголовка
    return createElement(PaymentMethodLabel, {text: label}) //Передача заголовка шлюза в элемент
}, null)

Осталось только вызвать функцию registerPaymentMethod, передав в нее объект с конфигурацией:

registerPaymentMethod({
    name: PAYMENT_METHOD_NAME, // id шлюза
    label: Label, // Ранее созданный компонент для заголовка шлюа
    content: Content, // Ранее созданный компонент для содержания шлюза
    edit: Content, // Используем его же во время редактирования страницы
    canMakePayment: () => true, // Заглушка для проверки доступности шлюза
    ariaLabel: label, // Значение aria-label атрибута
    supports: {
      features: settings?.supports ?? [], // Массив переданный с бекенда, по умолчанию пустой
    },
})

Теперь если все классы и функции правильно подключены и запущены, то предупреждение при редактировании Checkout block пропадет и шлюз появится в списке Methods:

Так же шлюз теперь доступен во frontend части сайта и с его помощью можно провести оплату:

Для шлюза, который работает на основе редиректа покупателя на внешнюю страницы оплаты, этого будет вполне достаточно. При нажатии на кнопку «Place Order» данные попадут в метод process_payment шлюза и далее могут обрабатываться как обычно.

Данной реализации поддержки Checkout block недостаточно для поддержки нескольких шлюзов в одном плагине и для более комплексной логики шлюза во frontend части. К сожалению WooCommerce не обладает полным набор требуемых функций для поддержки сложных шлюзов в блоке Checkout, но есть способы обойти эти недостатки.

В итоге в данной статье описан способ простой и быстрой интеграции шлюзов с Checkout block. С помощью описанного выше кода, можно добавить поддержку данного блока в любой WooCommerce шлюз. Также, несмотря на то, что блоки Gutenberg требуют написание кода на React, мы обошлись без подключения JSX. Во второй части статьи разберем примеры интеграций более сложных шлюзов.