События при оплате заказа в Bitrix Sale D7: заказ и отдельный платёж
Два уровня — два события
В модуле sale полная оплата заказа может пройти одним платежом или несколькими. Сохранение заказа после смены поля PAYED даёт событие OnSaleOrderSaved: удобно, когда нужно среагировать именно на деньги по заказу в целом. Сохранение сущности платежа порождает OnSalePaymentEntitySaved: его выбирают, если важно не допустить дубля на частичных оплатах или связать действие с конкретной записью в коллекции оплат.
Заказ: OnSaleOrderSaved
Обработчик получает \Bitrix\Main\Event. Полезные параметры: ENTITY — экземпляр \Bitrix\Sale\Order, VALUES — предыдущие значения полей. Реагировать имеет смысл только на переход флага «оплачен» из отрицательного состояния в Y; без сравнения с VALUES обработчик сработал бы на каждом сохранении уже оплаченного заказа.
Платёж: OnSalePaymentEntitySaved
Здесь ENTITY — \Bitrix\Sale\Payment. Служебные «внутренние» операции обычно отсекают по \Bitrix\Sale\PaySystem\Manager::getInnerPaySystemId(). Для срабатывания на момент оплаты у конкретного платежа проверяют пару VALUES['PAID'] и текущее поле PAID, затем при необходимости поднимают $payment->getOrder() и getPaymentCollection().
Ниже — один класс с регистрацией обоих хендлеров; вызовите register() из init.php или загрузчика своего модуля.
namespace Acme\SaleHooks;
use Bitrix\Main\Event;
use Bitrix\Main\EventManager;
use Bitrix\Main\Loader;
use Bitrix\Sale\PaySystem\Manager;
final class LedgerPayPulse
{
public static function register(): void
{
$hub = EventManager::getInstance();
$hub->addEventHandler('sale', 'OnSaleOrderSaved', [self::class, 'onOrderSaved']);
$hub->addEventHandler('sale', 'OnSalePaymentEntitySaved', [self::class, 'onPaymentSaved']);
}
public static function onOrderSaved(Event $signal): void
{
if (!Loader::includeModule('sale')) {
return;
}
$purchase = $signal->getParameter('ENTITY');
$priorFlags = $signal->getParameter('VALUES') ?? [];
if (!$purchase instanceof \Bitrix\Sale\Order) {
return;
}
if ($purchase->getField('PAYED') !== 'Y') {
return;
}
if (($priorFlags['PAYED'] ?? '') !== 'N') {
return;
}
/* PAYED: N → Y на уровне заказа */
}
public static function onPaymentSaved(Event $signal): void
{
if (!Loader::includeModule('sale')) {
return;
}
$installment = $signal->getParameter('ENTITY');
$priorFlags = $signal->getParameter('VALUES') ?? [];
if (!$installment instanceof \Bitrix\Sale\Payment) {
return;
}
if ((int) $installment->getField('PAY_SYSTEM_ID') === (int) Manager::getInnerPaySystemId()) {
return;
}
if (($priorFlags['PAID'] ?? '') !== 'N' || $installment->getField('PAID') !== 'Y') {
return;
}
$basketOrder = $installment->getOrder();
/* PAID: N → Y у конкретного платежа; при необходимости — $basketOrder->getPaymentCollection() */
/* далее своя логика */
}
}
/* LedgerPayPulse::register(); */Практические замечания
- Оба события приходят после записи сущности; отмена оплаты — отдельная ветка полей и сценариев.
- Тяжёлые интеграции лучше выносить в очередь или агента, чтобы не растягивать HTTP‑хит оплаты.
- Соседние темы: события при создании и сохранении заказа и работа с платёжными системами — смотрите по ссылкам в материалах раздела магазина.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии