← усі кейси
Виробник і роздріб продуктів здорового харчування В роботі E-commerce / FoodTech ·

Бонусна програма, яка живе окремо від сайту і від CRM

Бали клієнтів рахує один сервіс, а сайт, кошик, CRM і бот лише питають у нього. Стартові баланси нараховано 3 822 клієнтам, списання перевірено на живому кошику.

  • Стартові баланси нараховано 3 822 клієнтам, 137 098 балів
  • Списання перевірено наскрізно на живому кошику: сума до оплати зменшилась рівно на кількість списаних балів
  • Знайдено діру в обліку: 18% замовлень, близько 107 на місяць, проходили повз програму
  • Понад 100 автотестів на арифметику балів і на те, щоб повторний запуск нічого не дублював
Python 3.12FastAPIUvicornpsycopgPostgreSQL 16Docker ComposeTraefik
Статус проєкту

Кейс у активній розробці. Рушій балів працює і перевірений наскрізно, розкатка на всю базу поетапна: частина правил і текстів чекає рішення власниці. Ефект на повторні покупки фіксуємо на даних клієнта після запуску.

Власник магазину каже: «зробіть нам бали, як у всіх». Через місяць виявляється, що бали є на сайті, але їх не бачить кол-центр. Ще через місяць - що менеджер списав клієнту більше, ніж у нього було, бо сайт і CRM рахували незалежно.

Проблема бонусної програми не в тому, щоб намалювати цифру в кабінеті. Вона в тому, що балами користуються в чотирьох різних місцях, і кожне з них має отримати ту саму відповідь.

Ми зробили так, що бали не належать ні сайту, ні CRM. Вони живуть в окремому сервісі, а всі інші лише питають у нього і отримують однакову відповідь.

3 822
клієнти зі стартовим балансом
4
точки роботи з балами, одне правило
18%
замовлень проходили повз програму

Проблема: бали легко нарахувати і майже неможливо не заплутати

Бонусна програма виглядає простою рівно доти, доки нею користується одна людина в одному вікні. Щойно каналів стає більше, починається арифметика, яку ніхто не замовляв.

Три місця, де програма ламається на практиці

Подвійне списання

  • клієнт застосував бали в кошику
  • менеджер у той самий момент списав їх у картці замовлення
  • баланс пішов у мінус

Різні відповіді

  • сайт показує один баланс
  • кол-центр бачить інший
  • клієнт довіряє тому, хто пообіцяв більше

Невидимі канали

  • програма рахує тільки замовлення з двох сайтів
  • телефон, месенджери й роздріб лишаються поза нею
  • клієнт не розуміє, чому балів немає

Третій пункт виявився не гіпотезою. Коли ми звірили всі канали продажу з тими, які обробляла програма, різниця склала 18% замовлень - близько 107 на місяць. Це покупки реальних клієнтів, за які їм ніхто нічого не нарахував.

Як автоматизація це вирішує

Уся арифметика балів винесена в один сервіс. Сайт, кошик, картка замовлення в CRM і бот у месенджері не рахують нічого самі - вони питають і отримують готову відповідь.

Нарахування: після того, як замовлення реально виконане
Замовлення виконано перевірка каналу нарахування лотом баланс клієнта
Кошик на сайті резерв балів замовлення створено списання підтверджено

Ключова ідея: бали списуються у два кроки

Коли клієнт застосував бали в кошику, вони ще не списані - вони зарезервовані. Списання підтверджується тільки в момент, коли замовлення реально створене. Якщо клієнт передумав і закрив вкладку, резерв сам розсмоктується через півгодини.

Це рятує від найдорожчої помилки бонусних програм: балів, які списались за замовлення, якого не існує.

Що дістається менеджеру

У картці замовлення менеджер бачить готову фразу: скільки в клієнта балів, скільки з них можна списати саме з цього чека і чи варто про них згадати в розмові. Ніякої арифметики в голові.

