Проблема: некорректное обновление статусов заказов после callback платёжных систем
При интеграции WooCommerce с платёжными шлюзами часто возникает задача автоматически менять статус заказа после получения callback (уведомления) от платёжной системы. Типичные проблемы:
- Заказы не обновляются из-за ошибок авторизации API.
- Обновление происходит некорректно, например, статус не соответствует действительности.
- Отсутствует обработка ошибок, что приводит к потере данных.
- Повторные вызовы callback вызывают дублирование или конфликт статусов.
Диагностика проблемы
Для начала проверьте логи сервера и WooCommerce. Включите отладку WooCommerce REST API, добавив в wp-config.php:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);После этого найдите файл wp-content/debug.log и ищите ошибки, связанные с REST API и callback.
Проверьте, что ключи REST API имеют права на изменение заказов. В WooCommerce откройте WooCommerce > Settings > Advanced > REST API, проверьте права «Read/Write».
Также проверьте, что callback от платёжной системы корректно отправляет данные и заголовки (например, Content-Type: application/json) и что URL для callback совпадает с ожидаемым.
Пошаговое решение
1. Создаём кастомный endpoint для приёма callback
Добавьте в functions.php вашей темы или в отдельный плагин следующий код для регистрации endpoint:
add_action('rest_api_init', function () {
register_rest_route('custom/v1', '/payment-callback', array(
'methods' => 'POST',
'callback' => 'handle_payment_callback',
'permission_callback' => '__return_true', // Для безопасности лучше реализовать проверку
));
});2. Обработка callback и обновление статуса заказа
Основная функция для обработки данных и обновления заказа:
function handle_payment_callback(WP_REST_Request $request) {
$params = $request->get_json_params();
if (empty($params['order_id']) || empty($params['payment_status'])) {
return new WP_REST_Response(['error' => 'Missing order_id or payment_status'], 400);
}
$order_id = intval($params['order_id']);
$payment_status = sanitize_text_field($params['payment_status']);
$order = wc_get_order($order_id);
if (!$order) {
return new WP_REST_Response(['error' => 'Order not found'], 404);
}
// Проверяем текущий статус, чтобы избежать некорректных переходов
$current_status = $order->get_status();
// Пример логики статусов
if ($payment_status === 'paid' && $current_status !== 'completed') {
$order->update_status('completed', 'Оплата подтверждена через callback.');
} elseif ($payment_status === 'failed' && $current_status !== 'cancelled') {
$order->update_status('cancelled', 'Оплата не прошла.');
} else {
// Другие статусы или повторный callback
return new WP_REST_Response(['message' => 'No status change needed'], 200);
}
return new WP_REST_Response(['message' => 'Order status updated'], 200);
}3. Обработка ошибок и логирование
Для надёжности добавьте логирование всех запросов и ошибок в файл, чтобы можно было отследить инциденты:
function log_payment_callback($data) {
$log_file = WP_CONTENT_DIR . '/payment-callback.log';
$entry = date('Y-m-d H:i:s') . ' - ' . json_encode($data) . "\n";
file_put_contents($log_file, $entry, FILE_APPEND);
}
// Вызов логирования внутри handle_payment_callback
log_payment_callback($params);4. Защита endpoint
Для безопасности добавьте проверку секретного ключа или подписи от платёжной системы, например, в permission_callback или внутри обработчика:
function check_callback_auth($request) {
$headers = $request->get_headers();
if (empty($headers['x-custom-signature'])) {
return false;
}
$signature = $headers['x-custom-signature'][0];
$secret = 'ваш_секретный_ключ';
$body = $request->get_body();
$calculated = hash_hmac('sha256', $body, $secret);
return hash_equals($calculated, $signature);
}
// В регистрации маршрута
register_rest_route('custom/v1', '/payment-callback', array(
'methods' => 'POST',
'callback' => 'handle_payment_callback',
'permission_callback' => 'check_callback_auth',
));Проверка результата после внедрения
1. Отправьте тестовый POST-запрос на /wp-json/custom/v1/payment-callback с корректным JSON, например:
{
"order_id": 123,
"payment_status": "paid"
}2. Проверьте, что статус заказа действительно изменился в админке WooCommerce.
3. Проверьте лог-файл wp-content/payment-callback.log на предмет записей.
4. Попробуйте отправить запрос с неверным секретом и убедитесь, что доступ закрыт.
Частые ошибки и как исправить
- 401 Unauthorized — проверьте корректность и права REST API ключей или реализуйте корректную проверку подписи.
- 400 Bad Request — убедитесь, что JSON корректный и содержит все необходимые поля.
- Статус не меняется — проверьте логику в коде, не вызывается ли update_status с текущим статусом, или не блокирует ли фильтр смену статуса.
- Дублирование действий при повторных callback — добавьте проверку текущего статуса и игнорируйте повторные одинаковые статусы.
- Отсутствие логов — проверьте права на запись в папку
wp-contentи включен ли режим отладки.
Практические советы по безопасности и производительности
- Всегда проверяйте подпись или секретный ключ в callback для предотвращения подделки запросов.
- Используйте nonce или OAuth, если платёжная система поддерживает, для дополнительной защиты.
- Логируйте только нужные данные, избегайте записи персональных данных клиентов.
- Используйте транзакции или блокировки, если ваш магазин принимает много одновременных callback – чтобы избежать гонок статусов.
- Кэширование не применяйте для callback endpoint – он должен обрабатывать каждый запрос в реальном времени.
Сравнение вариантов реализации автоматизации обновления заказов
| Метод | Плюсы | Минусы | Пример |
|---|---|---|---|
| Использование кастомного REST API endpoint | Гибкость, полное управление логикой; легко интегрируется с любыми платёжными системами | Требует разработки и поддержки; необходимость защиты endpoint | Код из статьи выше |
| Использование готовых плагинов (например, WooCommerce Payment Gateway) | Быстрая настройка; поддержка популярных шлюзов | Ограниченная кастомизация, возможны конфликты; не всегда есть нужная логика | WooCommerce официальные плагины |
| Обработка callback через action hooks WooCommerce | Интеграция с внутренними событиями WooCommerce | Сложнее для кастомных платёжных систем; callback нужно правильно маршрутизировать | add_action('woocommerce_payment_complete', 'my_callback_func'); |