Налаштування єдиної програми лояльності онлайн та оффлайн 1С-Bitrix
Клієнт накопив 500 бонусів на сайті і прийшов у магазин їх витратити. Касир не бачить бонусів — вони в іншій системі. Або навпаки: покупка у магазині не нараховує бали на онлайн-акаунт. Розрив між онлайн та оффлайн програмою лояльності — це не тільки UX-проблема, але й прямі втрати на повторних покупках.
Як влаштована лояльність у Bitrix
Модуль sale реалізує дисконтну систему через b_sale_user_discount (персональні скидки) та бонусну систему через b_sale_discount (правила кошика). Для повнофункціональної програми лояльності (накопичувальні бали) використовується окремий модуль або інтеграція з зовнішньою системою.
У Bitrix24 є CRM-модуль з бонусами (b_crm_loyalty_bonus_transaction), але для інтернет-магазину без Bitrix24 частіше застосовується модуль marketingcrm або власна таблиця транзакцій.
Структура зберігання бонусів
Мінімальна структура для єдиної програми:
CREATE TABLE bl_loyalty_account (
id SERIAL PRIMARY KEY,
user_id INT UNIQUE, -- b_user.ID (онлайн)
card_number VARCHAR(20) UNIQUE, -- номер карти для оффлайн
balance NUMERIC(10,2) DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE bl_loyalty_transaction (
id SERIAL PRIMARY KEY,
account_id INT NOT NULL REFERENCES bl_loyalty_account(id),
amount NUMERIC(10,2) NOT NULL, -- позитивне=нараховано, негативне=списано
type VARCHAR(20) NOT NULL, -- 'earn_online', 'earn_offline', 'spend', 'expire'
order_id INT, -- b_sale_order.ID або зовнішній ID оффлайн-чека
source VARCHAR(20) NOT NULL, -- 'web', 'pos', 'mobile'
created_at TIMESTAMP DEFAULT NOW()
);
Трансакційна модель із історією — єдиний надійний спосіб зберігання бонусів. Ніколи не оновлюйте balance напряму без запису транзакції. balance — це або денормалізований агрегат (оновлюється триггером БД), або розраховується як SUM(amount) з таблиці транзакцій.
Ідентифікація клієнта на оффлайн-касі
Ключева задача: касир має знайти акаунт клієнта. Способи ідентифікації:
- Номер карти лояльності (фізична карта або штрихкод у мобільному додатку)
- Номер телефону
- QR-код з токеном (генерується в особистому кабінеті сайту)
При ідентифікації за телефоном касове ПО надсилає запит до API Bitrix:
// /local/ajax/loyalty/find-account.php
$phone = normalizePhone($_POST['phone']);
$bitrixUser = \CUser::GetList([], ['PERSONAL_PHONE' => $phone])->Fetch();
if ($bitrixUser) {
$account = getLoyaltyAccount($bitrixUser['ID']);
echo json_encode(['balance' => $account['balance'], 'account_id' => $account['id']]);
}
Нарахування бонусів при онлайн-замовленні
Обробник зміни статусу замовлення — після доставки/завершення:
AddEventHandler('sale', 'OnSaleStatusOrderChange', function(\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
$status = $order->getField('STATUS_ID');
if ($status !== 'F') return; // Лише завершені замовлення
$userId = $order->getUserId();
$bonus = round($order->getPrice() * BONUS_RATE); // BONUS_RATE = 0.05 (5%)
addLoyaltyTransaction($userId, $bonus, 'earn_online', $order->getId(), 'web');
});
Списання бонусів при онлайн-покупці
Бонуси застосовуються через правило кошика або через кастомний платіжний метод. Правило кошика (b_sale_discount) може давати скидку фіксованої суми. Для більш гнучкої схеми — власний «платіжний метод» типу «Оплата бонусами», який при проведенні замовлення списує транзакцію з bl_loyalty_transaction.
Синхронізація через API для оффлайн-касл
Оффлайн-каса викликає три endpoint:
-
GET /loyalty/balance?phone=...— перевірити баланс -
POST /loyalty/spend— списати бонуси при продажі (має бути трансакційним: початок продажу → резервування → підтвердження) -
POST /loyalty/earn— нарахувати бонуси після продажу
Резервування при списанні — критично важливий крок. Без нього два паралельні запити від різних касл можуть одночасно прочитати баланс 500 бонусів та двічі списати по 500, йдучи в мінус. Резервування через SELECT ... FOR UPDATE в PostgreSQL або через суворе UPDATE з перевіркою результату:
UPDATE bl_loyalty_account
SET balance = balance - :amount
WHERE id = :account_id AND balance >= :amount
RETURNING balance;
-- Якщо оновлено 0 рядків — недостатньо бонусів
Що налаштовуємо
- Таблиці
bl_loyalty_accountтаbl_loyalty_transaction - API-endpoints для касових систем (баланс, нарахування, списання)
- Обробники подій
OnSaleStatusOrderChangeдля онлайн-нарахувань - Механізм ідентифікації клієнта за телефоном/карткою
- Трансакційне списання з захистом від race condition
- Адміністративний інтерфейс для перегляду історії транзакцій







