Бонусная карта в Telegram Mini App: баллы, покупки и возвраты без путаницы
На экране у покупателя тысяча баллов. В отчёте кассира — восемьсот. В таблице маркетолога — полторы тысячи, потому что туда ещё не попал вчерашний возврат. Все три числа выглядят убедительно, особенно если рядом поставить красивый значок подарка.
Программа лояльности часто начинается с простого пожелания: «Давайте начислять бонусы, чтобы люди возвращались». Для интерфейса действительно достаточно показать карту и баланс. Для работающей системы придётся ответить на более интересный вопрос: откуда взялось каждое изменение этого баланса и что произойдёт, если покупку отменят.
Разберём небольшую программу магазина: баллы начисляются за завершённую покупку, позволяют получить скидку на следующую и могут иметь срок действия. Это собственные бонусы магазина, не Telegram Stars и не денежный счёт пользователя.
Содержание
- Правила лучше написать раньше приложения
- Карта в Telegram — это вход в личный кабинет
- Баланс — результат журнала, а не один редактируемый столбец
- Начисление привязывается к покупке, а не к сообщению
- Списание требует резерва
- Возврат не всегда равен обратному начислению
- Сгорание баллов должно быть объяснимым
- Сотруднику нужны полномочия, а не кнопка на все случаи
- Проверка программы на маленьком наборе покупок
- Где разместить бонусный сервис
VPS для вашей программы лояльности
Разместите личный кабинет и журнал бонусных операций на собственном VPS. Начните с пилота, сверки начислений и проверки возвратов.
Выбрать VPS для Mini AppПравила лучше написать раньше приложения
Возьмите один лист и опишите программу обычными словами. С какой суммы начисляются баллы? Учитывается ли доставка? Можно ли оплатить ими акционный товар? Как округляется результат? Когда начисление становится доступным? Что происходит при частичном возврате?
Если на эти вопросы нет однозначных ответов, разработчик всё равно выберет поведение. Только правила окажутся разбросаны по условиям в коде, а покупатель узнает о них через неприятный сюрприз.
Для пилота можно выбрать простую модель: начислять определённый процент от стоимости подходящих товаров после применения скидок, разрешать списание до установленной доли следующей покупки и открывать начисление после завершения заказа. Конкретные проценты задаёт экономика магазина; техническая статья не может вычислить их без вашей маржи и статистики возвратов.
Сохраните версию правил и дату её действия. Если условия поменялись в ноябре, октябрьскую покупку нужно объяснять по октябрьским условиям. Пересчитывать всю историю новым коэффициентом — удобный способ устроить неожиданный праздник, правда, не обязательно для магазина.
Карта в Telegram — это вход в личный кабинет
В Mini App покупатель видит доступные баллы, ожидающие начисления, ближайшее сгорание и историю операций. Эти значения стоит показывать раздельно. Большое число с мелкой подписью «пока недоступно» только создаёт лишний разговор на кассе.
Telegram помогает открыть интерфейс и передать данные запуска. Сервер проверяет их согласно документации Mini Apps, а затем связывает подтверждённого пользователя с записью программы лояльности. Номер телефона или имя из формы не должны автоматически давать доступ к чужой карте.
Если у магазина уже есть клиентская база, предусмотрите отдельную привязку старой карты. Например, подтверждение через ранее проверенный контакт или сотрудника с журналом действий. Совпадение фамилии не является достаточным основанием для объединения двух клиентов.
Не объединяйте карты автоматически и по одинаковому отображаемому имени Telegram. У людей бывают тёзки, а у пользователей мессенджера — ещё и свободное время на переименование профиля.
Баланс — результат журнала, а не один редактируемый столбец
Храните каждое начисление, списание, отмену, истечение и ручную корректировку отдельной операцией. У неё есть идентификатор, клиент, величина изменения, причина, ссылка на исходное событие и время.
Сам баланс можно поддерживать как быстро обновляемое значение, но он должен проверяться по журналу. Ручное исправление числа без объяснения лишает систему памяти: покупатель видит изменение, а поддержка может только предполагать, что произошло.
Удобно разделить три состояния: ожидающие баллы, доступные и зарезервированные для незавершённой покупки. Это не три независимых подарка. Один и тот же объём не должен одновременно учитываться как доступный и уже обещанный для списания.
Пример внутренней записи события может выглядеть так. Это модель вашего приложения, а не готовое поле Telegram API:
{
"event_id": "purchase-4821-completed-v1",
"customer_id": 817,
"kind": "earn",
"points": 240,
"order_id": 4821,
"rule_version": "loyalty-2026-10"
}
Идентификатор события помогает узнать повторную доставку. Ограничьте его уникальность в базе, а при конфликте сравните содержимое с уже обработанным событием. Повтор с другими данными должен попасть на разбор, а не молча считаться успешным.
Механизмы таких гарантий описаны в документации ограничений PostgreSQL. Главное практическое правило: проверку уникальности выполняет база, а не только предварительный запрос приложения.
Начисление привязывается к покупке, а не к сообщению
Источник события — ваша учётная система или сервер магазина. Клиентский интерфейс не должен присылать команду «начислить мне двести баллов» с готовой суммой. Сервер получает подтверждённый заказ и сам рассчитывает результат по сохранённым правилам.
Определите момент начисления: оплата, отгрузка, завершение заказа или окончание выбранного периода ожидания. Это бизнес-решение, которое должно совпадать с текстом условий. Если начисление пока ожидается, покупатель видит причину и предполагаемое действие, после которого оно станет доступным.
Сохраняйте исходную сумму и состав расчёта. Для заказа с несколькими товарами пригодится распределение начисления по позициям. Тогда при частичном возврате не придётся заново угадывать, какие баллы относились к возвращённой вещи.
События из учётной системы могут приходить повторно и не по порядку. Если завершение заказа доставили раньше его создания, положите событие в ожидание либо запросите актуальное состояние источника. Не создавайте случайного клиента и пустой заказ только ради того, чтобы очередь выглядела обработанной.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
АРЕНДОВАТЬ СЕРВЕРСписание требует резерва
Покупатель открывает два заказа и пытается использовать одни и те же баллы в обоих. Если приложение проверяет баланс отдельно от списания, оба запроса могут увидеть достаточную сумму.
Резервируйте баллы атомарно: проверьте доступный остаток, создайте резерв и уменьшите доступную часть в одной транзакции. Для конкретной реализации можно блокировать строку бонусного счёта на время операции; поведение таких блокировок описано в руководстве PostgreSQL.
У резерва есть срок и связь с заказом. Завершённая покупка переводит его в окончательное списание. Отмена освобождает резерв ровно один раз. Обработчик просроченных резервов должен проверять состояние заказа, чтобы не вернуть баллы в тот момент, когда платёж уже подтверждается.
Повторное нажатие «Применить бонусы» возвращает существующий резерв по ключу операции. Если покупатель меняет корзину, сумма скидки пересчитывается на сервере, а новый вариант явно показывается до подтверждения.
Возврат не всегда равен обратному начислению
При полном возврате нужно решить две задачи: отменить баллы, заработанные на возвращённой покупке, и восстановить баллы, которыми её частично оплатили. Это разные движения журнала.
Если начисленные баллы уже потрачены, механическое уменьшение доступного остатка может увести его ниже нуля. Возможные правила — отдельная задолженность по бонусам, ограничение новых списаний или иной заранее согласованный порядок. Нельзя придумывать этот порядок в момент претензии; он должен быть определён в условиях программы и проверен на ваших сценариях.
Частичный возврат сложнее. Скидку и начисление распределяют по товарным позициям с сохранённым правилом округления. Для последней возвращаемой позиции учитывают накопившийся остаток округления, чтобы сумма частичных операций совпала с полным возвратом.
Не удаляйте первоначальные записи журнала. Создавайте связанные корректирующие операции. Тогда поддержка сможет показать последовательность: покупка, начисление, использование, возврат и изменение доступного остатка.
Сгорание баллов должно быть объяснимым
Если у начислений есть разные сроки, храните их отдельными партиями. При списании определите порядок расходования, например сначала ближайшие к истечению. Иначе человеку могут сгореть старые баллы, пока система зачем-то расходовала новые.
Фоновая задача истечения обрабатывает конкретные партии и записывает отдельные события. Повторный запуск не должен списывать их ещё раз. Для резервов заранее решите, сохраняется ли право использования после наступления срока, пока заказ находится в допустимом ожидании.
В личном кабинете показывайте понятный прогноз: сколько и когда станет недоступно. Напоминание полезно, если человек разрешил сообщения, но отсутствие доставки не должно менять финансовую логику программы. В приложении информация остаётся доступной независимо от уведомления.
Сотруднику нужны полномочия, а не кнопка на все случаи
Разделите просмотр истории, ручное начисление, списание и изменение правил. Сотрудник поддержки может объяснить движение баллов, не получая возможности подарить произвольный баланс любому аккаунту.
Для корректировки указывайте причину, исходное обращение и автора. Крупные или необычные изменения можно отправлять на дополнительное согласование. Удалять следы корректировок через обычный интерфейс не следует.
QR-код карты помогает быстро найти клиента на кассе, но статический код сам по себе не должен разрешать списание. Для чувствительной операции пригодится подтверждение в личном кабинете или другой согласованный способ проверки владельца. Скриншот карты легко переживает и смену телефона, и добрые отношения между людьми.
Проверка программы на маленьком наборе покупок
Составьте учебный набор: обычная покупка, покупка со скидкой, повтор события, полная отмена, частичный возврат, два одновременных списания и истечение баллов в резерве. Для каждого случая посчитайте результат вручную до запуска приложения.
Сверьте журнал, доступный остаток и отображение в Mini App. Затем восстановите резервную копию на отдельном сервере и повторите сверку. Ошибка может скрываться не в красивом интерфейсе, а в потерянной связи между партией начисления и возвратом.
Следите за количеством необработанных событий, расхождениями баланса с журналом и длительностью резервов. Эти показатели полезнее общего счётчика открытий приложения: посещаемость радует маркетолога, а корректный остаток позволяет кассиру спокойно закончить смену.
Где разместить бонусный сервис
Для пилота с PostgreSQL, API и небольшим обработчиком событий можно начать с Uab Start: 2 vCPU, 4 ГБ памяти и 80 ГБ диска. Для одновременной работы с каталогом, заказами и импортом стоит проверить Heka Standard: 4 vCPU и 8 ГБ памяти. Это начальные конфигурации для испытаний, а не обещание определённого количества покупателей.
На VPS MAATRIX приложение устанавливается самостоятельно. Используйте поддерживаемую проектом ОС, закройте базу от внешнего доступа, настройте резервирование и ограничьте доступ сотрудников. Выбирайте локацию с учётом размещаемых данных и интеграций магазина.
Бонусная карта может выглядеть очень просто: число, история и одна понятная кнопка. Если за этим стоят ясные правила и проверяемые операции, простота становится достоинством. Покупателю не нужен учебник бухгалтерии в телефоне — ему нужно доверять тому, что показано на экране.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США и РФ. Оплата картой РФ и по СБП.
АРЕНДОВАТЬ СЕРВЕРОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →