RU EN

Предоплата в Telegram-боте записи: как не продать один слот дважды

У записи с предоплатой есть неудобный промежуток. Клиент выбрал мастера и время, бот выставил счёт, и дальше несколько минут ничего не происходит: человек ищет карту, отвлёкся, передумал. Всё это время слот висит в непонятном состоянии. Если считать его занятым, салон теряет окно, когда клиент так и не заплатил. Если считать свободным, тот же слот может купить кто-то ещё, и тогда у вас две оплаты на одно кресло.

Мы собрали демо-бот записи для вымышленного салона и на нём разобрали этот промежуток до конца. Ниже рассказываем, как он устроен, что нашлось на проверке и что из этого стоит спросить у любого подрядчика, который делает вам бота с оплатой. Бот живой, его можно потрогать: @cp_demo_booking_bot. Оплата в нём работает в тестовом режиме, деньги не списываются, об этом бот предупреждает в приветствии и в самом счёте.

Что видит клиент

Путь короткий: услуга, мастер, день, время, имя и контакт. Расписание нарезано на получасовые ячейки с 10:00 до 20:00. Стрижка на час занимает две соседние ячейки, окрашивание на два часа занимает четыре, поэтому бот показывает только те варианты, где рядом есть достаточно свободного места.

После контакта приходит счёт на предоплату в размере 30% от цены услуги. Для стрижки за 1 500 ₽ это 450 ₽. Когда оплата прошла, клиент получает карточку записи с кнопками «Отменить» и «Перенести», а за сутки и за два часа до визита бот присылает напоминание.

Отменить или перенести запись можно не позднее чем за 12 часов до начала. Позже бот вежливо отказывает, и время остаётся за салоном. Срок задаётся в настройках, в демо стоит именно 12 часов.

Что происходит с ячейкой

Внутри у каждой получасовой ячейки одно из четырёх состояний: свободна, удержана, занята или закрыта администратором. Когда клиент доходит до счёта, нужные ячейки переходят в состояние «удержана» на 15 минут. В это время никто другой их не увидит. Если оплаты не было, удержание истекает, фоновая задача возвращает ячейки в свободные, а незавершённая запись отменяется.

Главный вопрос здесь не «как удержать», а «что будет, если два человека нажали на одно и то же время в одну секунду». Проверка в духе «посмотрели, свободно ли, и если да, то заняли» не спасает: между «посмотрели» и «заняли» успевает пройти второй запрос. В демо захват сделан одним условным обновлением в базе: ячейки переводятся в «удержана» только при условии, что они всё ещё свободны, и всё это внутри одной транзакции. Если обновилось меньше строк, чем нужно услуге, транзакция откатывается, и клиент видит, что время уже занято. База данных у демо самая обычная, SQLite, и этого достаточно: гарантию даёт не размер базы, а то, что проверка и захват происходят одним действием.

На этот сценарий есть отдельный тест: два потока одновременно бронируют один слот, и успех должен быть ровно у одного.

Оплата приходит не тогда, когда удобно

Платёж в Telegram проходит в два шага. Сначала приходит предварительный запрос, на который бот обязан ответить «да» или «нет», и только потом сам платёж. На первом шаге демо сверяет три вещи: сумма совпадает с 30% от цены, валюта та, что ожидается, и ячейки всё ещё удержаны именно этой записью.

Первую версию мы вернули на доработку как раз на этом месте. Удержание истекает через 15 минут, но освобождала его фоновая задача, которая проходит раз в 30 секунд. Если клиент нажал «оплатить» на шестнадцатой минуте, до прохода этой задачи, бот ещё считал ячейки удержанными и отвечал «да». Исправление простое: сверять срок удержания с текущим временем прямо в момент запроса, а не полагаться на то, что фон успел прибраться. Теперь в таком случае клиент получает ответ «слот больше не удержан».

Вторая находка серьёзнее. Что если деньги всё же пришли, а записи уже нет: удержание сняли, или за это время ячейку взял и оплатил другой человек? В первой версии бот в этой ситуации всё равно писал «Оплата прошла, вы записаны». В демо это ничего не стоит, но у реального салона это оплата без записи и без следа в базе, и выяснится она, когда клиент придёт к занятому креслу.

