Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 20 из 25 ~16 мин +20 XP

Платежи: инвойсы и Telegram Stars

Платежи в telegram-боте на Go начинаются с одного вызова tgbotapi.NewInvoice: бот отправляет счёт, Telegram показывает кнопку «Оплатить», а дальше по цепочке приходят pre-checkout и successful_payment. Этот урок — обзорная карта всей схемы: инвойс с NewLabeledPrice, провайдеры и тестовые платежи, Telegram Stars с валютой XTR — честно без продакшен-глубины, но с точным списком того, куда копать дальше.

Содержание урока · 8 разделов

Деньги в боте — это подписки, разовые товары и донаты, и Bot API умеет всё три: платёжная схема встроена в Telegram давно, а с 2024 года к фиатным провайдерам добавились внутренние звёзды. Хорошая новость архитектурная: платежи живут в тех же апдейтах, что и сообщения, — новых серверов, портов и источников событий не появляется, бот просто обрабатывает ещё два типа обновлений. Схема одинаковая для любых денег: SendInvoice показывает счёт, пользователь жмёт «Оплатить», бот подтверждает pre-checkout, Telegram списывает и шлёт successful_payment. Меняется только источник списания: токен провайдера для реальных валют или звёзды для цифровых товаров. Сразу договоримся о глубине: это обзорная карта — кабинет провайдера, вебхуки, чеки и налоги останутся за кадром, зато весь путь от инвойса до выдачи товара будет виден целиком и каждый шаг будет с кодом.

Инвойс: tgbotapi.NewInvoice и NewLabeledPrice#

Инвойс — это особое сообщение с кнопкой «Оплатить», которое собирает конфигурация InvoiceConfig, а конструктор tgbotapi.NewInvoice заполняет обязательные поля. Их немного, но каждое работает в платёжной логике: title — название товара до 32 символов, description — строка на экране подтверждения, payload — ваш собственный идентификатор заказа, который Telegram хранит как есть и вернёт после оплаты; его длина — до 128 байт, и в него кладут id заказа из базы. Currency — код валюты, а Prices — срез tgbotapi.LabeledPrice, собранный через NewLabeledPrice(label, amount): сумма в минимальных единицах, для рублей и долларов это копейки и центы, для звёзд — целые штук. Провайдерские инвойсы ещё и разбиваются на компоненты — товар, доставка, скидка, — каждая своим LabeledPrice, и итог считается из этой таблицы. ProviderToken и StartParameter замыкают конфигурацию: токен решает, кто списывает деньги, о нём — следующий раздел.

Отправка инвойса

GO
prices := []tgbotapi.LabeledPrice{
        tgbotapi.NewLabeledPrice("Подписка «Про», месяц", 29900),
}
invoice := tgbotapi.NewInvoice(
        chatID,
        "Подписка «Про»",                    // заголовок, до 32 символов
        "Отчёты и экспорт на 30 дней",       // описание на экране оплаты
        "sub-monthly-1024",                  // payload: ваш id заказа
        os.Getenv("PROVIDER_TOKEN"),         // токен провайдера из BotFather
        "sub-monthly",                       // startParameter
        "EUR",                               // валюта
        prices,
)
if _, err := bot.Send(invoice); err != nil {
        log.Println(err)
}

Сумма 29900 при валюте EUR — это 299 евро и 00 центов: LabeledPrice всегда в минимальных единицах, и перепутанная шкала — самая дорогая опечатка в этой теме. Payload sub-monthly-1024 — связка с вашей базой: в successful_payment придёт именно он, по нему выдаётся оплаченный товар. Send уходит обычным методом: инвойс — обычное сообщение с особым содержимым.

Провайдеры и тестовые платежи#

Фиатные деньги в боте идут через платёжного провайдера: Telegram передаёт инвойс подключённой системе, та списывает средства, а бот получает только подтверждение — сами деньги через Telegram не проходят. Токены выдаются в BotFather разделом /payments: поддерживаются Stripe и региональные провайдеры, список с примерами есть в документации Bot API, а выбор обычно диктуется страной и юридическим статусом продавца. Для разработки у большинства провайдеров есть тестовый режим: тестовые токены и тестовые карты дают возможность пройти всю схему — инвойс, pre-checkout, successful_payment — без единой копейки. Проверять удобно в тестовой среде Telegram Desktop, где клиент работает против песочницы. Честно про сложность: реальный фиат тянет за собой кабинет провайдера, вебхуки о статусах, возвраты и договор — это отдельный проект, и обзорный урок сознательно не притворяется, что покрывает его двадцатью строками. Карта пути та же самая, меняется только обвязка вокруг неё.

Telegram Stars: XTR и пустой provider_token#

