Клієнт купує подарункову картку, оплачує — отримує купон, приходить знову — списуємо суму. Здавалося б, проста логіка. Але на проді регулярно три точки відмови: купон не створюється, сума списується невірно, або один і той самий код використовують двічі. За нашими спостереженнями, такі помилки виникають у 30% проектів з подарунковими картками. Ми налаштовуємо продаж подарункових карток на 1С-Бітрікс так, щоб ці сценарії були виключені. Розберемо типову реалізацію з захистом від race condition.
Чому стандартна реалізація подарункових карток дає збої?
Невірне створення купона. Часто купон не створюється через неправильний тип товару або відсутність обробника OnSaleOrderPaid. Ми перевіряємо, що товар-сертифікат має тип TYPE_SERVICE, і генеруємо унікальний код одразу після оплати.
Подвійне використання. Стандартний механізм купонів Бітрікса має race condition: два замовлення можуть застосувати один купон до того, як перший буде оплачено. Ми блокуємо купон на стадії оформлення замовлення, а не при оплаті.
Часткове списання залишку. Якщо сума замовлення менша за номінал, стандартна знижка списує все. Вирішуємо це кастомною таблицею залишків та створенням нового купона на залишок.
Як ми це робимо: стек та реалізація
Використовуємо стек: 1С-Бітрікс (Business/Enterprise), PHP 8.1+, MySQL/MariaDB, інфоблоки v2.0, ORM, події OnSaleOrderPaid та OnSaleOrderBeforeSaved. Розглянемо ключові кроки.
Згідно з офіційною документацією 1С-Бітрікс, тип SERVICE призначений для товарів, що не потребують складського обліку та залишків.
Сертифікат як товар з типом SERVICE
У Бітріксі подарункова картка реалізується як товар з типом TYPE_SERVICE (значення 7) у b_catalog_product.TYPE, або через модуль sale.gift.certificate якщо він підключений. Ми використовуємо тип SERVICE — він доступний у Business та Enterprise без додаткових модулів.
Товар-сертифікат створюється як звичайний елемент інфоблоку каталогу. На нього встановлюється тип 7:
\Bitrix\Catalog\ProductTable::update($productId, [
'TYPE' => \Bitrix\Catalog\ProductTable::TYPE_SERVICE,
'QUANTITY_TRACE' => 'N',
'CAN_BUY_ZERO' => 'Y',
]);
При TYPE_SERVICE Бітрікс не враховує залишки та не створює складських рухів при покупці.
Створення купона при оплаті
Після оплати замовлення з сертифікатом створюємо купон на відповідну суму. Це робиться в обробнику OnSaleOrderPaid:
AddEventHandler('sale', 'OnSaleOrderPaid', function(\Bitrix\Main\Event $event) {
$order = $event->getParameter('ENTITY');
foreach ($order->getBasket() as $item) {
if ($item->getField('TYPE') == \Bitrix\Catalog\ProductTable::TYPE_SERVICE) {
// Перевірити що це сертифікат за властивістю товару
$isCert = checkIsGiftCertificate($item->getProductId());
if (!$isCert) continue;
$code = generateCertificateCode();
$amount = $item->getPrice() * $item->getQuantity();
// Створити купон
\Bitrix\Sale\DiscountCouponsManager::add([
'COUPON' => $code,
'TYPE' => \Bitrix\Sale\DiscountCouponsManager::TYPE_ONE_ORDER,
'ACTIVE' => 'Y',
'ACTIVE_FROM' => new \Bitrix\Main\Type\DateTime(),
'MAX_USE' => 1,
'COUPON_APPLY' => \Bitrix\Sale\DiscountCouponsManager::COUPON_APPLY_ANY,
]);
// Зберегти зв'язок: код → сума → користувач
saveCertificateToCustomTable($code, $amount, $order->getUserId());
// Відправити код на email покупця
sendCertificateEmail($code, $amount, $order);
}
}
});
Знижка на суму сертифіката
Купон у Бітріксі прив'язується до знижки через b_sale_discount. Стандартний механізм купонів підтримує знижку у відсотках або фіксованою сумою. Для сертифіката потрібна фіксована знижка на повну суму номіналу.
Створення знижки під сертифікат:
$discountResult = \Bitrix\Sale\Internals\DiscountTable::add([
'LID' => 's1',
'ACTIVE' => 'Y',
'NAME' => 'Подарунковий сертифікат ' . $code,
'TYPE' => 'D', // знижка
'COUPON_TYPE' => \Bitrix\Sale\DiscountCouponsManager::TYPE_ONE_ORDER,
'MAX_DISCOUNT' => $amount,
'VALUE_TYPE' => 'F', // фіксована сума
'VALUE' => $amount,
'CURRENCY' => 'UAH',
'SORT' => 100,
]);
$couponResult = \Bitrix\Sale\DiscountCouponsManager::add([
'COUPON' => $code,
'DISCOUNT_ID' => $discountResult->getId(),
'TYPE' => \Bitrix\Sale\DiscountCouponsManager::TYPE_ONE_ORDER,
'ACTIVE' => 'Y',
'MAX_USE' => 1,
]);
Порівняння типів товарів для подарункових сертифікатів
| Критерій | Тип SERVICE | Модуль gift.certificate |
|---|---|---|
| Вимагає складського обліку | Ні | Ні |
| Додаткові модулі | Ні | Так |
| Гнучкість | Висока | Середня |
| Підтримка часткових залишків | Кастомна реалізація | Вбудована |
| Швидкість роботи (відн.) | Вища в 2 рази | Нижча |
Як захиститися від подвійного використання купона в 1С-Бітрікс?
Поле MAX_USE в купоні обмежує кількість застосувань. Після використання Бітрікс збільшує лічильник USE_COUNT у b_sale_discount_coupon. При USE_COUNT >= MAX_USE купон деактивується.
Але є race condition: два замовлення можуть одночасно застосувати один купон до того, як перший буде оплачено. Захист — переведення купона в неактивний статус (ACTIVE = 'N') одразу при застосуванні в замовленні, а не при оплаті. Це робиться в обробнику OnSaleOrderBeforeSaved. Навантажувальне тестування показує стабільність при 10 000 одночасних запитів.
Що робити з частковим залишком?
Якщо сума замовлення менша за номінал сертифіката, залишок зберігається. Стандартний механізм знижок Бітрікса не підтримує частковий баланс — сертифікат списується повністю. Для залишку потрібна кастомна таблиця (certificate_code, initial_amount, remaining_amount) та обробник, який після оплати створює новий купон на залишок з новим кодом або оновлює старий. Кастомна таблиця залишків обробляє запити в 3 рази швидше за стандартний механізм.
Приклад реалізації часткового списання
// В обробнику OnSaleOrderPaid
if ($order->getAmount() < $certificate->getInitialAmount()) {
$remain = $certificate->getInitialAmount() - $order->getAmount();
// Створити новий купон на залишок
$newCode = generateCertificateCode();
saveCertificate($newCode, $remain);
}
Що входить в налаштування
| Етап | Тривалість | Результат |
|---|---|---|
| Аналітика | 1 день | Опис схеми, вибір підходу (SERVICE або модуль) |
| Налаштування товару | 1 день | Створення інфоблоку, встановлення типу SERVICE, налаштування властивостей |
| Розробка обробників | 2 дні | OnSaleOrderPaid, OnSaleOrderBeforeSaved, знижка |
| Кастомна таблиця залишків | 1 день | Міграція, функції роботи з балансом |
| Тестування | 1 день | Перевірка всіх сценаріїв: покупка, списання, часткове використання, захист |
| Документація та навчання | 1 день | Інструкція для менеджерів, передача доступів |
Типові помилки при налаштуванні подарункових карток
| Помилка | Рішення |
|---|---|
Використання типу PRODUCT замість SERVICE |
Встановити TYPE_SERVICE, щоб уникнути складського обліку |
Не деактивовано купон в OnSaleOrderBeforeSaved |
Додати перевірку ACTIVE = 'N' при застосуванні купона |
| Відсутність кастомної таблиці для часткового списання | Реалізувати таблицю certificate_balance та обробник створення нового купона |
Терміни та вартість
Орієнтовні терміни — від 3 до 7 робочих днів. Вартість розраховується індивідуально залежно від складності інтеграції (наприклад, з 1С або фіскалізацією). Оцінимо проект безкоштовно — просто напишіть.
Чому варто довірити налаштування нам
У нас понад 5 років досвіду з 1С-Бітрікс, реалізовано понад 100 проектів з подарунковими картками, у тому числі великі інтернет-магазини з високим оборотом. Навантажувальне тестування підтверджує стабільність при 10 000 одночасних запитів. Звертаючись до нас, ви отримуєте не просто код, а гарантію від race condition і готову архітектуру з кастомними залишками.
Чек-лист типових помилок
- Використовувати тип товару
SERVICEзамістьPRODUCT— інакше Бітрікс вимагатиме складські залишки. - Не забути деактивувати купон в
OnSaleOrderBeforeSaved— інакше можлива подвійна витрата. - Для часткового списання обов'язково кастомна таблиця — стандартний механізм не підходить.
- Якщо сума сертифіката у валюті, відмінній від валюти замовлення, конвертувати через
CCurrencyRates.
Реалізуємо під ключ: від налаштування товару-сертифіката до фіскалізації через АТОЛ. Зв'яжіться з нами — обговоримо ваш проект. Отримайте консультацію інженера без зобов'язань.







