Where to Buy Telegram Stars: Prices and a Python Guide to Fragment

—

от автора

I wanted to buy Telegram Stars for less, so I compared the same packages on three websites. SMMESE had the lowest listed price of the three: $15.31 for 1,000 Stars, compared with $16.50 on TgSuperstars and $16.67 on MyStars.tg.

I then traced how a service finds the recipient, pays from its own wallet, and confirms the order. The Python implementation below can become the part of your own Stars website that fulfills purchases.

Where to buy Telegram Stars and what they cost

Listed prices in USD, checked on October 16, 2026:

Website

100 Stars

500 Stars

1,000 Stars

SMMESE

$1.54

$7.66

$15.31

TgSuperstars

$1.65

$8.25

$16.50

MyStars.tg

$1.68

$8.34

$16.67

For SMMESE, I selected Cryptocurrency; the calculator marked the total as fee included. TgSuperstars accepts USDT or GRAM through its TON-wallet payment option, and other cryptocurrencies through Heleket. MyStars.tg lists GRAM and USDT payments. Both ask for the recipient’s Telegram username and the number of Stars.

These are the prices displayed before payment. Network or exchange withdrawal fees can add to the amount spent. MyStars.tg also states that paying through Cryptomus adds a separate processor fee, so compare the final checkout total as well as the listed price.

What a Stars seller does after payment

On sites such as SMMESE, the buyer can specify their own or another recipient’s Telegram username. The implementation I examined buys Stars through Fragment using the service’s wallet. The recipient gets the Stars; the service pays for the purchase. That separates the customer’s payment from the transaction used to fulfill the order.

A storefront built this way needs to cover the Fragment purchase, currency conversion, payment-processing costs, and its margin. Its order handler also needs to distinguish an accepted payment from a completed purchase: a timeout can leave the outcome unknown even after a transaction has reached the network.

Automating Telegram Stars purchases with Python

fragment.py calls Fragment’s internal web methods from an authenticated browser session. worker.py checks the quote, calls the wallet signer, and tracks the order. These excerpts use the project’s shared models, settings, store, and wallet module.

1. Connect the browser session and verify the wallet

Playwright connects to an already running Chromium instance. The code extracts the /api?hash=… endpoint from the Fragment page and makes requests inside the browser with its cookies.

It checks that the authorized wallet matches the service’s signing wallet. If necessary, checkTonProofAuth proves ownership before the purchase request is created.

2. Find the recipient

Stars purchases use three methods:

"stars": (    "searchStarsRecipient",    "initBuyStarsRequest",    "getBuyStarsLink",)

searchStarsRecipient takes the Telegram username and returns an internal recipient value, plus the display name and avatar when available. Show that profile to the buyer so they can check the destination. The next request needs the returned recipient, not an arbitrary Telegram user ID.

3. Create the purchase request

Inside Fragment.quote(), _lookup() finds the recipient and initBuyStarsRequest creates the request:

page = await self._page(gift.product)await self._own_session(page)session = await self._session(page)_lookup_method, init_method, link_method = METHODS[gift.product]token = (await self._lookup(page, gift))["recipient"]init_params = {    "recipient": token,    "payment_method": (        "usdt_ton" if gift.currency == "USDT" else "ton"    ),}field = "quantity" if gift.product == "stars" else "months"init_params[field] = str(getattr(gift, field))initial = await self._rpc(page, init_method, init_params)identifier = request_id(initial)

gift holds the purchase parameters. Stars use quantity; the same module supports Premium through months. payment_method selects ton or usdt_ton. The response supplies the request ID; no payment has been sent yet.

4. Get and validate the payment quote

getBuyStarsLink receives the request ID, wallet information, and the sender-visibility setting:

link = await self._rpc(    page,    link_method,    {        "id": identifier,        "transaction": "1",        "show_sender": "1" if gift.show_sender else "0",        "account": json.dumps(            self.signer.account(), separators=(",", ":")        ),        "device": json.dumps(DEVICE, separators=(",", ":")),    },)if link.get("ok") is False:    raise ServiceError(        "fragment_invalid_quote",        "Fragment did not return a payment quote",    )if await self._session(page) != session:    raise ServiceError(        "fragment_session_changed",        "The Fragment account changed during quoting",    )quote = validate_transaction(    link,    self.signer.address,    self.settings,    currency=gift.currency,)

The response contains the destination, amount, expiry, and payment payload. The session check catches an account change during quoting; validate_transaction() checks the proposed transaction against the configured wallet and rules.

For TON payments, validation checks the allowed destination and message structure. USDT transfers have separate token checks. The method also saves Fragment’s confirm_method and confirm_params for confirmation after payment.

5. Check the spending limit, sign, and save before sending

The purchase price may change between checkout and fulfillment. The worker rejects a price above the order’s limit:

price = (    quote["token_amount_units"]    if request.currency == "USDT"    else quote["amount_nanotons"])if price > request.max_units:    raise ServiceError(        "price_limit_exceeded",        "Fragment price exceeds the order limit",        409,    )

It checks the TON balance, including a fee reserve, and leaves enough time before the quote expires. USDT orders also require a separate token-balance check.

state = await self.chain.state(self.signer.address)required_ton = (    quote["amount_nanotons"]    + self.settings.fee_reserve_nanotons)if state.balance < required_ton:    raise ServiceError(        "insufficient_balance",        "Insufficient TON balance including the network fee reserve",        409,    )if (    quote["valid_until"]    < int(time.time()) + self.settings.min_validity_seconds):    raise ServiceError(        "quote_expired",        "The payment quote expired before signing",        409,    )

The signer prepares the transaction using the wallet’s current state:

signed = await self.signer.sign(    quote,    state.seqno,    state.state,)

Before broadcasting, the worker records the signed BOC—the serialized message—along with the quote and order state:

order = self.store.transition(    identifier,    ("preparing",),    "signed",    quote=quote,    **asdict(signed),    before_lt=state.last_transaction_lt,    currency=request.currency,    **prices,    fee_reserve_ton=ton_string(        self.settings.fee_reserve_nanotons    ),    broadcast_attempts=0,)

prices contains the cost fields prepared earlier. Only after the store write succeeds does the worker broadcast:

await self.broadcast(order)

Saving first lets the worker continue with the same signed payment after an interruption.

6. Confirm the existing payment and complete the order

If broadcasting times out, reconcile() checks the existing payment through the blockchain module. Under the permitted retry conditions, the worker rebroadcasts the saved BOC instead of signing a new purchase.

Once the blockchain check reports success, the order becomes paid. The worker then calls Fragment.confirm() with the saved quote and signed BOC:

result = await self.fragment.confirm(    order["detail"]["quote"],    order["detail"]["boc"],)if result.get("confirmed"):    self.store.transition(        order["id"],        ("paid",),        "completed",        delivery_id=result["delivery_id"],        error_code=None,        message="Fragment confirmed the purchase",    )

Fragment.confirm() checks for ok: true in Fragment’s response. A successful confirmation moves the order to completed; confirmation errors leave it paid and awaiting completion. A paid order must not start another purchase just because confirmation is delayed.

Connecting the purchase handler to your own website

The storefront needs a recipient-and-quantity form, customer payment processing, and an order queue. After the server verifies the customer’s payment with the payment provider, it can queue the purchase for this handler.

Keep the customer payment reference, recipient, Fragment request, signed BOC, and status associated with the same order. This gives the worker enough information to resume checks after a failure. A redirect to a successful-payment page alone is not proof that the customer paid.

ссылка на оригинал статьи https://habr.com/ru/articles/1095242/