Шесть рабочих дней — это не рекорд и не маркетинг. Это срок, который получается, когда объём известен заранее, а решения по деньгам приняты до старта. Ниже — разбор того, из чего эти шесть дней складываются, на примере предзаказа для сети кофеен.
День ноль: решения, которые нельзя отложить
До кода нужно закрыть три вопроса. Каждый из них при переносе «на потом» стоит дней ожидания.
Кто принимает деньги. Если платёж идёт на счёт клиента напрямую, нужен его договор с эквайрингом и его же ключи. Подключение ЮKassa занимает от одного дня до недели, и эта неделя проходит на стороне банка. Начинать разработку, не запустив эту процедуру, означает упереться в неё на пятый день.
Как выдаётся чек. 54-ФЗ требует фискализации, и это не то, что дописывается в конце. Либо чек формирует платёжный провайдер, либо у клиента есть облачная касса, либо нужен отдельный сервис. Выбор влияет на структуру заказа в базе.
Что происходит при отказе. Возврат, частичный возврат, отменённый заказ, двойное списание. Логика возвратов — это половина кода платёжного модуля, и проектировать её нужно сразу вместе с основным потоком.
Дни 1–2: витрина
Mini App — это веб-приложение внутри мессенджера. «Бот с кнопками» тут ни при чём, и отсюда следуют две вещи. Первая: у вас полноценный интерфейс, поэтому каталог с модификаторами не превращается в двадцать сообщений подряд. Вторая: это обычный фронтенд, который надо адаптировать под маленький экран и тему, которую пользователь выбрал в Telegram.
Данные о пользователе приходят в initData — подписанной строке, которую
обязательно проверять на сервере. Это первое место, где ломаются боты: параметры
берут из запроса как есть и получают возможность оформить заказ от чужого имени.
import { createHmac } from 'node:crypto'
/** Проверка подписи initData. Без неё любой параметр можно подделать. */
export function verifyInitData(initData: string, botToken: string): boolean {
const params = new URLSearchParams(initData)
const hash = params.get('hash')
if (!hash) return false
params.delete('hash')
const dataCheckString = [...params.entries()]
.map(([key, value]) => `${key}=${value}`)
.sort()
.join('\n')
const secret = createHmac('sha256', 'WebAppData').update(botToken).digest()
const computed = createHmac('sha256', secret).update(dataCheckString).digest('hex')
return computed === hash
}День 3: оплата
Схема простая, но у неё есть один неочевидный узел. Клиент нажимает «оплатить»,
приложение создаёт заказ в статусе pending и получает от провайдера ссылку.
Дальше пользователь уходит на страницу банка — и может не вернуться. Закрыл
вкладку, потерял сеть, ушёл в другое приложение.
Поэтому источник правды о статусе оплаты — вебхук от провайдера. Редирект
пользователя источником правды не является. Редирект показывает экран «спасибо», вебхук переводит заказ в
paid. Если полагаться только на редирект, часть оплаченных заказов останется
висеть в pending, а часть неоплаченных уйдёт в работу.
Вебхук должен быть идемпотентным: провайдер имеет право прислать одно и то же уведомление дважды. Ключ идемпотентности — идентификатор платежа, а не идентификатор заказа.
День 4: экран исполнителя
Гость видит статус, но кто-то должен нажать «готово». Для кофейни это планшет у стойки: очередь заказов, время в очереди, одна большая кнопка. Никакой авторизации по паролю — планшет один и стоит за стойкой, поэтому доступ по одноразовой ссылке с длинным токеном.
Здесь же появляется первое требование к производительности, о котором обычно забывают: экран должен обновляться сам. На десятках заказов в день опрос раз в пять секунд надёжнее вебсокета. Меньше кода, меньше состояний, переживает разрыв связи без переподключения.
День 5: отчёты и мелочи
Ночная выгрузка продаж в таблицу управляющего. Уведомление гостю, когда заказ готов. Обработка ситуации, когда гость не пришёл. Отдельный текст для случая, когда точка закрыта.
Мелочи — это половина времени. И это нормально: продукт отличается от прототипа именно ими.
День 6: пилот на одной точке
Не на всей сети. Одна точка, один день, живой бариста рядом. За этот день обычно всплывает две-три формулировки, которые непонятны гостю, и один сценарий, который никто не предусмотрел — например, заказ на вынос на троих с разными временами готовности.
Что реально может сдвинуть срок
- Эквайринг. Ждать подключения — самая частая причина. Начинайте первым делом.
- Контент. Меню с описаниями, фото, модификаторами. Если его нет, разработка упирается в ожидание.
- Один ответственный. Не комитет. Чаще всего проект тормозят согласования, а не код.
Шесть дней — это когда всё вышеперечисленное готово. Если нет — считайте честно: срок разработки плюс срок ожидания. Мы стараемся называть оба.