Хотел купить звезды Telegram подешевле - в итоге разобрался, как самому их продавать.
Открыл поиск, а там звездами торгуют все подряд. Даже МТС. Покупка одна и та же, цены разные - стало интересно, сколько можно сэкономить и что вообще делает продавец после того, как получает деньги.
Так обычный поиск дешевых Stars превратился в разбор всей цепочки: от ввода имени получателя до платежа и подтверждения покупки. В примерах кода ниже можно увидеть, как эти действия автоматизированы.
Начну с цен: сравню МТС, SMMESE и KUPIKOD на одинаковом количестве звезд, чтобы определить самый дешевый вариант среди них. Затем покажу механику покупки через Fragment и разберу код. Если захочется пойти дальше покупки для себя - этот механизм можно взять за основу собственного сайта по продаже звёзд Telegram.
Где купить звезды Telegram дешевле: SMMESE, МТС и KUPIKOD
Для сравнения я выбрал три сервиса и одинаковые пакеты: 100, 500 и 1000 звезд. На каждом сайте установил оплату через СБП, без промокодов.
Вот какие цены отображались 16 октября 2026 года:
Среди трёх сравниваемых сервисов самая низкая показанная цена оказалась у SMMESE - для всех выбранных пакетов. На 1000 звезд разница составила 123,28 руб. относительно МТС и 146,28 руб. относительно KUPIKOD. Это сравнение цен, отображавшихся при выборе оплаты через СБП на дату проверки.
С ценами стало понятнее. Теперь интереснее другое: что происходит между нажатием «Оплатить» и появлением звезд у получателя? Чтобы разобраться, дальше покажу цепочку покупки через Fragment на конкретной реализации.
Почему звезды Telegram стоят по-разному и что происходит после оплаты
На этом можно было закончить: выбрать цену и купить. Но меня зацепило другое - что именно делает продавец между моей оплатой и появлением звезд в Telegram?
Я разобрал конкретную реализацию покупки через Fragment. Здесь сервис находит получателя по имени пользователя, создает заявку на нужное количество Stars и оплачивает её своим кошельком. Получатель звезд и владелец кошелька, из которого уходят деньги, - разные участники этой операции.
Так становятся понятны две разные суммы: цена для покупателя на сайте и стоимость исполнения заявки через Fragment.
Для расчета розничной цены такого сервиса нужно учитывать закупочную стоимость звезд, конвертацию валют, расходы на проведение платежей и наценку. Если покупатель платит рублями, а заявка оплачивается в TON или USDT, имеет значение и курс, по которому сервис получает эту валюту.
От оплаты до завершенного заказа
У собственного магазина две отдельные задачи: принять деньги от покупателя и выполнить покупку звезд для указанного получателя. В рассматриваемом коде показана вторая часть.
Сначала сервис получает от Fragment параметры заявки: сколько заплатить, куда отправить платёж и какие данные передать вместе с ним. Затем проверяет сумму, срок действия заявки и баланс кошелька. Только после этого подписывает и отправляет транзакцию.
Но отправка платежа ещё не завершает заказ. Например, соединение может оборваться уже после того, как сеть приняла транзакцию. Если в этот момент просто начать покупку заново, появляется риск заплатить повторно.
Поэтому обработчик сначала выясняет судьбу уже отправленного платежа. После подтверждения в блокчейне заказ получает статус paid. Затем сервис передаёт подтверждение Fragment и ждёт положительного ответа — только после него устанавливается completed.
Именно эту последовательность дальше разберу по шагам: от браузерной сессии и поиска получателя до подписания платежа и подтверждения покупки.
Как устроена покупка звезд Telegram через Fragment
В этой реализации покупку выполняют два модуля. fragment.py общается с Fragment: находит получателя, создаёт заявку и получает параметры платежа. worker.py проверяет стоимость, вызывает подписание транзакции и ведёт заказ до подтверждения.
Я разделил процесс на шесть этапов.
1. Подключение к Fragment и проверка кошелька
Скрипт через Playwright подключается к уже работающему Chromium и использует его браузерную сессию. Из страницы Fragment извлекается адрес внутренних запросов вида /api?hash=…. Запросы выполняются внутри браузера с его cookies.
Затем код проверяет, какой кошелёк авторизован на странице. Если он не совпадает с кошельком, которым сервис собирается оплачивать покупки, выполняется подтверждение владения через checkTonProofAuth.
Это важная связка: заявку нужно создавать и оплачивать в контексте нужного кошелька.
2. Поиск получателя по username
В searchStarsRecipient передаётся имя пользователя Telegram. В ответ код получает сведения о найденном аккаунте: имя, данные аватара, если он есть, и служебное значение recipient.
Именно recipient используется при создании заявки. Это внутренний идентификатор получателя в ответе Fragment; подставлять вместо него произвольный Telegram ID нельзя.
Имя и аватар позволяют показать покупателю, какой аккаунт найден, ещё до оплаты.
3. Создание заявки на покупку Stars
Следующий запрос — initBuyStarsRequest. В него передаются три основных параметра:
Параметр |
Что передается |
recipient |
Получатель из предыдущего ответа |
quantity |
Количество звезд |
payment_method |
ton или usdt_ton |
Из ответа извлекается идентификатор заявки. На этом этапе транзакция кошельком ещё не отправляется: сначала нужно получить условия платежа и проверить их.
4. Получение параметров платежа
С идентификатором заявки код вызывает getBuyStarsLink. Вместе с ним передаются сведения о кошельке, устройстве и настройка отображения отправителя.
Ответ содержит параметры транзакции: адрес назначения, сумму, срок действия и payload — данные, которые должны уйти вместе с платежом. Код сохраняет полученный payload для последующего подписания.
Здесь же обрабатываются confirm_method и confirm_params: операция и параметры, которыми Fragment предлагает подтвердить покупку после отправки платежа. Название этой операции берётся из ответа.
5. Проверка и подписание
Перед оплатой обработчик проверяет, укладывается ли цена в установленный для заказа предел, хватает ли средств и не истекла ли заявка.
Для TON дополнительно проверяются адрес назначения по разрешённому списку, структура платёжного сообщения и соответствие кошелька плательщика. Для USDT предусмотрена отдельная проверка перевода токенов.
Затем вызывается подписание. Полученный BOC - сериализованное подписанное сообщение - передаётся в хранилище вместе с данными заказа до отправки в сеть. Это позволяет после сбоя продолжить работу с уже подготовленным платежом.
6. Подтверждение покупки
После отправки обработчик обращается к модулю проверки блокчейна. Успешный результат переводит заказ в paid.
Далее Fragment получает подписанный BOC и сохранённые параметры подтверждения. Если ответ содержит ok: true, заказ переходит в completed.
Если подтверждение Fragment пока не получено, оплаченный заказ сохраняется в paid. Для него повторяется проверка завершения покупки, а новая оплата не создаётся.
Таким образом, у заказа сохраняется последовательная связь: получатель - заявка Fragment - подписанный платёж - подтверждение покупки. Именно эту связь должен сохранять обработчик, даже если браузер перезапустился или один из запросов завершился таймаутом.
Автоматизация покупки звезд Telegram на Python
Теперь перейду к коду. В этой реализации работу с Fragment выполняет класс Fragment, а оплату и обработку состояний заказа — Worker.
Ниже — фрагменты существующих методов. Они используют общие модели, настройки, хранилище и модуль кошелька проекта.
Получаю получателя и создаю заявку
Для Stars в коде задана последовательность из трёх методов:
"stars": ( "searchStarsRecipient", "initBuyStarsRequest", "getBuyStarsLink", )
Внутри Fragment.quote() сначала открывается страница нужного продукта и проверяется авторизация кошелька. Затем _lookup() выполняет поиск получателя, а его recipient передаётся в заявку:
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 содержит параметры покупки. Для звезд используется quantity; поддержка Premium в том же модуле переключает запрос на months.
Функция request_id() извлекает идентификатор заявки из ответа Fragment. Именно он понадобится следующему запросу.
Запрашиваю платёж для созданной заявки
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, )
Здесь проверяется, что Fragment вернул предложение платежа и браузерная сессия не изменилась во время подготовки заявки. После этого ответ проходит через validate_transaction().
В полном методе дальше выполняются дополнительные проверки USDT, если выбрана эта валюта, и сохраняются параметры последующего подтверждения. Полученный quote передаётся обработчику заказа.
Ограничиваю сумму, которую разрешено потратить
Между показом цены покупателю и исполнением заказа может пройти время. Поэтому обработчик сравнивает полученную стоимость с установленным пределом:
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, )
Далее проверяются баланс и срок действия заявки. Для USDT отдельно проверяется баланс токенов; TON требуется также для выполнения транзакции.
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, )
В этом фрагменте я вынес расчёт необходимого баланса в required_ton для читаемости; в исходнике используется то же выражение непосредственно в условии.
Сохраняю подписанный платёж перед отправкой
После проверок обработчик вызывает модуль подписи:
signed = await self.signer.sign( quote, state.seqno, state.state, )
Затем результат записывается через Store вместе с параметрами заявки:
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 — подготовленные выше поля стоимости заказа. После успешной записи вызывается:
await self.broadcast(order)
Отправкой занимается отдельный метод. При таймауте он сохраняет неопределённый результат, после чего reconcile() проверяет платёж через модуль блокчейна. При допустимых условиях повторно отправляется сохранённый BOC.
Завершаю уже оплаченный заказ
Когда проверка платежа перевела заказ в paid, обработчик вызывает подтверждение Fragment:
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", )
Это успешная ветка метода delivery(). В полном обработчике ошибки подтверждения оставляют заказ оплаченным и ожидающим завершения.
Для собственного сайта этот механизм становится исполнителем заказов. Интерфейс, приём оплаты покупателя и постановка заказа в очередь подключаются отдельно. Проверка оплаты должна выполняться на сервере по подтверждению платёжного провайдера - одного возвращения покупателя на страницу успешной оплаты недостаточно.