DEV Community

liveavabot
liveavabot

Posted on

Telegram Stars: Zero-Friction Micro-Payments for Utility Bots

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
    )
Enter fullscreen mode Exit fullscreen mode

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)"
        )
Enter fullscreen mode Exit fullscreen mode

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)