Решение RollyPay

Платёжная система для Telegram-бота

Бот показывает кнопку, backend создаёт платёж и получает подписанное событие, покупатель получает товар через пару секунд после списания. Полтора дня работы разработчика — и бот продаёт сам.

Обсудить мой сценарий

Оплата в Telegram-боте устроена так: бот не принимает деньги сам, а просит ваш backend создать платёж и присылает пользователю кнопку со ссылкой. Человек платит через СБП или картой, ваш сервер получает подписанное событие и в ту же секунду выдаёт то, что купили, — файл, ключ, инвайт в закрытый канал.

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

Stars или внешняя оплата: что вам подойдёт

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

Внешняя оплата остаётся штатным путём там, где товар живёт за пределами Telegram: физическая доставка, вебинар на своей платформе, лицензия к десктопной программе, услуга офлайн, аккаунт в вашем SaaS. Разбор пограничных случаев — в материале Stars или внешняя оплата.

Определитесь с этим до вёрстки меню бота. Переезд между Stars и внешней оплатой задним числом означает переделку каталога, кнопок и логики выдачи.

Из чего собирается оплата в боте

Частей ровно три, и путать их зоны ответственности не стоит.

ЧастьЗа что отвечаетЧего никогда не делает
БотПоказывает каталог, собирает выбор, отправляет кнопку с оплатой, доставляет товарНе хранит секреты и не решает, оплачен ли заказ
BackendСчитает цену, создаёт платёж, принимает колбэк, ведёт заказыНе доверяет данным, пришедшим из сообщения пользователя
Платёжный сервисПоказывает форму оплаты, проводит деньги, присылает событиеНе знает вашей бизнес-логики и не выдаёт товар за вас

Ошибка новичков — держать всю логику в обработчике callback_query. Пока товаров пять, это работает; на тридцати позициях и промокодах бот превращается в клубок, где цена вычисляется в трёх местах по-разному.

Путь покупателя: от команды до выдачи

  1. Пользователь жмёт «Купить». Бот отвечает карточкой товара и кнопкой подтверждения — на этом шаге ничего в базе ещё не меняется.
  2. Backend создаёт заказ. Строка в orders: telegram user id, артикул, цена, status = pending, номер вида tg-4711.
  3. Backend создаёт платёж. В ответе приходит pay_url, бот отправляет его кнопкой вида «Оплатить 990 ₽».
  4. Человек платит. Ссылка открывается в браузере телефона, дальше — приложение банка и возврат обратно в чат.
  5. Приходит колбэк. Backend меняет статус и просит бота отправить товар.

Между шагами 4 и 5 пользователь обязательно вернётся в чат раньше, чем придёт событие. Поставьте туда кнопку «Проверить оплату», которая читает статус из вашей базы: это снимает половину обращений в поддержку.

Код: создание платежа и обработка события

Платёж создаётся одним запросом. Заголовок X-API-Key — ключ кассы, X-Nonce — уникальный UUID на каждый запрос, он живёт 10 минут; при повторе того же значения вернётся 401 nonce already used.

POST /api/v1/payments
X-API-Key: <ключ кассы>
X-Nonce: 8b0d4f31-2c77-4b5f-a1de-6c9e30f47aa2

{
  "amount": "990.00",
  "payment_currency": "RUB",
  "order_id": "tg-4711",
  "description": "Набор пресетов Winter",
  "customer_id": "tg:582931044",
  "metadata": {"chat_id": 582931044, "sku": "preset-winter"}
}

Поле metadata вернётся в колбэке без изменений — кладите туда chat_id, чтобы не искать покупателя по сумме и времени. Колбэк приходит на ваш callback_url с заголовком X-Signature: это HMAC-SHA256 hex от строки X-Timestamp + "." + сырое тело запроса на ключе signing_secret. Считайте подпись до разбора JSON и сравнивайте constant-time. События бывают трёх типов: payment.paid, payment.canceled, payment.expired.