Сейчас такой платёж записывается с пометкой «требует возврата», вместе с суммой и идентификатором операции. Клиенту бот честно сообщает, что слот ушёл и предоплата будет возвращена, без кнопок отмены и переноса. Администраторам уходит сообщение о том, что нужен возврат, с номером брони и суммой. Это не красивый сценарий, но он единственный честный, и именно его стоит показывать владельцу салона до запуска.

Мелочь, которая вылезла на живом прогоне

Когда бот уже работал, в 15:40 по Москве мы записались на сегодня на 10:00. Бот отдавал все свободные ячейки дня и не смотрел на часы. Починили в трёх местах: в списке времени показываются только будущие ячейки, кнопка с прошедшим временем из старого сообщения отвечает «Это время уже прошло», а день, в котором будущих окон не осталось, в выборе дня не появляется. Та же защита стоит и на ручной записи администратором.

Отдельно пришлось доработать тест. Первый вариант проверял слот на два часа в прошлом, и ошибка «показывать всё, что было меньше часа назад» через него проходила. Теперь тест держит границу: ячейка 30 минут назад не показывается, ячейка через 30 минут показывается.

Напоминания, которые переживают перезапуск

Напоминания за сутки и за два часа хранятся в базе как конкретное время, а не как «задача в памяти процесса». Если бот перезапустили, после старта он смотрит, какие напоминания просрочены, и досылает их. У каждого напоминания есть флаг «отправлено», так что дважды одно и то же не уйдёт. Отмена записи снимает её напоминания.

Для демо есть ускоренный режим: напоминания приходят через одну и две минуты после оплаты, а окно отмены сжимается до минуты. Так можно за несколько минут показать всю цепочку: запись, оплата, оба напоминания и отказ в отмене, когда срок вышел.

Что есть у администратора

Четыре команды: расписание на день, ручная запись без оплаты, закрытие ячейки и выгрузка записей за период в CSV. Для демо этого хватает; у реального салона список обычно растёт после первой недели работы, и это нормально.

Как это проверялось

В демо 23 теста. Они гоняют реальные обработчики бота на обычной SQLite во временной папке, без сети: платёжные события подаются как поддельные обновления от Telegram. Отдельно есть 11 «порч»: каждая намеренно ломает одну защиту, например снимает проверку срока удержания или выключает ветку с возвратом, и проверяет, что хотя бы один тест на это покраснел. Если защиту можно убрать, а тесты остаются зелёными, значит, тесты её не проверяют. Так и нашлась дыра в тесте на прошедшие слоты, о которой написано выше.

Что спросить у подрядчика

  • Что происходит со слотом между выбором времени и оплатой, и сколько он удерживается.
  • Что увидят два клиента, которые нажали на одно и то же время одновременно.
  • Что бот делает, если оплата пришла, а удержание уже истекло или слот забрал другой человек. Где остаётся след об этом платеже и кто узнаёт о возврате.
  • Переживают ли напоминания перезапуск бота.
  • Предлагает ли бот прошедшее время, если открыть вчерашнее сообщение.
  • Есть ли тесты на всё перечисленное, и проверял ли кто-нибудь, что они действительно падают, когда защиту убрать.

Если на первые три вопроса ответ «ну, обычно такого не бывает», предоплату в этот бот лучше пока не подключать.

Нужен такой бот для вашего салона, клиники или студии? Посмотрите страницу услуги или напишите нам, разберём ваш случай.

Нужно похожее?

Сделаем Telegram-бота под вашу задачу: приём заявок и записей, каталог или меню, ответы на частые вопросы, уведомления вам в чат, простая админ-панель для просмотра и выгрузки заявок.

Telegram-бот под ключ — от 25 000 ₽, срок от 10 дней.

← Все статьи

Контакты

Напишите нам — обсудим задачу и предложим план в течение суток.

dmitry.zhdanov@craftpodium.com

Telegram: @signmark