Настройка холдирования средств на 1С-Битрикс
Покупатель оплатил заказ — деньги списаны, но заказ ещё не подтверждён. Через два дня выясняется, что товара нет на складе. Возврат — это ещё несколько дней. Холдирование (двухстадийная оплата) решает эту проблему: банк резервирует сумму на карте покупателя, а фактическое списание происходит только после подтверждения наличия товара.
Двухстадийная оплата: схема работы
Стадия 1 — Холд (авторизация): магазин отправляет запрос в платёжную систему на резервирование суммы. Деньги не списаны, но заблокированы на карте. Банк-эквайер выдаёт pre_auth_code или аналогичный идентификатор авторизации.
Стадия 2 — Подтверждение (capture): магазин подтверждает списание, когда товар готов к отгрузке. Деньги переходят на счёт магазина. Либо — отмена (void/cancel): если товара нет, резерв снимается без комиссий.
Битрикс реализует это через модуль sale, класс \Bitrix\Sale\PaySystem\Manager и конкретные обработчики платёжных систем.
Поддержка двухстадийной оплаты в модуле sale
Не все платёжные системы в Битрикс поддерживают двухстадийную оплату. Встроенная поддержка есть у:
- Сбербанк (через
SberPaymenthandler) - Тинькофф (
TinkoffPayment) - ЮКасса — YooKassa (
YandexMoney) - Альфа-Банк (
AlfaPayment)
Конфигурация платёжной системы в административной панели: «Магазин» → «Оплата» → выбрать платёжную систему → вкладка «Настройки» → параметр «Двухстадийная оплата» или «Режим холдирования».
Конфигурация холдирования в таблице базы данных
Параметры платёжной системы хранятся в таблице b_sale_pay_system и b_sale_pay_system_handler:
-- Найти ID платёжной системы
SELECT ID, NAME, HANDLER_TYPE FROM b_sale_pay_system WHERE ACTIVE = 'Y';
-- Параметры конкретной платёжной системы
SELECT PS_ID, CODE, VALUE FROM b_sale_pay_system_params WHERE PS_ID = 5;
Программная работа с холдированием
Статусы оплаты в b_sale_payment:
-
N— не оплачен -
Y— оплачен (деньги списаны)
Кастомные статусы холдирования обычно хранятся в дополнительных полях или через b_sale_order_props.
Подтверждение оплаты (capture) через API:
use Bitrix\Sale;
// Получить заказ
$order = Sale\Order::load($orderId);
// Получить оплату
$paymentCollection = $order->getPaymentCollection();
foreach ($paymentCollection as $payment) {
if (!$payment->isPaid()) {
// Подтвердить холдированный платёж
$result = Sale\PaySystem\Manager::confirm($payment);
if (!$result->isSuccess()) {
// Обработать ошибку
$errors = $result->getErrorMessages();
}
}
}
Отмена холдирования:
foreach ($paymentCollection as $payment) {
$result = Sale\PaySystem\Manager::cancel($payment);
if ($result->isSuccess()) {
// Обнуляем сумму оплаты
$payment->setField('SUM', 0);
}
}
$order->save();
Обработчик события подтверждения заказа
Автоматическое подтверждение (capture) при переводе заказа в статус «Подтверждён»:
// /bitrix/php_interface/init.php
use Bitrix\Main\EventManager;
EventManager::getInstance()->addEventHandler(
'sale',
'OnSaleStatusOrderChange',
function(\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
$value = $event->getParameter('VALUE');
// Статус "подтверждён" — подтвердить холд
if ($value === 'C') { // ID вашего статуса подтверждения
$paymentCollection = $order->getPaymentCollection();
foreach ($paymentCollection as $payment) {
if (!$payment->isPaid()) {
\Bitrix\Sale\PaySystem\Manager::confirm($payment);
}
}
}
// Статус "отменён" — снять холд
if ($value === 'F') {
$paymentCollection = $order->getPaymentCollection();
foreach ($paymentCollection as $payment) {
\Bitrix\Sale\PaySystem\Manager::cancel($payment);
}
}
}
);
Срок действия холдирования
Банки ограничивают срок холдирования. Сбербанк — до 30 дней, большинство зарубежных эквайеров — 7 дней. Если за этот срок не подтвердить или не отменить, банк автоматически снимает резерв.
Настроить агент Битрикс, который проверяет просроченные холды:
// Агент: ProccessExpiredHolds()
function ProccessExpiredHolds() {
// Найти оплаты в статусе холд, старше 5 дней
$cutoffDate = new \Bitrix\Main\Type\DateTime();
$cutoffDate->add('-5D');
$res = \Bitrix\Sale\Internals\PaymentTable::getList([
'filter' => [
'PAID' => 'N',
'<DATE_BILL' => $cutoffDate,
'!SUM' => 0
],
'select' => ['ID', 'ORDER_ID']
]);
while ($row = $res->fetch()) {
// Отправить уведомление менеджеру о необходимости обработать заказ
\CEvent::Send('HOLD_EXPIRING', 's1', [
'ORDER_ID' => $row['ORDER_ID']
]);
}
return 'ProccessExpiredHolds();';
}







