How to Accept Payments in a Telegram Bot via Payme and Click
Practice4 minInnovialabs editorial team
In short
Either connect Payme or Click as a payment provider in BotFather and send invoices right in the chat, or send a payment link and confirm the order from the gateway callback; in both cases an order counts as paid only after server-side confirmation.
The short answer
There are two ways to accept payments in a bot through Uzbek payment gateways:
- Native Telegram invoice. You connect Payme or Click as a payment provider in BotFather, the bot sends an invoice, and the customer pays without leaving the chat.
- Payment link (redirect). The bot creates an order on your side, builds a link to the Payme or Click payment page and waits for the gateway to call your server back.
One important restriction: digital goods and services inside Telegram (subscriptions, content access, in-game items) must be sold for Telegram Stars under Telegram's rules. Payme and Click fit physical goods, delivery, service bookings and other offline scenarios.
Comparing the two approaches
| Criterion | Telegram invoice | Payment link |
|---|---|---|
| Where the customer pays | Inside Telegram | On the gateway's page |
| What you connect | A provider in BotFather | A merchant account and the gateway API |
| Who confirms payment | Telegram sends successful_payment | The gateway calls your server |
| Flexibility | Limited to the invoice format | Full control over logic and receipts |
| Effort | Lower | Higher: you need a callback endpoint |
The invoice is smoother for customers and faster to launch. Redirect makes sense when payments already work on your website, when the site and the bot should share one order flow, or when the invoice format does not fit.
Option 1: invoice via a provider
- In BotFather, open your bot, go to Payments, choose Payme or Click and complete the setup. Start in test mode.
- Get the provider token and keep it in environment variables, not in code.
- Send the invoice with
sendInvoice. The amount goes in the smallest currency unit: forUZSthat means tiyin, so multiply the sum amount by 100. - Answer the
pre_checkout_query. This is your last chance to decline: out of stock, price changed, order cancelled. Answer quickly, or Telegram cancels the payment. - Wait for the message with
successful_paymentand only then mark the order as paid.
A minimal aiogram 3 example:
from aiogram import Bot, F, Router
from aiogram.types import LabeledPrice, Message, PreCheckoutQuery
router = Router()
@router.message(F.text == "/buy")
async def buy(message: Message, bot: Bot):
await bot.send_invoice(
chat_id=message.chat.id,
title="Order #1024",
description="Delivery within Tashkent",
payload="order:1024",
provider_token=PROVIDER_TOKEN,
currency="UZS",
prices=[LabeledPrice(label="Item", amount=15000000)], # 150,000 UZS
)
@router.pre_checkout_query()
async def pre_checkout(query: PreCheckoutQuery):
ok = await order_is_valid(query.invoice_payload, query.total_amount)
await query.answer(ok=ok, error_message=None if ok else "This order is no longer available")
@router.message(F.successful_payment)
async def paid(message: Message):
sp = message.successful_payment
await mark_paid(sp.invoice_payload, sp.provider_payment_charge_id)
Put your database order ID in payload, not product details: you will use it to find the order at every later step.
Option 2: payment link
- The bot creates an order in your database with the status "awaiting payment".
- The server builds a payment page link with the order ID and amount following the gateway's rules and sends it as a button.
- The customer pays on the Payme or Click side.
- The gateway calls your server: Payme uses its Merchant API with methods such as
CheckPerformTransaction,CreateTransactionandPerformTransaction, while Click sends Prepare and Complete requests. - The server verifies the request signature or authorization, the amount and the order status, then the bot tells the customer the payment went through.
Watch the amount units: gateways differ (tiyin or sum), so check their documentation.
Confirming the order: what to check
- Amount and currency in the confirmation match the order in your database.
- Idempotency: a repeated callback or message never creates a second payment. Store the transaction ID and check it.
- Order status changes only on the server. A customer's "I paid" button is not proof of payment.
- Cancellations and refunds are handled: Payme has a dedicated method for cancelling a transaction.
- Manager notification goes out after confirmation, not after the customer taps "Pay".
Common mistakes
- Passing sums instead of tiyin to
sendInvoice, so the customer sees an invoice a hundred times smaller. - Slow logic in the
pre_checkout_queryhandler, causing payments to time out. - Selling digital access via Payme or Click inside Telegram, which breaks platform rules.
- A test token in production, or a live token committed to the repository.
More detail is in the Telegram payments documentation.