LiqPay подтвердил оплату, а заказ WooCommerce остался неоплаченным: как мы восстановили обработку платежей

LiqPay · WooCommerce · кейс
Оплата прошла. Магазин продолжал ждать деньги

LiqPay подтверждал успешную оплату, а заказы оставались неоплаченными. Мы нашли блокировку серверных уведомлений, точечно исправили защиту и сверили покупки, которые уже зависли в ожидании.

Расхождение
Причина
Проверка оплаты



Оплата и статус заказа


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

Коротко о кейсе
С чем обратилисьПлатежи проходили через LiqPay, а WooCommerce оставлял заказы в ожидании оплаты.
Что нашлиModSecurity блокировал серверные уведомления до запуска WordPress.
Что восстановилиОбработку новых подтверждений; прежние расхождения разобрали отдельно.
01 / Заявка клиента

К нам поступила заявка: покупатели оплатили товары, но магазин продолжал ждать деньги

К нам обратился владелец интернет-магазина на WordPress и WooCommerce. Покупатели сообщали об успешной оплате через LiqPay, а соответствующие заказы оставались в статусе «Ожидает оплаты». Сотрудникам приходилось проверять покупки вручную, прежде чем передавать их в работу. Некоторые покупатели, не увидев подтверждения в магазине, пытались заплатить ещё раз.

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

Незадолго до первых обращений на сервере меняли настройки защиты входящих запросов. Мы зафиксировали это обстоятельство, но сначала проверили сами платежи. Совпадение по времени ещё не объясняло, почему WooCommerce не подтверждал оплату.

02 / Сверка платежей

Сначала установили, какие покупки действительно были оплачены

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

Затем сопоставили заказы с операциями LiqPay. Связь проверяли по данным установленного платёжного модуля: идентификатору магазина, переданному в LiqPay значению order_id и идентификатору платёжной операции. Номер заказа, показанный в административной панели WooCommerce, не использовали как единственный ориентир — формат платёжного order_id зависел от интеграции.

Для каждой найденной операции сверили сумму и валюту исходного платежа. Сумму заказа сравнивали с соответствующими полями операции, а не с зачислением за вычетом комиссии или суммой в другой валюте. Актуальное состояние дополнительно запросили через API LiqPay.

У выбранных проблемных операций был статус success. Платёжные данные соответствовали заказам, а повторных успешных списаний в проверенных попытках мы не обнаружили. Так подтвердилось расхождение: LiqPay считал оплату успешной, WooCommerce продолжал её ожидать.

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

03 / Маршрут подтверждения

Проверили, как магазин получает результат оплаты

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

Адрес result_url использовался для возврата браузера. На server_url LiqPay отправлял серверное уведомление с параметрами data и signature. Первый содержал сведения о платеже, второй использовался для проверки подлинности сообщения. Именно серверный обработчик модуля подтверждал оплату соответствующего заказа.

Мы проверили фактический server_url, переданный при создании проблемных платежей. Он указывал на действующий HTTPS-адрес магазина и зарегистрированный обработчик установленного расширения. Путь относился к маршрутам WooCommerce wc-api. Ошибки домена или адреса обработчика не нашли.

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

04 / Причина отказа

В журналах нашли блокировку перед запуском WordPress

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

Мы проверили настройки журналирования и включили необходимую диагностику. Закрытые ключи, платёжные токены и лишние персональные данные в новые диагностические записи не включали. Одновременно просмотрели ошибки PHP за соответствующие интервалы. Аварийного завершения обработчика LiqPay, которое объясняло бы эти случаи, не нашли.

После этого перешли к журналам веб-сервера. На адрес уведомлений поступали POST-запросы, часть которых завершалась ответом 403 Forbidden. Сам по себе этот код не показывал, какой компонент отказал в доступе, поэтому мы сопоставили записи веб-сервера с журналом ModSecurity.

У соответствующих запросов обнаружились события блокировки. По идентификаторам запросов связали отказ веб-сервера с конкретным срабатыванием защиты. В записи ModSecurity были указаны правило и проверяемый параметр data. В данном случае блокировка происходила на входе, до выполнения обработчика WordPress.


ModSecurity блокирует серверное уведомление LiqPay ответом HTTP 403 до обработки магазином
По журналам мы связали ответ HTTP 403 со срабатыванием ModSecurity: запрос блокировался до запуска платёжного обработчика WordPress.

Мы выяснили, какая проверка содержимого вызвала срабатывание, а не ограничились записью об итоговом запрете. Поле data было штатной частью уведомления LiqPay; отдельные значения в нём попадали под установленное защитное правило.

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

05 / Подлинность уведомления

Убедились, что доступный обработчик проверяет подлинность платежа

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

В установленном обработчике проверялась подпись с использованием закрытого ключа магазина. Мы сверили реализацию с используемой версией интеграции. Для проверки использовалось полученное значение data, а не заново собранный после декодирования JSON: изменение представления сообщения могло нарушить проверку подписи.

В исследованных случаях речь шла об обычной разовой оплате со статусом success. Обработку ещё выполняющейся операции и блокировки средств проверили отдельно. Статусы processing и hold_wait не использовались как подтверждение завершённого платежа по этим заказам. Также посмотрели обработку неуспешных операций и возвратов.

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

