When I added Stars support to @liveavabot, I expected the usual friction. Card forms, KYC prompts, redirect to Stripe, payment failures. What I got instead was a payment flow that feels like clicking a reaction.
Here's what I learned building it, including the actual aiogram 3 code.
The Problem With Micropayments for Utility Bots
@liveavabot does one thing: convert videos and GIFs to Telegram's video avatar format (800x800, H.264, max 10 seconds, max 2MB, no audio). It's a tool, not a SaaS. Average order value is low. Users are price-sensitive.
Any friction in the checkout kills conversion. I've tested this with other payment setups: a card form alone cuts the funnel by more than half. An external redirect loses more on top of that. Stars removes both.
What Telegram Stars Actually Is
Stars is Telegram's native virtual currency. Users buy Stars with fiat (card, Google Pay, Apple Pay) through the Telegram app itself, then spend them inside bots. From a developer perspective, receiving Stars is just handling a webhook event.
The Stars ecosystem has been growing since Telegram introduced it in 2024. More users already have Stars in their wallet than you'd expect, because many have bought them for sticker packs or channel subscriptions. A user who already has Stars treats your payment as a one-tap transfer, not a purchase decision. That changes the psychology.
The user-facing flow: tap "Buy Pack 20", Telegram shows a native payment sheet (Google Pay, Apple Pay, or Stars wallet), one confirmation tap, credits appear in the bot. There's no card form on your side, no redirect to an external site, and no KYC for small amounts. Under 30 seconds total.
Implementing Stars in aiogram 3
You create an invoice, Telegram handles the payment UI, then you handle the pre_checkout_query and successful_payment events.
Creating an Invoice
from aiogram.types import LabeledPrice, Message
async def cmd_buy(message: Message):
await message.bot.send_invoice(
chat_id=message.chat.id,
title="20 conversions",
description="Convert 20 videos or GIFs to Telegram avatar format",
payload="pack_20",
currency="XTR", # XTR is the currency code for Telegram Stars
prices=[LabeledPrice(label="20 conversions", amount=50)], # 50 Stars
# No provider_token needed for Stars
)
Key detail: currency="XTR" and no provider_token. Telegram routes the payment through its own wallet. No payment gateway to configure.
Handling the Payment
from aiogram import Router, F
from aiogram.types import PreCheckoutQuery, Message
router = Router()
@router.pre_checkout_query()
async def pre_checkout(query: PreCheckoutQuery):
# Must answer within 10 seconds or Telegram cancels the transaction
# Don't put heavy DB calls here
await query.answer(ok=True)
@router.message(F.successful_payment)
async def payment_confirmed(message: Message):
payload = message.successful_payment.invoice_payload
stars_paid = message.successful_payment.total_amount
if payload == "pack_20":
await add_credits(message.from_user.id, 20)
await message.answer(
f"Got it. 20 conversions added. ({stars_paid} Stars charged)"
)
The 10-second deadline on pre_checkout_query is real. I got bitten by this in testing when I added a DB lookup inside the handler. Answer first, do heavy work in successful_payment. Also: validate the payload in successful_payment, not pre_checkout_query. Returning ok=False in pre-checkout shows a generic Telegram error to the user. Better to accept, then handle unexpected payloads gracefully in the confirmation handler.
The NATIVE Partnership Program
Telegram's NATIVE Partnership pays 20% of referred Stars spend back to the developer for 6 months after each user's first Stars payment in your bot. Enrollment is through BotFather and takes about 2 minutes. For a utility bot with steady daily use, this compounds even at small scale. Worth setting up before you launch Stars, so you don't leave early users uncounted.
Lessons From 478 Users
@liveavabot currently has 478 users. Stars revenue is proportional to that scale, so I can't report large numbers. What I can say:
The funnel from "sees buy button" to "completes payment" is the shortest I've built. Users in India going through Google Pay have zero friction points I control. If they don't buy, it's a pricing issue, not a UX issue. That's a useful signal to isolate.
A few specific things that caught me:
Refunds are manual. No API to refund Stars programmatically yet. When a user hits a bug and loses a credit, I credit the account directly and issue a manual Stars refund via BotFather.
Invoice amount is in whole Stars. No fractional prices. I went with 50 Stars for 20 conversions, roughly $0.05 per conversion at current exchange rates.
Test mode uses a separate BotFather test bot. To test without spending real Stars, create a test bot via BotFather's test environment. The Telegram docs describe this but it's easy to miss on first read.
Stars don't expire. Users can hold credits indefinitely. Not a problem at my scale, but worth noting if you're building something with financial weight on either side.
What's Next
I want to add a smaller pack (5 Stars for 2 conversions) for users who want to try before committing to a larger pack. I'm also adding a credits command so users can check their balance without doing a conversion first.
The current pricing is rough. I'll adjust after a month of real data.
Try it at https://t.me/LiveAvaBot?start=devto_article_20261028 if you want to see the Stars flow live.
Built by me: @liveavabot is a side project to explore Telegram's monetization stack.
Top comments (0)