Setting up funds holding in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Setting Up Fund Holding on 1C-Bitrix

The customer paid for the order — the money is debited, but the order is not yet confirmed. Two days later, it turns out there is no product in stock. A refund — that's another few days. Fund holding (two-stage payment) solves this problem: the bank reserves the amount on the customer's card, and the actual debit occurs only after confirming the product is available.

Two-Stage Payment: How It Works

Stage 1 — Hold (authorization): the store sends a request to the payment system to reserve the amount. The money is not debited, but blocked on the card. The acquirer bank issues a pre_auth_code or similar authorization identifier.

Stage 2 — Confirmation (capture): the store confirms the debit when the product is ready to ship. The money goes to the store's account. Or — cancellation (void/cancel): if there is no product, the reserve is removed without fees.

Bitrix implements this via the sale module, \Bitrix\Sale\PaySystem\Manager class, and specific payment system handlers.

Two-Stage Payment Support in the sale Module

Not all payment systems in Bitrix support two-stage payment. Built-in support is available from:

  • Sberbank (via SberPayment handler)
  • Tinkoff (TinkoffPayment)
  • YooKassa — YooKassa (YandexMoney)
  • Alfa-Bank (AlfaPayment)

Payment system configuration in the admin panel: "Store" → "Payment" → select payment system → "Settings" tab → "Two-Stage Payment" or "Holding Mode" parameter.

Holding Configuration in Database Table

Payment system parameters are stored in tables b_sale_pay_system and b_sale_pay_system_handler:

-- Find payment system ID
SELECT ID, NAME, HANDLER_TYPE FROM b_sale_pay_system WHERE ACTIVE = 'Y';

-- Parameters of a specific payment system
SELECT PS_ID, CODE, VALUE FROM b_sale_pay_system_params WHERE PS_ID = 5;

Programmatic Work with Holding

Payment statuses in b_sale_payment:

  • N — not paid
  • Y — paid (money debited)

Custom holding statuses are usually stored in additional fields or via b_sale_order_props.

Confirming payment (capture) via API:

use Bitrix\Sale;

// Get order
$order = Sale\Order::load($orderId);

// Get payment
$paymentCollection = $order->getPaymentCollection();
foreach ($paymentCollection as $payment) {
    if (!$payment->isPaid()) {
        // Confirm held payment
        $result = Sale\PaySystem\Manager::confirm($payment);
        if (!$result->isSuccess()) {
            // Handle error
            $errors = $result->getErrorMessages();
        }
    }
}

Canceling hold:

foreach ($paymentCollection as $payment) {
    $result = Sale\PaySystem\Manager::cancel($payment);
    if ($result->isSuccess()) {
        // Zero out payment amount
        $payment->setField('SUM', 0);
    }
}
$order->save();

Event Handler for Order Confirmation

Automatic confirmation (capture) when order is moved to "Confirmed" status:

// /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');

        // "Confirmed" status — confirm hold
        if ($value === 'C') { // ID of your confirmation status
            $paymentCollection = $order->getPaymentCollection();
            foreach ($paymentCollection as $payment) {
                if (!$payment->isPaid()) {
                    \Bitrix\Sale\PaySystem\Manager::confirm($payment);
                }
            }
        }

        // "Cancelled" status — lift hold
        if ($value === 'F') {
            $paymentCollection = $order->getPaymentCollection();
            foreach ($paymentCollection as $payment) {
                \Bitrix\Sale\PaySystem\Manager::cancel($payment);
            }
        }
    }
);

Holding Validity Period

Banks limit the holding period. Sberbank — up to 30 days, most foreign acquirers — 7 days. If not confirmed or canceled within this period, the bank automatically removes the reserve.

Set up a Bitrix agent to check for expired holds:

// Agent: ProccessExpiredHolds()
function ProccessExpiredHolds() {
    // Find payments in hold status, older than 5 days
    $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()) {
        // Send notification to manager about the need to process the order
        \CEvent::Send('HOLD_EXPIRING', 's1', [
            'ORDER_ID' => $row['ORDER_ID']
        ]);
    }

    return 'ProccessExpiredHolds();';
}