Представьте: вы запустили интернет-магазин на WooCommerce, наполнили товарами, настроили дизайн, запустили рекламу. Клиент дошёл до корзины, нажал «Оплатить» — и ушёл. Потому что платёжный шлюз вернул ошибку, форма оплаты зависла на мобильном, а деньги просто не дошли до вашего счёта. Знакомая история? По данным исследований, около 70% брошенных корзин связаны именно с проблемами на этапе оплаты. И большинство этих проблем — следствие неграмотной или неполной интеграции WooCommerce с платёжным шлюзом.

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

Что такое платёжный шлюз и зачем он WooCommerce

Платёжный шлюз — это посредник между вашим магазином и банком-эквайером. Когда покупатель вводит данные карты на сайте, шлюз:

  • шифрует платёжные данные;
  • отправляет запрос на авторизацию в банк-эмитент;
  • получает ответ — одобрено или отклонено;
  • передаёт результат обратно в WooCommerce.

Без этого механизма вы просто не сможете принимать онлайн-оплату. WooCommerce «из коробки» поддерживает несколько способов оплаты (прямой банковский перевод, наложенный платёж), но для реального бизнеса нужен полноценный шлюз — с оплатой картой, возможностью принимать СБП, ЮMoney и другие популярные в России методы.

Почему нельзя просто «вставить код»

Казалось бы, платёжные системы дают API-документацию, примеры запросов — подключай и работай. Но на практике интеграция WooCommerce с платёжными шлюзами затрагивает сразу несколько слоёв:

  • логику оформления заказа (checkout flow);
  • обработку callback-уведомлений (webhooks);
  • синхронизацию статусов заказов;
  • безопасность передачи данных;
  • отображение форм оплаты в теме оформления.

Ошибка в любом из этих слоёв — и клиент получает «висящий» заказ, деньги списались, но магазин не знает об этом. Или наоборот: статус «Оплачено» появился, а деньги так и не зачислились.

Популярные платёжные шлюзы для WooCommerce в России

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

ЮKassa (Яндекс)

Одна из самых распространённых платёжных систем для российских магазинов. Официальный плагин для WooCommerce поддерживает оплату картами, СБП, ЮMoney, SberPay. Технически — один из самых стабильных модулей. API чётко документирован, webhook’и работают предсказуемо. Комиссия — от 2,8% в зависимости от типа бизнеса.

Tinkoff Pay (Т-Касса)

Интеграция с эквайрингом Тинькофф. Модуль для WooCommerce существует, но обновляется нерегулярно. При подключении обратите внимание на настройку статусов: по умолчанию модуль маппит статусы Tinkoff на внутренние статусы WooCommerce, и если вы используете нестандартные статусы заказов, потребуется доработка.

Robokassa

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

CloudPayments

Хороший вариант с понятной документацией и поддержкой рекуррентных платежей (подписок). Если вы планируете модель с подписками — это один из лучших выборов. Официальный плагин стабильный.

Stripe (для международных проектов)

Если магазин работает на международный рынок — Stripe остаётся эталоном. API разработан с любовью к деталям, WooCommerce-интеграция одна из самых зрелых. Но для российских юрлиц есть ограничения, связанные с географией работы сервиса.

Техническая реализация: как это работает изнутри

Разберём архитектуру на примере кастомной интеграции с платёжным шлюзом. Допустим, вы хотите подключить шлюз, для которого нет готового плагина, или стандартный модуль не покрывает ваши бизнес-требования.

Структура WooCommerce-плагина для оплаты

WooCommerce предоставляет класс WC_Payment_Gateway, от которого наследуется ваш модуль. Минимальная структура выглядит так:

<?php
/*
 * Plugin Name: Custom Payment Gateway
 * Description: Интеграция с платёжным шлюзом
 */

add_action('plugins_loaded', 'init_custom_gateway');

