Ошибка оплаты в Telegram-боте может возникнуть на совершенно разных этапах.
У одного пользователя вообще не открывается счёт.
У другого счёт появляется, но Telegram не даёт завершить оплату.
У третьего деньги списываются, но бот не выдаёт товар или не меняет статус заказа.
Это три разные проблемы.
Поэтому начинать диагностику нужно не с замены платёжного провайдера или переписывания всего payment-кода, а с определения:
на каком именно этапе ломается платёжная цепочка.
Упрощённо она выглядит так:
создание invoice
↓
пользователь нажимает оплатить
↓
pre_checkout_query
↓
answerPreCheckoutQuery
↓
платёж
↓
successful_payment
↓
выдача товара / изменение заказаTelegram документирует именно такую последовательность для Bot Payments.
Сначала определите, что именно продаёт бот
↑ К оглавлениюЭто сейчас принципиально важно.
Telegram разделяет платежи за:
- цифровые товары и услуги;
- физические товары и услуги.
Для цифровых товаров и услуг внутри Telegram Apps используется Telegram Stars.
В Bot API для таких платежей указывается:
currency = XTRа provider_token передаётся пустым.
Для физических товаров и услуг Telegram предусматривает работу через сторонних платёжных провайдеров, подключаемых к боту.
Поэтому сначала ответьте на вопрос:
платёжная схема вообще соответствует тому, что продаёт бот?
Если цифровую услугу пытаются проводить внутри Telegram по схеме, рассчитанной на внешний платёжный provider, проблема может быть не в обработчике события, а в самой архитектуре оплаты.
Если бот вообще не отправляет счёт
↑ К оглавлениюСоздание стандартного счёта выполняется методом:
sendInvoiceBot API требует для него, среди прочего:
title;description;payload;currency;prices.
Для Telegram Stars используется XTR, а список prices для Stars должен содержать ровно одну позицию.
Если ошибка возникает уже на sendInvoice, сохраните ответ Bot API целиком.
Не ограничивайтесь сообщением приложения:
payment errorНужно видеть реальный ответ Telegram и параметры, которые были отправлены.
Проверьте provider_token
↑ К оглавлениюДля Telegram Stars:
provider_token = ""То есть отдельный токен платёжного провайдера для цифровых товаров и услуг не нужен.
Для платежей через стороннего провайдера, используемых для физических товаров и услуг, токен получается через BotFather и передаётся в платёжном запросе.
Поэтому ошибка в этой части часто сводится к неправильному сочетанию:
тип товара
+
currency
+
provider_tokenНе копируйте payment-конфигурацию из старого проекта, не проверив, для какого типа товара она создавалась.
Проверьте валюту
↑ К оглавлениюДля Telegram Stars используется:
XTRОбычные валюты в Bot API обозначаются ISO-кодами вроде:
USD
EURНо для цифровых товаров и услуг внутри Telegram требуется именно Stars.
Если код одновременно предполагает:
currency = XTRи обработку обычного карточного провайдера, сначала проверьте логику формирования invoice.
Проверьте сумму
↑ К оглавлениюВ обычных валютных платежах amount передаётся целым числом в минимальных единицах валюты.
Например, для валюты с двумя знаками после запятой сумма:
1.45передаётся как:
145Bot API отдельно указывает, что здесь используется integer, а не float или double.
Поэтому ошибка вида:
цена в базе = 990но один участок приложения считает её рублями, а другой копейками,
может привести к неправильной сумме invoice или к тому, что последующая проверка заказа не совпадёт с полученным total_amount.
Не доверяйте цене только из Telegram update
↑ К оглавлениюКогда приходит платёжное событие, связывайте его с внутренним заказом.
Для этого в invoice существует:
payloadTelegram возвращает его далее как:
invoice_payloadв PreCheckoutQuery и SuccessfulPayment.
Хорошая схема:
создали заказ WF-123
↓
сформировали payload
↓
отправили invoice
↓
получили pre_checkout_query
↓
нашли WF-123 в своей БД
↓
сверили ожидаемую сумму и состояние заказаНе используйте текст сообщения или имя пользователя как единственный идентификатор заказа.
Если пользователь видит оплату, но она не завершается
↑ К оглавлениюСледующий критический этап:
pre_checkout_queryTelegram отправляет его перед окончательным проведением оплаты.
Бот обязан ответить методом:
answerPreCheckoutQueryпричём Bot API должен получить ответ в течение 10 секунд.
Если ответа нет вовремя, checkout отменяется.
Поэтому если пользователь говорит:
> нажимаю оплатить, жду, потом ошибка
обязательно измерьте именно участок:
pre_checkout_query received
→
answerPreCheckoutQuery sentПроверьте, получает ли бот pre_checkout_query
↑ К оглавлениюДобавьте журналирование входящих Update.
Нужно увидеть:
pre_checkout_query.id
invoice_payload
currency
total_amount
received_atPreCheckoutQuery действительно содержит эти значения.
Если такого update вообще нет, проблема находится раньше:
- маршрутизация webhook;
- получение updates;
- фильтрация событий;
- обработчик библиотеки;
- инфраструктура между Telegram и приложением.
Нет смысла оптимизировать answerPreCheckoutQuery, пока ваш код даже не получает соответствующее событие.
Проверьте маршрутизацию payment updates
↑ К оглавлениюИногда бот отлично принимает обычные сообщения и кнопки:
message
callback_queryно код просто не обрабатывает:
pre_checkout_queryОсобенно это встречается в приложениях, где каждый тип Update отправляется в отдельный handler.
Логируйте тип update до того, как приложение распределит его между обработчиками.
Так быстро становится видно:
Telegram прислал платёжное событие или код потерял его внутри собственной маршрутизации.
Не выполняйте долгие операции до ответа Telegram
↑ К оглавлениюПлохая схема:
pre_checkout_query
↓
CRM
↓
внешний API
↓
долгий SQL
↓
создание документа
↓
email
↓
answerPreCheckoutQueryУ бота всего десять секунд на pre-checkout ответ.
До answerPreCheckoutQuery должны выполняться только проверки, действительно необходимые для решения:
можно принимать этот заказ или нельзя.
Отправку писем, генерацию файлов, уведомления менеджеру и другие необязательные действия лучше не помещать в критический десятисекундный участок.
Если заказ нельзя оплатить — ответьте явно
↑ К оглавлениюanswerPreCheckoutQuery принимает:
ok = trueкогда заказ можно продолжать,
или:
ok = falseесли есть проблема.
При ok = false передаётся человекочитаемый:
error_messageкоторый Telegram показывает пользователю.
Например, если товар уже недоступен, лучше явно отклонить checkout, чем просто не отвечать и заставлять пользователя ждать timeout.
Проверьте внешний API
↑ К оглавлениюЕсли перед подтверждением заказа бот обращается к:
- CRM;
- ERP;
- складу;
- собственному backend;
- системе лицензий;
- удалённой БД;
- внешнему API;
измерьте каждый такой запрос.
Например:
pre_checkout received 12:00:00.100
CRM started 12:00:00.200
CRM finished 12:00:08.900
DB finished 12:00:10.100
answer sent 12:00:10.300После такого лога уже не нужно гадать, почему пользователь видит ошибку оплаты.
Ответ ушёл слишком поздно.
Не путайте подтверждение checkout с успешной оплатой
↑ К оглавлениюЭто одна из самых важных ошибок в реализации Telegram Payments.
Успешный:
answerPreCheckoutQuery(ok=true)не означает, что деньги уже успешно получены.
После завершения платежа Telegram присылает:
successful_paymentИ именно после него нужно выполнять выдачу цифрового товара или услуги.
Правильная последовательность:
pre_checkout_query
↓
answerPreCheckoutQuery(ok=true)
↓
оплата
↓
successful_payment
↓
заказ = paid
↓
выдачаЕсли деньги списались, а бот ничего не выдал
↑ К оглавлениюТогда sendInvoice и checkout уже могут быть полностью исправны.
Проверяйте обработку:
successful_paymentОбъект содержит в том числе:
currency;total_amount;invoice_payload;telegram_payment_charge_id;- идентификатор операции провайдера, когда применимо.
Проверьте:
- пришёл ли
successful_payment; - попал ли он в нужный handler;
- найден ли заказ по
invoice_payload; - изменился ли статус заказа;
- сохранился ли идентификатор платежа;
- выполнилась ли выдача.
Если деньги приняты, но обработчик события упал после этого, пользователь воспринимает ситуацию как «ошибка оплаты», хотя сам платёж уже произошёл.
Не создавайте заказ повторно при каждом update
↑ К оглавлениюPayment flow содержит несколько событий.
Поэтому внутреннюю логику лучше связывать с одним уже созданным заказом, а не создавать новую запись на:
invoice
pre_checkout
successful_paymentИначе при повторной обработке update могут появиться:
- дубли заказов;
- повторная выдача;
- повторные уведомления;
- неправильный статус.
Для успешного платежа Telegram предоставляет telegram_payment_charge_id; его стоит сохранять вместе с результатом транзакции.
Если используется webhook
↑ К оглавлениюПроверьте полный путь:
Telegram
↓
HTTPS
↓
reverse proxy
↓
приложение
↓
Telegram update router
↓
payment handlerПроблема может находиться ещё до payment-кода.
Например:
- приложение перегружено;
- webhook ждёт свободный worker;
- proxy долго устанавливает соединение с backend;
- приложение запускается после простоя;
- очередь запросов выросла.
Для платежей особенно важно знать фактическое время получения pre_checkout_query, потому что дальше действует жёсткое десятисекундное ограничение Telegram.
Если используется getUpdates
↑ К оглавлениюПлатёжные события также приходят внутри обычных Update.
Если бот обрабатывает updates последовательно, длинный предыдущий handler может задержать payment update.
Например:
долгий импорт
↓
долгая команда пользователя
↓
pre_checkout_query ждёт своей очередиВ таком случае сам payment handler может выполняться за 100 миллисекунд, но стартовать уже слишком поздно.
Измеряйте:
update received
handler started
handler finishedотдельно.
Если ошибка возникает только иногда
↑ К оглавлениюПлавающая ошибка оплаты особенно полезна для диагностики.
Если большая часть платежей проходит, а некоторые заканчиваются ошибкой, сравните успешные и неуспешные операции по времени.
Частые причины такого поведения:
- нестабильный внешний сервис;
- блокировка БД;
- очередь workers;
- высокая нагрузка;
- сетевой timeout;
- редкий медленный сценарий заказа.
Если код всегда неправильный, ошибка чаще будет воспроизводиться постоянно.
Если она появляется периодически, ищите нестабильную зависимость или задержку.
Для цифровых товаров проверьте Telegram Stars
↑ К оглавлениюЕсли бот продаёт внутри Telegram:
- цифровой файл;
- подписку на цифровую функцию;
- доступ;
- цифровую услугу;
- виртуальный продукт,
актуальная схема Telegram предусматривает оплату в Stars с:
currency = XTRи без токена внешнего платёжного provider.
Если проект был написан до перехода на текущую модель цифровых платежей или скопирован из старого примера, этот участок нужно проверить особенно внимательно.
Для физических товаров проверьте подключённого провайдера
↑ К оглавлениюДля физических товаров и услуг Telegram использует отдельный Payment API со сторонними провайдерами.
Провайдер подключается через BotFather, после чего его token используется при создании invoice.
Если ошибка находится уже после передачи данных провайдеру, нужно разделить:
ошибка Telegram Bot APIи:
отказ / ошибка платёжного providerЭто разные системы и разные журналы.
Не стоит менять webhook Telegram, если фактический отказ пришёл от платёжного сервиса.
Что логировать
↑ К оглавлениюДля каждого заказа полезно иметь один связанный trace:
order_id
invoice_payload
invoice_created_at
pre_checkout_query_id
pre_checkout_received_at
pre_checkout_answered_at
pre_checkout_result
successful_payment_received_at
telegram_payment_charge_id
order_statusТогда сообщение клиента:
> оплата не прошла
можно превратить в точный диагноз:
invoice отправлен
pre_checkout получен
answer ушёл за 350 мс
successful_payment не пришёлили:
pre_checkout пришёл
CRM отвечала 12 секунд
Telegram отменил checkoutЭто две совершенно разные неисправности.
Короткий порядок диагностики
↑ К оглавлениюЕсли появилась ошибка оплаты в Telegram-боте, проверяйте так:
- определить, цифровой это товар или физический;
- проверить используемую платёжную схему;
- сохранить ответ
sendInvoice; - проверить
currency,pricesиprovider_token; - проверить связь invoice с внутренним заказом через payload;
- убедиться, что приходит
pre_checkout_query; - проверить маршрутизацию payment update;
- измерить время до
answerPreCheckoutQuery; - уложиться в 10 секунд;
- проверить внешние API и БД;
- после подтверждения checkout дождаться
successful_payment; - только после этого менять заказ на оплаченный и выполнять выдачу;
- сохранить идентификатор платежа;
- повторно пройти один тестовый платёж полностью.
Чего не стоит делать
↑ К оглавлениюПри ошибке оплаты Telegram-бота не стоит сразу:
- менять платёжного провайдера;
- переписывать весь бот;
- считать любой timeout ошибкой Telegram;
- выдавать товар после одного
answerPreCheckoutQuery(ok=true); - игнорировать
successful_payment; - выполнять длинную бизнес-логику до ответа pre-checkout;
- смешивать платежи Stars и внешнего провайдера;
- менять несколько частей payment flow одновременно;
- тестировать без журналирования этапов.
Сначала найдите последний успешно пройденный этап платежа.
Следующий этап после него обычно и показывает, где находится неисправность.
Частые вопросы
Почему не проходит оплата в Telegram-боте?
Причина зависит от этапа: ошибка может возникать при создании invoice, при проверке pre_checkout_query, у платёжного провайдера или уже при обработке successful_payment. Сначала нужно определить последний успешно выполненный шаг.
Сколько времени бот может обрабатывать pre_checkout_query?
Telegram Bot API требует ответить через answerPreCheckoutQuery в течение 10 секунд. Если Bot API не получает ответ вовремя, checkout отменяется.
Нужно ли использовать Telegram Stars?
Для продажи цифровых товаров и услуг внутри Telegram Apps используется Telegram Stars с валютой XTR. Для физических товаров и услуг Telegram предоставляет отдельный сценарий со сторонними платёжными провайдерами.
Когда можно считать заказ действительно оплаченным?
После получения события successful_payment. Успешный ответ на pre_checkout_query только разрешает продолжить checkout и сам по себе не подтверждает завершённый платёж.
--------------------------------------------------
Нужна помощь с оплатой в Telegram-боте?
Поможем найти причину ошибки в платёжной цепочке Telegram-бота и восстановить корректную обработку платежей.