Ошибка оплаты в Telegram-боте: как найти причину и восстановить платежи

Разбираем, на каком этапе платёжной цепочки возникает ошибка в Telegram-боте и что проверить до и после checkout.

Ошибка оплаты в 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, проблема может быть не в обработчике события, а в самой архитектуре оплаты.

Если бот вообще не отправляет счёт

↑ К оглавлению

Создание стандартного счёта выполняется методом:

sendInvoice

Bot 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

передаётся как:

145

Bot API отдельно указывает, что здесь используется integer, а не float или double.

Поэтому ошибка вида:

цена в базе = 990

но один участок приложения считает её рублями, а другой копейками,

может привести к неправильной сумме invoice или к тому, что последующая проверка заказа не совпадёт с полученным total_amount.

Не доверяйте цене только из Telegram update

↑ К оглавлению

Когда приходит платёжное событие, связывайте его с внутренним заказом.

Для этого в invoice существует:

payload

Telegram возвращает его далее как:

invoice_payload

в PreCheckoutQuery и SuccessfulPayment.

Хорошая схема:

создали заказ WF-123
↓
сформировали payload
↓
отправили invoice
↓
получили pre_checkout_query
↓
нашли WF-123 в своей БД
↓
сверили ожидаемую сумму и состояние заказа

Не используйте текст сообщения или имя пользователя как единственный идентификатор заказа.

Если пользователь видит оплату, но она не завершается

↑ К оглавлению

Следующий критический этап:

pre_checkout_query

Telegram отправляет его перед окончательным проведением оплаты.

Бот обязан ответить методом:

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_at

PreCheckoutQuery действительно содержит эти значения.

Если такого 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;
  • идентификатор операции провайдера, когда применимо.

Проверьте:

  1. пришёл ли successful_payment;
  2. попал ли он в нужный handler;
  3. найден ли заказ по invoice_payload;
  4. изменился ли статус заказа;
  5. сохранился ли идентификатор платежа;
  6. выполнилась ли выдача.

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

Не создавайте заказ повторно при каждом 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-боте, проверяйте так:

  1. определить, цифровой это товар или физический;
  2. проверить используемую платёжную схему;
  3. сохранить ответ sendInvoice;
  4. проверить currency, prices и provider_token;
  5. проверить связь invoice с внутренним заказом через payload;
  6. убедиться, что приходит pre_checkout_query;
  7. проверить маршрутизацию payment update;
  8. измерить время до answerPreCheckoutQuery;
  9. уложиться в 10 секунд;
  10. проверить внешние API и БД;
  11. после подтверждения checkout дождаться successful_payment;
  12. только после этого менять заказ на оплаченный и выполнять выдачу;
  13. сохранить идентификатор платежа;
  14. повторно пройти один тестовый платёж полностью.

Чего не стоит делать

↑ К оглавлению

При ошибке оплаты 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-бота и восстановить корректную обработку платежей.

↑ К оглавлению