function init_custom_gateway() {
    if (!class_exists('WC_Payment_Gateway')) return;

    class WC_Gateway_Custom extends WC_Payment_Gateway {
        public function __construct() {
            $this->id = 'custom_gateway';
            $this->method_title = 'Custom Payment';
            $this->method_description = 'Приём оплаты через Custom Gateway';
            $this->has_fields = true;

            $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,
                array($this, 'process_admin_options')
            );

            add_action('woocommerce_receipt_' . $this->id, array($this, 'receipt_page'));
            add_action('woocommerce_api_wc_gateway_custom', array($this, 'webhook_handler'));
        }

        public function init_form_fields() {
            $this->form_fields = array(
                'enabled' => array(
                    'title' => 'Включить/Выключить',
                    'type' => 'checkbox',
                    'default' => 'yes'
                ),
                'api_key' => array(
                    'title' => 'API Key',
                    'type' => 'text'
                ),
                'secret_key' => array(
                    'title' => 'Secret Key',
                    'type' => 'password'
                )
            );
        }

        public function process_payment($order_id) {
            $order = wc_get_order($order_id);

            // Формируем запрос к API шлюза
            $response = wp_remote_post('https://api.gateway.com/v1/payments', array(
                'body' => array(
                    'amount' => $order->get_total(),
                    'currency' => $order->get_currency(),
                    'order_id' => $order_id,
                    'return_url' => $order->get_checkout_order_received_url(),
                    'callback_url' => home_url('/wc-api/wc_gateway_custom/'),
                ),
                'headers' => array(
                    'Authorization' => 'Bearer ' . $this->get_option('api_key'),
                ),
            ));

            $body = json_decode(wp_remote_retrieve_body($response), true);

            if (!empty($body['payment_url'])) {
                $order->update_status('pending', 'Ожидает оплаты');
                return array(
                    'result' => 'success',
                    'redirect' => $body['payment_url'],
                );
            }

            wc_add_notice('Ошибка при создании платежа.', 'error');
            return array('result' => 'failure');
        }

        public function webhook_handler() {
            $raw = file_get_contents('php://input');
            $data = json_decode($raw, true);

            // Верификация подписи
            $signature = hash_hmac('sha256', $raw, $this->get_option('secret_key'));
            if ($signature !== $_SERVER['HTTP_X_SIGNATURE']) {
                wp_die('Invalid signature', 'Signature mismatch', array('response' => 403));
            }

            $order = wc_get_order((int) $data['order_id']);
            if (!$order) {
                wp_die('Order not found', 'Not found', array('response' => 404));
            }

            switch ($data['status']) {
                case 'succeeded':
                    $order->payment_complete($data['transaction_id']);
                    break;
                case 'canceled':
                    $order->update_status('cancelled', 'Платёж отменён');
                    break;
                case 'refunded':
                    $order->update_status('refunded', 'Возврат средств');
                    break;
            }

            status_header(200);
            echo json_encode(array('received' => true));
            exit;
        }
    }

    add_filter('woocommerce_payment_gateways', function($methods) {
        $methods[] = 'WC_Gateway_Custom';
        return $methods;
    });
}

Это упрощённый каркас, но он показывает ключевые точки интеграции. В реальном проекте здесь будут retry-логика, логирование, обработка частичных возвратов и ещё дюжина нюансов.

Обработка callback (webhook)

Это самая критичная часть. Когда шлюз подтверждает оплату, он отправляет запрос на ваш callback URL. Вот что часто идёт не так:

  • Сервер блокирует входящие запросы — файрвол или плагин безопасности фильтрует неизвестные IP. Нужно добавить IP-адреса шлюза в белый список.
  • Повторная обработка — шлюзы часто отправляют один и тот же webhook несколько раз. Без проверки уникальности транзакции вы рискуете отправить товар дважды.
  • Таймаут ответа — если ваш сервер не ответил за 5-10 секунд, шлюз сочтёт webhook не доставленным и попробует ещё раз.
  • Неверный маппинг статусов — у каждого шлюза свои значения статусов. «captured», «succeeded», «completed» — это может быть одно и то же, а может и нет.

Редирект vs. встраиваемая форма

Есть два подхода к отображению формы оплаты:

Редирект на сторону шлюза — клиент уходит на страницу платёжной системы, вводит данные карты там и возвращается обратно. Проще в реализации, PCI DSS-compliant по умолчанию, но хуже конверсия: клиент покидает ваш сайт.

Встраиваемая форма (iframe / widget) — платёжная форма отображается прямо на странице вашего магазина. Конверсия выше, реализация сложнее. Нужно корректно обрабатывать postMessage из iframe, следить за адаптивностью на мобильных устройствах.

В большинстве случаев мы рекомендуем второй вариант. Разница в конверсии может составлять 10-15% — это ощутимо для любого бизнеса.

Типичные ошибки при интеграции

За годы работы мы собрали целую коллекцию граблей, на которые наступают при подключении оплаты. Вот самые болезненные.

Отсутствие идемпотентности

Если посетитель дважды нажал кнопку «Оплатить» (а на мобильных это происходит регулярно), вы получите два запроса к API шлюза. Без защиты от дублирования — два платежа на одну сумму. Решение: генерировать уникальный идентификатор транзации на стороне WooCommerce и передавать его в шлюз, проверяя на дубликат перед созданием нового платежа.

Некорректная обработка ошибок

API шлюза может вернуть ошибку: недостаточно средств, карта заблокирована, превышен лимит. Если ваш модуль просто выводит «Произошла ошибка» — клиент не понимает, что делать, и уходит. Нужно маппить коды ошибок шлюза на понятные сообщения.

Игнорирование тестового режима

Запуск интеграции сразу в продакшене — классика. Все платёжные шлюзы предоставляют sandbox-окружение для тестирования. Используйте его. Прогоните все сценарии: успешную оплату, отказ, возврат, таймаут, webhook при выключенном сайте.

Плохая работа с мобильными

Формы оплаты должны быть адаптивными. Особенно если вы используете встраиваемые виджеты: iframe может обрезаться на маленьких экранах, кнопки могут быть неудобны для нажатия пальцем. Тестируйте на реальных устройствах, а не только в DevTools.

Безопасность: что нельзя игнорировать

Обработка платёжных данных — зона повышенной ответственности. Вот минимальный набор требований:

  • HTTPS обязателен — без SSL-сертификата платёжные шлюзы просто не подключатся.
  • Не храните данные карт — если вы не PCI DSS-сертифицированы (а малый бизнес обычно не сертифицирован), хранить номера карт, CVV и сроки действия на своих серверах нельзя. Всегда используйте токенизацию.
  • Верифицируйте подписи webhook — каждый уважающий себя шлюз подписывает свои callback’и. Проверяйте подпись. Без этого злоумышленник может под

Добавить комментарий

Разработка сайтов на Wordpress