Звёзды — внутренняя валюта Telegram, появившаяся в 2024 году, и для цифровых товаров в ботах она стала обязательной дорогой: правила магазинов приложений требуют, чтобы цифровой контент продавался через встроенную оплату, и звёзды — это та самая встроенная оплата. Схема для разработчика почти не меняется: в инвойсе ставится Currency XTR, ProviderToken пустая строка, а суммы в Prices считаются целыми звёздами — никакого деления на сотни. Подтверждение и successful_payment те же, возврат делается методом Bot API refundStarPayment, который свежие обёртки библиотеки дают соответствующей конфигурацией. Звёзды же обслуживают периодические подписки ботов и платные медиа — в этом уроке только отметим их существование, а инвойс в звёздах собирается так же, как обычный: те же поля, другая валюта. Для пет-проекта это идеальный вход в платежи: ни договора с провайдером, ни тестовых карт — минимальный инвойс оплачивается парой звёзд.

Pre-checkout и successful_payment

GO
for update := range updates {
        switch {
        case update.PreCheckoutQuery != nil:
                // между нажатием «Оплатить» и списанием: 10 секунд на решение
                ok := tgbotapi.PreCheckoutQueryConfig{
                        PreCheckoutQueryID: update.PreCheckoutQuery.ID,
                        OK:                 true,
                }
                _, _ = bot.Request(ok)

        case update.Message != nil && update.Message.SuccessfulPayment != nil:
                p := update.Message.SuccessfulPayment
                log.Printf("оплачен заказ %s: %d %s, charge %s",
                        p.InvoicePayload, p.TotalAmount, p.Currency,
                        p.TelegramPaymentChargeId)
                grantPurchase(p.InvoicePayload, p.TelegramPaymentChargeId)
        }
}

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

Что приходит после кнопки «Оплатить»#

Весь диалог денег укладывается в два апдейта и один ответ. PreCheckoutQuery несёт ID запроса, валюту, сумму и ваш payload — достаточно данных, чтобы отказать по делу: товар сняли с продажи, лимит исчерпан, пользователь из заблокированной страны. Ответ конфигурацией PreCheckoutQueryConfig с OK true открывает списание; OK false с ErrorMessage до пары десятков слов объясняет причину — этот текст пользователь увидит в диалоге оплаты. Дальше успешная ветка: SuccessfulPayment внутри Message приносит InvoicePayload, TotalAmount, Currency и пару идентификаторов — TelegramPaymentChargeId и ProviderPaymentChargeId. По payload выдаётся товар, charge_id записывается как факт выдачи и страховка от повторов. Для звёзд на этом всё — деньги уже внутри Telegram; для провайдера начинается его половина работы: чеки, статусы и возвраты в его кабинете. Статусы заказов хранятся у себя — хэш в Redis на каждый payload отлично закрывает эту роль.

Обзорная карта платежа#

ЭтапКто инициируетВаш кодРезультат
ИнвойсБотNewInvoice + bot.SendСообщение с кнопкой «Оплатить»
Pre-checkoutTelegramPreCheckoutQueryConfig, OK true/falseРазрешение или вежливый отказ до списания
ОплатаПользовательничего — Telegram списываетsuccessful_payment в Message
ВыдачаБотgrantPurchase по payloadТовар у пользователя, charge_id записан
ВозвратБотrefundStarPayment или кабинет провайдераДеньги вернулись, статус обновлён

Что осталось за кадром — и об этом честно. Реальный фиат: договор с провайдером, KYC, вебхуки о промежуточных статусах, чеки и налоги по юрисдикции — у каждого провайдера своя документация, и объём этой части выше всего курса про ботов. Подписки через звёзды: отдельные методы на выдачу, продление и отмену, плюс служебные апдейты об их изменении. Mini App со своей корзиной: интерфейс покупки переезжает в веб-вью, но бэкенд остаётся ровно этой схемой. Проверка платежа на своей стороне: суммы и валюты стоит сверять с ожидаемыми, payload валидировать — successful_payment верить, но перепроверять. Обзорная карта не делает вас платёжным разработчиком, зато снимает страх перед темой: четыре этапа, два апдейта, один ответ — остальное обвязка вокруг уже знакомого цикла range.

Проверка: инвойс в звёздах

Что нужно поменять в инвойсе, чтобы принимать Telegram Stars?

Проверка: окно pre-checkout

Сколько времени у бота на ответ на pre_checkout_query?

Медленная проверка перед списанием

Прочитайте код и выберите, что он выведет:

case update.PreCheckoutQuery != nil:
        // хендлер проверяет наличие товара в базе
        // и на фоне деплоя база отвечает 15 секунд
        ok := tgbotapi.PreCheckoutQueryConfig{
                PreCheckoutQueryID: update.PreCheckoutQuery.ID,
                OK:                 true,
        }
        _, _ = bot.Request(ok)

Соберите платёжные ветки

Впишите поля апдейтов и подтверждение pre-checkout.

case update. != nil: // запрос перед списанием _, _ = bot.Request(tgbotapi.PreCheckoutQueryConfig{ OK: , }) case update.Message. != nil: // деньги пришли payload := update.Message.SuccessfulPayment. grantPurchase(payload)

Соберите путь платежа

Расставьте строки: от инвойса до выдачи товара.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: платежи

Термин

NewInvoice

нажмите, чтобы перевернуть
Определение

Конструктор InvoiceConfig: chatID, title до 32 символов, description, payload, provider_token, валюта и Prices. Отправляется обычным bot.Send.

1 / 6

Карта собрана — дальше маршрут курса: следующий урок про Mini Apps — веб-интерфейс внутри Telegram, куда переезжает витрина для сложных товаров; урок про ошибки и лимиты — почему массовые инвойсы подчиняются тем же 429, что и рассылки; урок про тесты бота — как покрыть платёжные ветки без Telegram, сохранёнными апдейтами. Откуда платёжная механика растёт: цикл апдейтов и webhook — восемнадцатый урок, деплой на VPS — обязательный шаг, прежде чем принимать деньги: платежи не прощают перезапусков без логов. Хранение статусов заказов в Redis разобрано в уроке про хэши курса go-redis. Полный маршрут — на странице курса.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Платёж — четыре этапа и два апдейта: NewInvoice и Send, pre_checkout_query с ответом за десять секунд, successful_payment, выдача по payload.
  • LabeledPrice считает в минимальных единицах: копейки и центы для валют, целые звёзды для XTR — перепутанная шкала дороже всего остального.
  • Фиат идёт через провайдера: токен из BotFather /payments, тестовый режим для разработки, обвязка провайдера — отдельный проект.
  • Telegram Stars: Currency XTR и пустой provider_token; цифровые товары в ботах продаются через звёзды, возврат — refundStarPayment.
  • TelegramPaymentChargeId — ключ идемпотентности выдачи и возврата; без него повтор доставки апдейта выдаёт товар дважды.
  • Урок обзорный: вебхуки провайдера, чеки, налоги и подписки через звёзды остались картой направлений, а не инструкцией.

Домашнее задание

Соберите платёжный стенд: бот с командой /buy, которая отправляет инвойс в звёздах — Currency XTR, пустой provider_token, цена в одну-две звезды, payload вида test-order-1. Обработайте pre_checkout_query с лёгкой проверкой payload и successful_payment с записью charge_id в Redis-хэш. Оплатите инвойс сами себе и убедитесь, что повторная доставка successful_payment не выдаёт товар второй раз — добавьте идемпотентность по TelegramPaymentChargeId. Затем отправьте себе отказ: OK false с сообщением о снятом с продажи товаре.

Личные заметкиПусто
Хранятся только в вашем браузере (Telegram · Платежи: инвойсы и Telegram Stars)

Вопросы и ответы по уроку

Можно ли принимать рубли и доллары без провайдера?

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

Чем Stars лучше провайдера для пет-проекта?

Меньше обвязки: нет договора, кабинета и вебхуков провайдера, инвойс отличается только валютой XTR и пустым токеном, а вся схема — pre-checkout и successful_payment — та же. Минус тоже честный: звёзды — внутренняя валюта, и вывод заработанного идёт через конвертацию, а пользователи должны сначала звёзды купить.

Что делать, если pre-checkout ответил OK, а бот упал до выдачи товара?

Платёж состоится, successful_payment придёт — возможно, с повторами доставки при webhook. Отсюда идемпотентность: выдача товара привязана к payload, а факт выдачи записывается с charge_id; обработчик, увидев знакомый charge_id, просто подтверждает обработку без повторной выдачи. Деньги не теряются, если выдача вынесена из опасной зоны и основана на апдейте, а не на памяти процесса.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

Prometheus25 уроков~12 чпрактика25 уроков уже доступны

Мониторинг Go с Prometheus: метрики, Grafana, алерты

Курс по метрикам в Go: Counter, Gauge, Histogram, лейблы, PromQL, Grafana-дашборды, алерты и бизнес-метрики. 25 уроков с примерами client_golang.

  • Понимать четыре типа метрик и выбирать правильный
  • Экспортировать метрики из Go через promhttp
  • Инструментировать HTTP-запросы и бизнес-события
  • Писать PromQL-запросы: rate, sum, p99