Telegram Bots

Accepting Payments in a Telegram Bot

How payments work in Telegram bots — Telegram Stars for digital goods, provider payments for physical goods, the invoice flow, and hosting it so no payment is lost.

On this page
  1. Two kinds of payments
  2. The payment flow
  3. A Stars payment with aiogram 3
  4. Make payments impossible to lose
  5. Refunds and support
  6. Subscriptions
  7. Security checklist
  8. Summary

Telegram has a built-in payments system, so a bot can sell things without sending users to an external website. Premium features, digital downloads, subscriptions, physical products — all can be paid for in the chat. This article explains the options, walks through the payment flow, and covers what reliable hosting means when money is involved.

Payment rules change over time, so treat this as an orientation and check Telegram’s current Bot Payments documentation before launching.

Two kinds of payments

Telegram Stars for digital goods and services

For digital goods and services sold inside Telegram — premium features, in-bot currency, digital content, access to private channels — Telegram requires payment in Telegram Stars, its in-app currency (currency code XTR). Users buy Stars through Telegram, and your bot receives them. This rule exists largely because of app store policies on digital purchases.

Stars have practical advantages: no payment provider to sign up with, no card details to handle, and a consistent flow across every platform.

Payment providers for physical goods

For physical goods and real-world services — merchandise, deliveries, bookings — bots can use third-party payment providers connected through BotFather (Bot Settings → Payments). Each provider gives you a provider_token, and prices are set in ordinary currencies. Availability depends on your country and the provider.

The payment flow

Whichever method you use, the flow has the same steps:

  1. Your bot sends an invoice — sendInvoice, or a shareable link from createInvoiceLink.
  2. The user taps Pay and confirms in Telegram’s payment interface.
  3. Telegram sends a pre_checkout_query. Your bot must answer it — ok=true to proceed, or ok=false with an error message — within 10 seconds. This is your last chance to check stock, validate the order or reject it.
  4. Telegram completes the payment and sends your bot a message containing successful_payment.
  5. Your bot delivers the item and confirms.

A Stars payment with aiogram 3

from aiogram import F, Router
from aiogram.filters import Command
from aiogram.types import LabeledPrice, Message, PreCheckoutQuery

router = Router()


@router.message(Command("premium"))
async def send_invoice(message: Message):
    await message.answer_invoice(
        title="Premium — 30 days",
        description="Unlocks all premium commands for 30 days.",
        payload=f"premium30:{message.from_user.id}",
        currency="XTR",
        prices=[LabeledPrice(label="Premium (30 days)", amount=100)],  # 100 Stars
    )


@router.pre_checkout_query()
async def on_pre_checkout(query: PreCheckoutQuery):
    # Validate quickly: is the payload one we issued? Is the product available?
    ok = query.invoice_payload.startswith("premium30:")
    await query.answer(ok=ok, error_message=None if ok else "This offer is no longer available.")


@router.message(F.successful_payment)
async def on_paid(message: Message):
    sp = message.successful_payment
    await record_payment(                      # write to the database FIRST
        user_id=message.from_user.id,
        charge_id=sp.telegram_payment_charge_id,
        payload=sp.invoice_payload,
        amount=sp.total_amount,
        currency=sp.currency,
    )
    await grant_premium(message.from_user.id, days=30)
    await message.answer("Thank you! Premium is active for 30 days.")

For Stars, prices are whole numbers of Stars and there’s a single price item. For provider payments, amount is in the currency’s smallest unit (for example, paise or cents), and you pass the provider_token.

Make payments impossible to lose

When a user has paid, delivering what they bought is non-negotiable. Design for failure:

Persist before you do anything else

Write the payment to your database — with telegram_payment_charge_id — as the very first step in the successful_payment handler, before granting anything or replying. If the bot crashes after that write, you can finish delivery on restart. If it crashes before, Telegram’s update is still pending and will be redelivered.

Make delivery idempotent

Updates can be delivered more than once, for example after a webhook retry. Put a unique constraint on the charge ID so a duplicate insert fails, and skip delivery if the payment is already recorded:

CREATE TABLE payments (
  charge_id   TEXT PRIMARY KEY,
  user_id     BIGINT NOT NULL,
  payload     TEXT NOT NULL,
  amount      INTEGER NOT NULL,
  currency    TEXT NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  delivered   BOOLEAN NOT NULL DEFAULT FALSE
);

On startup, find payments where delivered is false and complete them.

Answer pre-checkout quickly

You have 10 seconds. Don’t call slow external APIs in the pre-checkout handler; check local data and answer. A slow answer means a failed payment and a confused user.

Keep the bot online

A bot that’s offline when users try to pay loses sales, and one that restarts mid-flow creates support tickets. Host payment bots on a platform with persistent processes, automatic restarts and backups. On Kerit Cloud, the Starter Telegram plan and up include daily backups; Pro adds PostgreSQL and one-click restore; Ultra adds a 99.99% uptime target.

Refunds and support

Telegram expects bots that take payments to support their buyers. Provide clear terms and a way to get help — Telegram’s payment guidelines ask bots to handle support commands such as /paysupport. For Stars payments, the Bot API provides refundStarPayment, which takes the user ID and the telegram_payment_charge_id — another reason to store it. For provider payments, refunds go through the provider.

Log every payment event (invoice sent, pre-checkout answered, payment received, delivery completed) with the charge ID, so you can trace any complaint end to end.

Subscriptions

Telegram has added subscription support for Stars, letting users pay a recurring amount for access to a bot’s premium features or a private channel. The mechanics — invoice links with a subscription period, renewal notifications and cancellations — are described in the Bot API documentation. Whatever you use, store each subscription’s status and expiry in your database and check it on every premium action rather than trusting in-memory flags.

Security checklist

  • Never trust the client for prices or products. Build invoices from your own catalogue, and verify the payload in pre-checkout.
  • Sign or randomise payloads if they identify orders, so they can’t be guessed or reused.
  • Keep provider tokens in environment variables, like your bot token.
  • Don’t log card or personal details. Telegram handles payment data; you should only ever store charge IDs and order details.
  • Restrict admin tools that grant items or issue refunds to specific user IDs.

Summary

Telegram bots sell digital goods with Telegram Stars and physical goods through connected payment providers. The flow is always invoice → pre-checkout (answer within 10 seconds) → successful_payment → delivery. Record each payment with its charge ID before doing anything else, make delivery idempotent, finish undelivered orders on startup, support refunds and help requests, and host the bot somewhere with persistent processes and backups.