Запуск программы лояльности
Задача: необходимо запустить систему лояльности в приложении. В списке товаров и детальной карточке, отображаем количество бонусов для начисления.В оформлении заказа даем списать и начислить. В личном кабинете отображаем историю списания, начисления и количество активных бонусов.
Условия программы лояльности
- За каждый товар со скидкой, начисляется 5% от конечной стоимости
- За товар БЕЗ скидки, начисляются 10% от стоимости
- Если клиент положил в корзину, товаров более чем на 5000 руб., дополнительно начислим 5% от суммы всей корзины
- Списать можно только 30% от суммы покупки
Создание новой платежной системы
В административном разделе сайта создаем новую платежную систему, с параметрами:
- Обработчик: Наличный расчет (cash)
- Название: Оплата бонусами
Привяжем созданную платежную систему в настройках модуля MobiusApp

Расчет количества бонусов в списке товаров
Для расчета бонусов и вывод их в списке товаров и детальной карточке, нам нужно написать обработчик события в БУСе. Здесь официальная документация на этот счет.
Приступим к пошаговой реализации.
Создадим файл /local/php_interface/mobiusapp_loyalty.php с содержимым:
use Bitrix\Main;
class MobiusAppLoyaltyEvents
{
/**
* Этот метод будет вызван для каждого элемента каталога при его выводе в мобильном приложении
* Результат выполнения функции всегда должен быть целым числом
*/
public static function onCatalogElementLoyaltyCalculate(Main\Event $event) :int
{
$result = $event->getParameter('attributes');
// На товар без скидки начисляем 10% на товар со скидкой 5%
return (int)$result['BASE_PRICE'] > (int)$result['PRICE'] ? $result['PRICE'] * 0.05 : $result['PRICE'] * 0.1;
}
}
Main\EventManager::getInstance()->addEventHandler(
'mobiusapp.backend',
'onCatalogElementLoyaltyCalculate',
['MobiusAppLoyaltyEvents', 'onCatalogElementLoyaltyCalculate']
);
Добавим в конец файла /local/php_interface/init.php строку (если файла нет - создаем):
include_once(__DIR__ . "/mobiusapp_loyalty.php");
Проверим работоспособность, вызвав api-метод списка товаров. Ответ должен содержать корректный JSON-объект и у каждого элемента должен быть ключ "bonuses" отличный от нуля.
Вывод бонусов в профиле
Количество активных бонусов
Расширим содержание файла /local/php_interface/mobiusapp_loyalty.php новой подпиской на событие onGetCurrentLoyaltyBalance:
getParameter('user');
$eventResult = new Main\Entity\EventResult($event->getEventType());
if (!empty($user['ID']))
{
$resource = \Bitrix\Main\UserTable::getList([
'select' => [
"ID",
"UF_ACTIVE_BONUSES"
],
'filter' => [
'=ID' => $user['ID'],
],
'limit' => 1
])->fetch();
$result["currentAmount"] = $resource && $resource['UF_ACTIVE_BONUSES'] ? (int)$resource['UF_ACTIVE_BONUSES'] : 0;
$eventResult->modifyFields($result);
}
return $eventResult;
}
}
...
Main\EventManager::getInstance()->addEventHandler(
'mobiusapp.backend',
'onGetCurrentLoyaltyBalance',
['MobiusAppLoyaltyEvents', 'onGetCurrentLoyaltyBalance']
);
Проверим работоспособность, вызвав api-метод получения текущего бонусного счета клиента. Ответ должен содержать корректный JSON-массив. Каждый элемент должен соответствовать структуре:
{
"data": {
"currentAmount": 999
}
}
История списания и начисления
Расширим содержание файла /local/php_interface/mobiusapp_loyalty.php новой подпиской на событие onGetLoyaltyHistory:
getParameter('user');
$eventResult = new Main\Entity\EventResult($event->getEventType());
if (!empty($user['ID']))
{
// Здесь код для получения выборки записей истории списания и начисления
// Например из стороннего API или напрямую из БД
// Сформируем массив с фиксированным набором ключей
$result = [];
$result[] = [
"AMOUNT" => 999,
"TITLE" => "Начисление за покупку",
"CREATED_DATE" => "20.10.2024"
];
$result[] = [
"AMOUNT" => -200,
"TITLE" => "Списание",
"CREATED_DATE" => "18.10.2024"
];
$eventResult->modifyFields($result);
}
return $eventResult;
}
}
...
Main\EventManager::getInstance()->addEventHandler(
'mobiusapp.backend',
'onGetLoyaltyHistory',
['MobiusAppLoyaltyEvents', 'onGetLoyaltyHistory']
);
Проверим работоспособность, вызвав api-метод получения истории. Ответ должен содержать корректный JSON-массив. Каждый элемент должен соответствовать структуре:
{
"data": [
{
"title": "Начисление за покупку",
"createdDate": "20.10.2024",
"amount": 999
},
{
"title": "Списание",
"createdDate": "18.10.2024",
"amount": -200
}
],
"meta": null
}
Расчет бонусов к списанию на экране оформления заказа
Обработчик события будет вызван при старте экрана заказа и непосредственно при оформлении заказа.
Расширим содержание файла /local/php_interface/mobiusapp_loyalty.php новой подпиской на событие onOrderLoyaltyCalculate:
getParameter('user');
$result = $event->getParameter('result');
$order = $event->getParameter('order');
$eventResult = new Main\Entity\EventResult($event->getEventType());
if ($order && !empty($user['ID']))
{
// Получим актуальную корзину из заказа
// Для товаров со скидкой начислим 5%, для товаров без скидки 10%
// Если клиент списывает бонусы, начислится только 5%
$discounts = $order->getDiscount();
$discountResult = $discounts->calculate();
$basket = $order->getBasket()->getOrderableItems();
if ($basket->isEmpty())
return $eventResult;
if ($discountResult->isSuccess())
{
$showPrices = $discounts->getShowPrices();
if (!empty($showPrices['BASKET']))
{
foreach ($showPrices['BASKET'] as $basketCode => $data)
{
$basketItem = $basket->getItemByBasketCode($basketCode);
if ($basketItem instanceof \Bitrix\Sale\BasketItemBase)
{
$basketItem->setFieldNoDemand('BASE_PRICE', $data['SHOW_BASE_PRICE']);
$basketItem->setFieldNoDemand('PRICE', $data['SHOW_PRICE']);
$basketItem->setFieldNoDemand('DISCOUNT_PRICE', $data['SHOW_DISCOUNT']);
}
}
unset($basketItem, $basketCode, $data);
}
unset($showPrices);
}
unset($discountResult);
// Ниже представлена структура результирующего массива. Сохраните его структуру.
$finallyResult = array_merge($result, [
"canSpent" => 0, // Количество бонусов, доступных к списанию для этого заказа
"accrualAmount" => [ // Массив с данными о начислении
"withSpend" => 0, // Сумма начисления когда клиент СПИСЫВАЕТ бонусы
"withoutSpend" => 0 // Сумма к начислению БЕЗ СПИСАНИЯ бонусов
]
]);
foreach ($basket as $basketItem)
{
// Списать можно 30% от стоимости корзины
$finallyResult["canSpent"] += ($basketItem->getQuantity() * $basketItem->getPrice()) * 0.3;
if ($basketItem->getPrice() < $basketItem->getBasePrice())
{
// Товар СО скидкой. Начисляем только 5%
$finallyResult["accrualAmount"]["withSpend"] += ($basketItem->getQuantity() * $basketItem->getPrice()) * 0.05;
$finallyResult["accrualAmount"]["withoutSpend"] += ($basketItem->getQuantity() * $basketItem->getPrice()) * 0.05;
}
else
{
// Товар БЕЗ скидки. Начисляем 10%, если клиент НЕ списывает бонусы и 5% если списывает
$finallyResult["accrualAmount"]["withSpend"] += ($basketItem->getQuantity() * $basketItem->getPrice()) * 0.05;
$finallyResult["accrualAmount"]["withoutSpend"] += ($basketItem->getQuantity() * $basketItem->getPrice()) * 0.1;
}
}
$eventResult->modifyFields($finallyResult);
}
return $eventResult;
}
}
...
Main\EventManager::getInstance()->addEventHandler(
'mobiusapp.backend',
'onOrderLoyaltyCalculate',
['MobiusAppLoyaltyEvents', 'onOrderLoyaltyCalculate']
);
Списание бонусов при оформлении заказа
В примере ниже, мы умышленно не написали код на списание бонусов, вы должны сделать это самостоятельно