Коротко — купон нужно не просто «записать в БД», а пройти через менеджер купонов и заново пересчитать корзину/заказ. Частая ошибка — добавление купона в «нетот» контекст (неинициализированный site/fuser, неправильный режим менеджера) или отсутствие перерасчёта корзины, поэтому визуально купон не «сохраняется».
Ниже — рабочие варианты и чеклист для отладки.
1) Серверный (D7) — пример
(предполагается, что подключён модуль sale)
use Bitrix\Main\Loader;
use Bitrix\Sale;
Loader::includeModule('sale');
$siteId = SITE_ID; // или 's1'
$fuserId = Sale\Fuser::getId(); // ID временного покупателя
// Инициализируем менеджер купонов для текущего сайта
Sale\DiscountCouponsManager::init($siteId);
// Режим: для API/сценариев управления можно использовать MODE_MANAGER
Sale\DiscountCouponsManager::setMode(Sale\DiscountCouponsManager::MODE_MANAGER);
// Добавляем купон
Sale\DiscountCouponsManager::add($couponCode);
// Загружаем корзину и делаем перерасчёт, чтобы купон применился к позициям
$basket = Sale\Basket::loadItemsForFUser($fuserId, $siteId);
// Пересчитать скидки для корзины
\Bitrix\Sale\Discount::calculateBasket($basket);
// Получить список применённых купонов (если нужно)
$applied = Sale\DiscountCouponsManager::get(true);
// Сохранение/дальнейшие действия — например создание заказа с этой корзиной
// $order = Sale\Order::create($siteId, $userId); $order->setBasket($basket); $order->save();
2) Через AJAX (компонент корзины)
Если вы хотите «как делает корзина» — можно вызвать тот же AJAX-обработчик компонента:
POST /bitrix/components/bitrix/sale.basket.basket/ajax.php
параметры примерно: sessid, siteId, action = applyCoupon, coupon = 'CODE'
(точные параметры посмотрите в коде компонента в папке /bitrix/components/bitrix/sale.basket.basket)
3) Проблемы, из‑за которых купон «не сохраняется»
- Не подключён модуль sale (Loader::includeModule('sale')).
- Неинициализирован DiscountCouponsManager для текущего SITE_ID (важно при мультисайтах).
- Вы добавляете купон после того, как уже сделали финальный расчёт/сохранение заказа; нужно добавлять до расчёта.
- Не выполняете перерасчёт корзины/заказа после add(). Купон будет виден только после расчёта.
- Купон невалиден/истёк/привязан к другому сайту или пользователю, или имеет ограничения по товарам (тогда он не применится).
- Кеширование компонента корзины: компонент может показывать закешированные данные — почистите кеш или обновите компонент.
- Неправильный FUSER_ID / SITE_ID — купоны для fuser хранятся в привязке к fuser/site.
- Вы думаете, что «добавили в БД», но менеджер не создал запись в таблицах — лучше использовать DiscountCouponsManager, он правильно сохранит привязку.
4) Как проверить (дебаг)
- После add() вызовите Sale\DiscountCouponsManager::get(true) — должен вернуть массив с добавленным кодом.
- Посмотрите таблицу b_sale_discount_coupon (или через ORM \Bitrix\Sale\Internals\DiscountCouponTable) — должен появиться купон с FUSER_ID/SITE_ID.
- Включите логирование / var_dump результата \Bitrix\Sale\Discount::calculateBasket($basket) — посмотрите применённые скидки.
Если хотите, пришлите:
- фрагмент кода, как вы добавляете купон,
- SITE_ID и где/когда вы это выполняете (в момент запроса, в cron, после создания заказа),
— я проверю и укажу точную причину и правки.