06 / Исправление защиты

Подготовили ограниченное исключение и сначала проверили его на копии

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

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


Точечное исключение для параметра data при сохранении проверки подписи платёжного уведомления
Исключение ограничили конкретным правилом, параметром data и маршрутом уведомлений. Проверка подписи в модуле осталась обязательной.

После проверки синтаксиса применили конфигурацию в тестовом окружении. На этом этапе настройки рабочего магазина ещё не меняли. Следующей задачей было проверить всю обработку уведомления, а не только исчезновение ответа 403.

07 / Проверка на копии

Проверили уведомления и повторную обработку в тестовом режиме

На копии создали отдельный заказ и выполнили оплату с тестовыми настройками LiqPay. Проверили, что используются тестовые учётные данные и операция не связана с реальным списанием. Вывод о режиме делали по настройкам интеграции и платёжным данным, а не только по тексту статуса в ответе.

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

Мы сверили ответ сервера, запись модуля и состояние заказа. Одного успешного HTTP-ответа было бы недостаточно: его мог вернуть промежуточный обработчик, не подтвердив оплату в WooCommerce.

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

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

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

08 / Рабочий магазин

Внедрили исправление и проверили реальную оплату без возврата покупателя

После испытаний сохранили действующую конфигурацию рабочего сервера и внесли в неё согласованное исключение. Проверили настройки перед применением. Изменять файлы WooCommerce или переустанавливать платёжный модуль не потребовалось. Базу с тестовой копии в рабочий магазин не переносили.

С владельцем согласовали контрольную покупку с реальной оплатой. Для неё использовали отдельный заказ, исключённый из обычной отправки товара. После проведения операции проверили её состояние в LiqPay: провайдер подтвердил success.

В этой проверке исключили загрузку страницы возврата покупателя. Результат смотрели из административного сеанса и сопоставляли с серверными записями. Это позволяло проверить именно уведомление LiqPay, не смешивая его работу с возможными действиями страницы result_url.

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

Для этого заказа с физическим товаром такой статус был ожидаемым: оплату подтвердили, отправка ещё предстояла. Мы сверили идентификатор транзакции, дату оплаты, историю заказа и учёт товаров. В проверенном платеже прежняя блокировка не повторилась.


Серверное подтверждение оплаты получено, заказ WooCommerce переведён в обработку
Контрольная реальная оплата подтвердилась через серверное уведомление. Страница возврата покупателя в этой проверке не участвовала.

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

09 / Ранее оплаченные заказы

Восстановили подтверждение ранее оплаченных заказов

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

Для каждой покупки заново запросили актуальное состояние операции LiqPay. Повторно сверили магазин, платёжный order_id, идентификатор операции, сумму и валюту. Учли возвраты и изменения, которые могли произойти после первого успешного платежа. Исторического сообщения success для такого восстановления было недостаточно.

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

Для однозначно сопоставленных, ещё не обработанных оплат восстановили подтверждение через штатный механизм WooCommerce. Использовали объект заказа и его обработку завершённого платежа с учётом данных установленного модуля LiqPay. Прямую замену статуса в таблице базы не применяли: она могла оставить несогласованными платёжные сведения и связанные действия.

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

10 / Ручные изменения и отмены

Отдельно разобрали ручные изменения, отмены и возвраты

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

Сопоставили текущий статус, идентификатор транзакции, сведения о платеже, изменения остатков и историю действий сотрудников. Где требовалось, уточнили платёжные данные и добавили служебные записи. Уже выполненные складские операции и отправки повторно не запускали.

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

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

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


Сверка платежей LiqPay с ранее созданными заказами WooCommerce после исправления
Ранее созданные заказы сверили с актуальным состоянием операций LiqPay. Ручные изменения, отмены и возвраты разобрали отдельно.
11 / Контроль после исправления

Добавили контроль расхождений между платежами и заказами

До инцидента мониторинг в основном проверял доступность страниц. Эта проверка оставалась полезной, но не показывала, получил ли магазин подтверждение конкретной оплаты.

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

Отдельно организовали наблюдение за ошибками маршрута уведомлений и срабатываниями ModSecurity. Ответ 403 рассматривали вместе с данными операции и результатом обработки в модуле. Посторонний отклонённый запрос на тот же адрес не считали потерянным платежом.

Локальное исключение включили в сопровождение конфигурации сервера. После изменений защиты предусмотрели проверку его действия и прохождения платёжного уведомления. Для интеграции сохранили контроль полного сценария: операция у провайдера, серверное сообщение, подтверждение заказа и связанные действия магазина.

12 / Результат для магазина

Что изменилось после нашей работы

Причиной расхождения оказалось правило ModSecurity, которое блокировало штатные серверные уведомления LiqPay до запуска WordPress. Покупатель завершал оплату у провайдера, но платёжный модуль магазина не получал сообщение, необходимое для подтверждения заказа.

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

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

Техническая помощь · VozniNet

Покупатель оплатил, а магазин этого не видит?

Разберём расхождение между платёжной системой и заказом, найдём место отказа и проверим исправление. Поможем восстановить обработку новых оплат и сверить уже зависшие покупки.