Setting up order confirmation via SMS code 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 Order Confirmation via SMS Code in 1C-Bitrix

Large stores with a high average order value sometimes have a requirement: confirm the buyer's intent via SMS before finalizing the order. This reduces the number of "accidental" orders and verifies the phone number, which is then used in a loyalty program. The standard 1C-Bitrix checkout does not have this mechanism — the order process must be intercepted.

Confirmation Architecture

Flow: the customer fills out the order form → clicks "Place Order" → the system sends an SMS with a code to the specified phone number → the customer enters the code → the order is created. The order in 1C-Bitrix is created only after successful code verification, not before.

Code storage — in the local_order_confirmations table:

CREATE TABLE local_order_confirmations (
    ID         INT AUTO_INCREMENT PRIMARY KEY,
    PHONE      VARCHAR(20) NOT NULL,
    CODE       VARCHAR(6) NOT NULL,
    SESSION_ID VARCHAR(128),
    ATTEMPTS   TINYINT DEFAULT 0,
    CONFIRMED  CHAR(1) DEFAULT 'N',
    CREATED_AT DATETIME NOT NULL,
    EXPIRES_AT DATETIME NOT NULL,
    INDEX idx_phone_code (PHONE, CODE),
    INDEX idx_session (SESSION_ID)
);

Code lifetime — 5 minutes. Maximum attempts — 3. After exhaustion — a new code request with a delay (rate limit).

Integration into Checkout

If sale.order.ajax is used — intercept its JavaScript. Capture the form submission event:

// In result_modifier.php of the component or an included JS file
BX.addCustomEvent('onSaleComponentOrderSuccess', function(order) {
    // Standard handling — disabled
});

document.querySelector('.order-confirm-btn').addEventListener('click', async (e) => {
    e.preventDefault();

    const phone = document.querySelector('[name="ORDER_PROP_PHONE"]').value;

    // Request SMS
    const res = await fetch('/local/api/order-confirm/send', {
        method: 'POST',
        headers: {'Content-Type': 'application/json', 'X-Bitrix-Csrf-Token': BX.bitrix_sessid()},
        body: JSON.stringify({phone})
    });

    if (res.ok) {
        showCodeInputModal(phone);
    }
});

After successful code confirmation, the frontend sends a confirmation token along with the order data; the server validates the token before creating the order.

Server Side

class OrderConfirmController
{
    public function sendCode(): void
    {
        $phone = $this->normalizePhone($_POST['phone'] ?? '');
        if (!$phone) { $this->error('Invalid phone number'); }

        // Rate limit: no more than 3 sends in 15 minutes
        if ($this->isRateLimited($phone)) {
            $this->error('Too many requests. Please wait 15 minutes.');
        }

        $code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
        $expiresAt = (new \DateTime())->modify('+5 minutes');

        OrderConfirmationTable::add([
            'PHONE'      => $phone,
            'CODE'       => password_hash($code, PASSWORD_DEFAULT), // do not store plain code
            'SESSION_ID' => session_id(),
            'EXPIRES_AT' => \Bitrix\Main\Type\DateTime::createFromTimestamp($expiresAt->getTimestamp()),
        ]);

        SmsService::send($phone, "Your order confirmation code: {$code}. Valid for 5 minutes.");
        $this->success(['expires_in' => 300]);
    }

    public function verifyCode(): void
    {
        $phone = $this->normalizePhone($_POST['phone'] ?? '');
        $code  = $_POST['code'] ?? '';

        $record = OrderConfirmationTable::getActiveRecord($phone, session_id());

        if (!$record) {
            $this->error('Code not found or expired');
        }

        // Increment attempts
        OrderConfirmationTable::incrementAttempts($record['ID']);

        if ($record['ATTEMPTS'] >= 3) {
            $this->error('Maximum attempts exceeded. Please request a new code.');
        }

        if (!password_verify($code, $record['CODE'])) {
            $this->error('Invalid code');
        }

        // Code is valid — issue a one-time token for order creation
        $token = bin2hex(random_bytes(32));
        OrderConfirmationTable::markConfirmed($record['ID'], $token);

        $this->success(['token' => $token]);
    }
}

The code is stored in the database in hashed form — password_hash(). Even if the database is compromised, the codes are not exposed.

Token Validation on Order Creation

Before the standard order creation via OnBeforeSaleOrderSaved or controller override:

AddEventHandler('sale', 'OnBeforeSaleOrderSaved', function(\Bitrix\Sale\Order $order, array $data) {
    if (!ConfirmConfig::isRequiredForOrder($order)) {
        return; // not all orders require confirmation
    }

    $token = $_POST['confirm_token'] ?? '';
    if (!OrderConfirmationTable::isValidToken($token, session_id())) {
        throw new \Exception('Order not confirmed via SMS');
    }
});

SMS Provider

Integration with the provider via abstraction:

interface SmsProviderInterface {
    public function send(string $phone, string $message): bool;
}

Implementations: SMS.ru, SMSC.ru, MTS Communicator, integration via Bitrix24 SMS. Switching the provider without changing business logic.

Scope of Work

  • Confirmation table, rate limit and attempts logic
  • API controller: send code, verify, issue token
  • Checkout integration (JS interception, token passing)
  • Token validation before order creation
  • SMS provider connection

Timelines: 5–7 days with one SMS provider. 1.5–2 weeks with a custom checkout.