Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Способ оплаты

Способ оплаты в 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

КлючЗначение
resultsuccess или 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() или на странице возврата — до уведомления банка. Покупатель закрыл вкладку или подделал адрес возврата, и товар уходит без денег. Оплаченным заказ делает только проверенное уведомление. А данные карты не должны проходить через ваш сервер: их вводят на странице платёжного сервиса.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.