Как мы интегрировали наш модуль в wordpress + woocommerce + интернет-эквайринг от Тинькофф Банк

2020.10.31
За 2 дня выкатили более 15 патчей и поправили плагин интернет эквайринга от Тинькофф Банк, и все это для wordpress + woocommerce

У клиента сайт на wordpress + woocommerce, внутри множество модулей (которые в админке выдают уведомления разного рода notice - error, просто трэш) среди которых модуль интернет-эквайринга от Тинькофф Банк.

Наш модуль реагирует на хуки woocommerce и осуществляет на основании этого свою работу. Однако, логика модуля зависит от того, как отработает модуль эквайринга ... и как следствие все работало не так как надо.

Доступа к ЛК клиента экваринга банка нет, так же нет доступа по ftp и к базе данных сайта клиента, однако, есть наш модуль на сайте клиента, который может обновляться, и есть доступ к админке.

На протяжении всего процесса решения проблем включили не раз проверенный метод отладки print_r/file-put-contents, из-за чего выкатили 15 патчей за 2 дня (на самом деле их было больше, потому что иногда забывали обновлять патч-версию, в следствии чего приходилось переустанвливать модуль).

Проблемы конечно решены, клиент доволен, но само решение на скорую руку, которое естественно будет переделываться.

Адаптация модуля под текущий вариант эквайринга

Первым делом выяснилось что экваринг в ЛК банка был не полностью настроен (юзеры такие юзеры), из-за чего не приходили http уведомления о приеме оплаты. После включения этой функции модуль все-равно не хотел работать так как ожидалось - пришлось лезть в код модуля эквайринга и смотреть (исходники можно взять здесь).

В модуле интернет-эквайринга при http нотификации (на файл success.php) в случае успешно проведенной оплаты, нет вызова хука woocommerce_payment_complete, который вызывается payment_complete методом объекта WC_Order, что не давало нашему модулю фискализации обработать совершение платежа в интернет-магазине клиента.

Также нет информации об идентификаторах транзакций (внутри банка) совершенных клиентом, что делает почти невозможным отслеживание оплат для других модулей (все-таки есть).

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

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

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

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

Реализация этого костыля так же была не без прикола. Оказалось что при http оповещении сайта со стороны банка, скрипт модуля эквайринга на сайте ставит свой обработчик ошибок:

set_error_handler('exceptions_error_handler', E_ALL);
function exceptions_error_handler($severity)
{
  if (error_reporting() == 0) {
    return;
  }
  if (error_reporting() & $severity) {
    die('NOTOK1');
  }
}

И так как это wordpress с кучей плагинов (естественно возникают ошибки уровня ниже критичности E_ERROR), очень часто обработчик заходит во вторую ветку условия и не дает отработать другим модулям, в том числе нашему.

На помощь пришел другой костыль error_reporting(0) прямо в нашем модуле, чтобы заткнуть обработку ошибок и не давать скрипту возможности упасть с ошибкой.

Эта идея плоха сама по себе, но в такой среде видимо это единственный вариант.

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

Решение проблем модуля эквайринга

В первую очередь нужно решить обработку ошибок, но так как с wordpress и woocommerce на таком глубоком уровне я работал впервые, то затык в виде error_reporting(0) временно закрыл эту проблему, и начались выяснения как правильно фикировать оплату и сохранять статусы.

Обработка этапов оплаты

Модуль эквайринга осуществляет обработку статуса заказа так:

if ($request['Status'] === 'CONFIRMED' && $settings['auto_complete'] === 'yes') {
  $order_status = 'wc-completed';
}

$order->update_status($order_status);
do_action('woocommerce_order_edit_status', (int)$request['OrderId'], $order_status);

Здесь видно что модуль использует вызов хука woocommerce_order_edit_status, который не вызывается из set_status, который должен вызваться из update_status потому что модуль не сообщает что это manual/ручное изменение статуса.

Подписавшись на хук woocommerce_order_edit_status можно узнать статус заказа, но нельзя узнать о статусе оплаты (потому что нет фиксации посредством payment_complete). Если копнуть глубже, то можно узнать о способе оплаты (есть инфа о том, что оплата прошла через банк), но статуса все-равно не найти.

Так как банк присылает http уведомления не только об успешной оплате, но и промежуточные этапы ее осуществления, то надо оставить возможность смены статуса заказа и отдельно смену статуса оплаты:

if ($request['Status'] === 'CONFIRMED') {
	if($settings['auto_complete'] === 'yes') {
		$order_status = 'wc-completed';
		$order->set_status($order_status);
	}

	$order->payment_complete($request['PaymentId']);
}
else {
	$order->set_status($order_status);
	$order->save();
}

Обработка ошибок

Теперь настал момент решения следующей проблемы: демократизировать обработчик ошибок, сделать его более лояльным к wordpress и плагинам.

Ранее мы уже выяснили что в продакшене интернет-магазин может вполне работать на этом движке, даже если внутри есть ошибки ниже уровня критичности E_ERROR, поэтому немного изменил обработчик:

set_error_handler('exceptions_error_handler', E_ALL);
function exceptions_error_handler($errno, $errstr, $errfile, $errline)
{
	if (error_reporting() == 0 || $errno != E_ERROR) {
		return;
	}

	die('NOTOK1');
}

Теперь если вообще отключен вывод ошибок или ошибки уровня не E_ERROR, то обработчик будет их пропускать, а на E_ERROR будет реагировать как и раньше.

Протестировав множество раз, правленный модуль подружился с wordpress и другими модулями, в том числе и с нашим :)

Итог

Проблема клиента при использовании нашего модуля фискализации продаж с интернет экрвайрингом решена. Письмо банку сформированно, предлагаемые правки модуля эквайринга написаны. Осталось передать банку данные и ожидать реакции :)

UPDATE 25.07.2021

С момента написания статья вышло несколько обновлений модуля, обработка ошибок исправлена, однако сохранение статуса оплаты так и не появилось.

В телеграм канале DevOps от первого лица можно оставить комментарий или почитать интересные истории из практики DevOps