Якщо менеджер вписує суму списання, система не «списує ще раз», а доводить фактичне списання до вказаного числа. Тому менеджер може виправити свою ж помилку: змінив 200 на 300 - система дописала сотню, змінив на нуль - повернула все.

Що це дало бізнесу

Клієнт отримав програму, яку не треба тримати в голові. Бали однакові скрізь: у кабінеті на сайті, в кошику, в розмові з менеджером і в месенджері. Стартові баланси вже нараховані, історія кожної операції зберігається, а помилку менеджера можна відкотити тим самим полем, яким вона зроблена.

Другий, менш очікуваний результат - програма показала стан бази. Розподіл стартових балів виявився таким:

Стартове нарахування: кому скільки дісталось
Менше 20 балів1 585 осіб
20-50 балів1 433
50-100 балів619
Понад 100 балів185

Медіана - 24 бали. Тобто кешбек структурно дає найменше саме тим, кого треба повертати: разовим покупцям із невеликим чеком. Це не привід не робити програму, але це привід не чекати від неї чуда без другого механізму - подарунка за активацію або підвищеного відсотка саме на другу покупку.

Чого система не робить. Вона не вирішує за власника, скільки коштує лояльність, і не робить із разового покупця постійного сама по собі. Бали - це інструмент повернення, а не причина повернутись. Причина - продукт і сервіс.

Як це влаштовано всередині

Далі - інженерна частина

Наступний розділ пояснює, як система влаштована. Якщо вас цікавить лише результат, його можна пропустити - нижче є розділ про те, кому це підходить.

Шари системи
ЯдроБали зберігаються лотами з власним терміном життя. Списання йде за FIFO: першими згорають ті, що згоріли б раніше. Блокування рядка на час операції знімає гонку «сайт і менеджер одночасно»
APIЄдина точка входу для сайту, кошика, CRM і бота. Окремий метод «скільки можна списати з цього чека» - щоб ліміт рахувався в одному місці, а не в кожному клієнті
КлючіДві ролі. Ключ сайту вміє читати, резервувати й підтверджувати; малювати бали не вміє. Витік ключа з вітрини не дає намалювати собі баланс
ВоркерНарахування раз на пів години за курсором дати оновлення, окремим курсором на кожен канал продажу. Збій циклу не рухає курсор - замовлення дограються наступного разу
ВітринаМодуль магазину нічого не рахує, лише запитує. Недоступність сервісу не блокує оформлення замовлення - це окреме правило, прописане в специфікації

Три рішення, які виявились принциповими:

Відсоток - це абсолютна межа на замовлення, а не квота, що зростає. Перша версія рахувала ліміт як «30% від чека плюс уже списане» і після часткового списання дозволяла вибрати 42% чека замість 30%. Спіймав тест, а не клієнт.

Вебхук CRM приходить на кожну зміну замовлення - статус, ТТН, коментар. Тому будь-яка операція, зроблена «за подією», мусить бути рухом до цільового стану, а не додаванням. Інакше одне списання повторюється десятки разів за життя замовлення.

Курсор часу треба ставити в часовій зоні джерела. База працює в UTC, CRM пише київський час. Курсор, поставлений через час бази, зсунувся б на три години і повторно нарахував би бали за вже оброблений період.

Кому це підходить

Магазинам і мережам, де замовлення приходять більш ніж з одного місця: сайт, телефон, месенджери, роздрібна точка, маркетплейс. Чим більше каналів, тим дорожче обходиться програма, зроблена всередині сайту.

Як зрозуміти, що це про вас: якщо на питання «скільки в цього клієнта балів» менеджер відповідає «зараз подивлюсь на сайті», програма вже живе не там, де треба.

Бонусна програма - це не поле в базі. Це одна відповідь на одне питання, яку однаково чують клієнт, менеджер і касир.

Потрібне схоже рішення?

Опишіть задачу - підберу архітектуру під ваш бюджет і дані.