Схема оплаты в Telegram-боте: заказ на backend, ссылка в чате, подписанное событие и выдача товара
Схема. Бот только показывает кнопку, а решение о выдаче принимает backend после проверки подписи.

Выдача доступа: инвайт, ключ, файл

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

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

Подписки требуют отдельного поля с датой окончания. Продление — это новый платёж с новым order_id, а не редактирование старого; иначе история оплат перестанет сходиться уже на третьем месяце. Механика разобрана в статье об оплате подписки в боте.

Что ломается на сотом платеже

  • Двойная выдача. Колбэк доставлен дважды, бот отправил два ключа. Лечится проверкой текущего статуса заказа перед выдачей.
  • Два платежа на один заказ. Пользователь нажал кнопку трижды. Идемпотентность создания платежа работает по order_id — не генерируйте новый номер на каждый клик.
  • Просроченные ссылки. У платежа есть expires_at; человек открыл кнопку через сутки и увидел ошибку. Дайте команду «Оплатить снова», которая создаёт свежий платёж.
  • Секреты в коде бота. Ключ кассы и signing_secret живут в переменных окружения, а исходники ботов слишком часто уезжают в публичные репозитории.
  • Нет журнала событий. Без таблицы входящих колбэков спор «деньги списались, товара нет» разбирается вручную и долго.

Как проверить бота до первых клиентов

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

Дальше проверьте неприятные ветки. Закройте страницу оплаты, не заплатив, и убедитесь, что заказ остался в pending, а бот не отправил ничего лишнего. Отправьте себе тот же колбэк дважды и посмотрите, придёт ли товар два раза. Наконец, дождитесь expires_at и проверьте, что старая кнопка не оживает.

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

Сколько времени займёт запуск

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

Подготовьте заранее описание того, что продаёте, ссылку на бота и правила возврата. Обязанности продавца платёжный сервис не забирает: чек, налог и общение с покупателем остаются на вас. Самозанятый формирует чек в приложении «Мой налог» и держит в уме годовой лимит 2,4 млн ₽, ИП работает со своей кассой. Если хочется сначала проверить спрос, стартуйте с ссылок на оплату и добавьте API, когда пойдут первые продажи.

Частые вопросы

Когда в боте обязательны Telegram Stars, а когда можно ставить внешнюю ссылку?

Stars нужны, если товар — это цифровое содержимое внутри Telegram: доступ к каналу, платные материалы, функции самого бота. Внешняя оплата уместна там, где товар живёт вне мессенджера: доставка, обучение на своей платформе, лицензия к программе, услуга офлайн. Проверьте свой случай до вёрстки меню, потому что переезд между схемами задним числом дорогой.

Может ли бот работать без отдельного сервера?

Для приёма колбэков нужен адрес, доступный из интернета, — это может быть небольшой контейнер или serverless-функция, полноценная машина не обязательна. Сам бот при этом остаётся тонким: он показывает кнопки и доставляет товар. Решение об оплате всегда принимает backend, потому что только он видит подпись события.

Как не выдать товар дважды при повторном колбэке?

Найдите заказ по своему order_id и меняйте статус только из состояния pending. Все повторные события для уже оплаченного заказа просто подтверждайте, ничего не отправляя. Отправку файла или инвайта уносите в очередь, чтобы ошибка Telegram не заставила сервис перепосылать событие.

Что делать, если пользователь оплатил, но вернулся в чат раньше уведомления?

Добавьте кнопку «Проверить оплату», которая читает статус заказа из вашей базы. Обычно событие приходит за 1–3 секунды, поэтому вторая попытка уже показывает результат. Так вы снимаете большую часть обращений в поддержку без ручного разбора.

Кто отвечает за чек и возврат покупателю бота?

Продавец. Самозанятый выбивает чек в приложении «Мой налог» и следит за годовым лимитом 2,4 млн ₽, ИП и компании применяют собственную кассу. Платёжный сервис даёт техническое подключение и сообщает результат платежа, а условия возврата вы описываете сами и публикуете в боте.