Способ оплаты
Способ оплаты в WooCommerce называют платёжным шлюзом. Это класс, наследующий WC_Payment_Gateway: он описывает свои настройки и один главный метод process_payment(), который решает, что делать с заказом после нажатия «Подтвердить заказ».
Простой шлюз
Начнём с оплаты по счёту — без обращения к банку. Создайте файл includes/class-acme-gateway.php:
// wp-content/plugins/acme-shop/includes/class-acme-gateway.php
class Acme_Gateway extends WC_Payment_Gateway
{
public function __construct()
{
$this->id = 'acme_invoice';
$this->method_title = 'Оплата по счёту';
$this->method_description = 'Покупатель получает счёт и платит переводом.';
$this->has_fields = false;
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option('title');
$this->description = $this->get_option('description');
add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']);
}
public function init_form_fields(): void
{
$this->form_fields = [
'enabled' => ['title' => 'Включить', 'type' => 'checkbox', 'default' => 'no'],
'title' => ['title' => 'Название', 'type' => 'text', 'default' => 'Оплата по счёту'],
'description' => ['title' => 'Описание', 'type' => 'textarea', 'default' => 'Счёт придёт на почту.'],
];
}
public function process_payment($orderId): array
{
$order = wc_get_order($orderId);
$order->update_status('on-hold', 'Ожидается оплата по счёту.');
WC()->cart->empty_cart();
return [
'result' => 'success',
'redirect' => $this->get_return_url($order),
];
}
}Конструктор задаёт идентификатор и читает настройки. Массив form_fields превращается в страницу настроек шлюза, а строка с add_action сохраняет её. process_payment() ставит заказ на удержание, очищает корзину и возвращает адрес страницы «Заказ принят».
Регистрация
// wp-content/plugins/acme-shop/acme-shop.php
add_filter('woocommerce_payment_gateways', function (array $gateways): array {
require_once __DIR__ . '/includes/class-acme-gateway.php';
$gateways[] = Acme_Gateway::class;
return $gateways;
});Откройте «WooCommerce → Настройки → Платежи»: в списке появилась «Оплата по счёту». Включите её — способ станет доступен на странице классического оформления заказа.
Что возвращает process_payment
| Ключ | Значение |
|---|---|
result | success или failure |
redirect | Куда отправить покупателя при успехе |
При неудаче вызывают wc_add_notice('Текст ошибки', 'error') и возвращают ['result' => 'failure'] — покупатель остаётся на странице оформления.
Оплата через банк
Настоящий шлюз создаёт платёж у платёжного сервиса и отправляет покупателя на его страницу. Добавьте в form_fields поле 'api_key' => ['title' => 'Ключ API', 'type' => 'password'] и замените метод:
// wp-content/plugins/acme-shop/includes/class-acme-gateway.php
public function process_payment($orderId): array
{
$order = wc_get_order($orderId);
$response = wp_remote_post('https://pay.example.com/v1/payments', [
'headers' => ['Authorization' => 'Bearer ' . $this->get_option('api_key')],
'body' => [
'amount' => $order->get_total(),
'currency' => $order->get_currency(),
'order' => $order->get_id(),
'return_url' => $this->get_return_url($order),
],
]);
if (is_wp_error($response)) {
wc_add_notice('Не удалось связаться с банком. Попробуйте ещё раз.', 'error');
return ['result' => 'failure'];
}
$payment = json_decode(wp_remote_retrieve_body($response), true);
$order->update_meta_data('_acme_payment_id', $payment['id']);
$order->save();
return ['result' => 'success', 'redirect' => $payment['checkout_url']];
}Заказ остаётся в состоянии «В ожидании оплаты», в нём сохраняется идентификатор платежа, а покупатель уходит на страницу банка. Адрес и поля запроса у каждого сервиса свои — здесь они условные.
Уведомление банка
Об оплате банк сообщает отдельным запросом к сайту. Для него у WooCommerce есть готовый адрес /wc-api/acme_invoice/:
// wp-content/plugins/acme-shop/includes/orders.php
add_action('woocommerce_api_acme_invoice', function () {
$payload = json_decode(file_get_contents('php://input'), true);
$orders = wc_get_orders([
'limit' => 1,
'meta_query' => [['key' => '_acme_payment_id', 'value' => $payload['id'] ?? '']],
]);
if ($orders && ($payload['status'] ?? '') === 'paid') {
$orders[0]->payment_complete($payload['id']);
}
status_header(200);
exit;
});Метод payment_complete() переводит заказ в «Обработку», уменьшает остатки и запускает письма. Перед этим обязательно проверяют подпись запроса способом, описанным в документации банка, — иначе заказ «оплатит» любой, кто знает адрес.
Блочное оформление
В блочном оформлении заказа шлюз не появится сам: его дополнительно регистрируют на стороне JavaScript. Пока этого нет, используйте классическую страницу с шорткодом [woocommerce_checkout].
Частая ошибка
Заказ отмечают оплаченным в process_payment() или на странице возврата — до уведомления банка. Покупатель закрыл вкладку или подделал адрес возврата, и товар уходит без денег. Оплаченным заказ делает только проверенное уведомление. А данные карты не должны проходить через ваш сервер: их вводят на странице платёжного сервиса.