Skip to content

Repository files navigation

Telegram Stars/Premium Reseller Bot

Async Python 3.11 Telegram bot for reselling Telegram Stars and Telegram Premium with MyStars FaaS fulfillment and TON-network crypto payments.

Stack

  • aiogram v3 with routers, FSM, callback data, middleware, and admin filter.
  • PostgreSQL via SQLAlchemy 2.0 async and Alembic.
  • Redis for FSM, rate limiting, payment monitor locks, and dedupe support.
  • mystars-faas==0.1.3 as the only Stars/Premium fulfillment API.
  • tonutils and pytoniq-core for wallet and jetton transfers.
  • Aiohttp server (/health + MyStars webhook) always on; Telegram via polling (default) or webhook (TELEGRAM_UPDATE_MODE).

SDK Introspection

mystars-faas was installed before feature code and introspected. See docs/sdk_introspection.md.

Important difference: installed AsyncMyStarsClient exposes await_order(...), not wait_for_order(...). The code uses await_order(...).

TON Center API key

Deposits are monitored through the TON Center HTTP API v3 (https://toncenter.com; the app appends /api/v3/...). You need a TON_API_KEY — get one from the @toncenter Telegram bot:

  1. Open https://t.me/toncenter and press Start.
  2. Press Manage API Keys.
  3. Press Manage to choose a plan. The Free tier is ~10 req/sec with 1 token per network; paid tiers (higher rate limits) are billed in GRAM.
  4. Press Create API Key and copy the token.

Then set in .env:

TON_API_ENDPOINTS=https://toncenter.com
TON_API_KEY=<your key>
TON_NETWORK=mainnet

The key is sent as the X-API-Key header on every request. Any additional comma-separated URLs in TON_API_ENDPOINTS are used as fallbacks.

Prerequisites (get these first)

Obtain/prepare each of these and put them in .env:

  1. Docker + Docker Compose, installed and running.
  2. Telegram bot token (BOT_TOKEN) — create a bot with @BotFather (/newbot) and copy the token.
  3. MyStars API key + webhook secret (MYSTARS_API_KEY, MYSTARS_WEBHOOK_SECRET) — from the MyStars bot (@my_stars_tg_bot → "API access").
  4. TON Center API key (TON_API_KEY) — see TON Center API key above.
  5. Operational TON wallet (OPERATIONAL_WALLET_MNEMONIC) — the hot wallet the bot pays suppliers from. Leave it empty to auto-generate one on first boot (the seed is persisted in the DB and the address is printed in the logs); or provide your own 24-word seed (recommended for production / secret managers). Either way you must fund it — see Why fund the operational wallet? below. ⚠️ If you bring your own seed, generate it securely and keep only a small hot balance.
  6. Your Telegram admin id(s) (ADMIN_TELEGRAM_IDS) — your numeric user id (get it from @userinfobot); comma-separated for several. These accounts get the /admin panel, support messages, and low-balance alerts.
  7. (webhook mode only) a public HTTPS PUBLIC_WEBHOOK_URL. The default polling mode needs none.

The stack boots without real values (it fails only at the external-auth boundary), but it can only fulfill real orders once the wallet is funded and the keys are valid.

Why fund the operational wallet?

This is a "service in the middle" reseller: the bot buys Stars/Premium from MyStars and resells at a margin. On every order:

  1. The customer pays your operational wallet (USDT or GRAM) — the customer price.
  2. The bot then creates the MyStars order and pays MyStars from the same wallet (the supplier cost) plus a little TON for gas.
  3. MyStars delivers; your profit is customer price − supplier cost (the margin).

So the wallet needs a working float (USDT/GRAM to front supplier costs) plus TON for gas. An empty wallet can accept customer payments but cannot pay the supplier, so orders sit in paid and never deliver. Keep it topped up — the bot sends low-balance alerts to admins at LOW_BALANCE_TON_THRESHOLD.

Setup

  1. Create .env from .env.example and fill in the values from Prerequisites above (keep the production/mainnet defaults).
  2. Start dependencies:
docker compose up -d postgres redis
  1. Run everything with Docker — the bot service runs alembic upgrade head automatically before starting, so this is one command:
docker compose up --build

By default the bot runs in polling mode (TELEGRAM_UPDATE_MODE=polling) — a valid production setup that needs no public URL and works behind NAT. The aiohttp server (/health + the MyStars webhook) still runs. See Update mode below.

Local dev (app on host, deps in Docker)

Run Postgres + Redis in Docker but the app on the host. Because .env uses the in-network postgres/redis hostnames, override the URLs to localhost when running outside the compose network (note a host Postgres may already occupy localhost:5432):

python -m venv .venv
./.venv/bin/pip install -r requirements.txt
DATABASE_URL=postgresql+asyncpg://stars:stars@localhost:5432/stars ./.venv/bin/alembic upgrade head
DATABASE_URL=postgresql+asyncpg://stars:stars@localhost:5432/stars \
  REDIS_URL=redis://localhost:6379/0 ./.venv/bin/python -m app

Update mode (polling vs webhook)

TELEGRAM_UPDATE_MODE controls how Telegram updates arrive, independent of ENVIRONMENT:

  • polling (default) — the bot pulls updates via long polling. No public URL required; works behind NAT. Recommended for a single-instance deployment.
  • webhook — Telegram pushes updates to PUBLIC_WEBHOOK_URL. Set TELEGRAM_UPDATE_MODE=webhook and a public HTTPS PUBLIC_WEBHOOK_URL (validated at boot). Use behind a reverse proxy or for horizontal scaling.

In both modes the aiohttp server serves /health and the MyStars webhook (MYSTARS_WEBHOOK_PATH), and the payment monitor runs.

MyStars status sync in polling mode: with an empty PUBLIC_WEBHOOK_URL the MyStars order is created without a callback_url, so the supplier cannot push delivery webhooks. A background status-sync loop therefore polls get_order every MYSTARS_STATUS_POLL_INTERVAL_SECONDS (default 15) for orders in fulfilling, flips them to their terminal status (delivered/failed/reversed/expired), and notifies the customer. The loop runs in webhook mode too, as a backstop for missed webhooks.

Payment rails (USDT vs GRAM)

  • USDT is a jetton on TON (6 decimals) — deposits are matched from TON Center /jetton/transfers.
  • GRAM is the native coin (the native token was renamed to GRAM; 9 decimals) — deposits are plain coin transfers matched from TON Center /transactions (op-0, memo in the comment). GRAM is not a jetton, so it never appears in the jetton feed and is monitored separately.

Each order's payment QR and "Open in wallet" button encode the correct asset + amount + memo automatically (native GRAM omits the jetton parameter).

Flow

  1. Customer selects Stars quantity or Premium duration.
  2. Customer selects USDT or GRAM rail.
  3. Bot validates recipient with AsyncMyStarsClient.check_recipient(...).
  4. Bot quotes base cost with get_pricing(...), applies admin margin with apply_retail_markup(...), and creates a local order with a UUID memo.
  5. Customer pays the operational wallet with the exact asset (USDT jetton or native GRAM), amount, and memo.
  6. Payment monitor polls TON Center v3 for both USDT jetton transfers and native GRAM transactions, validates asset + amount + memo, and dedupes transaction events.
  7. After payment, bot creates the MyStars order with the local order ID as idempotency key.
  8. Bot pays the MyStars invoice from the operational wallet using tonutils.
  9. The background status-sync loop (and, in webhook mode, the signed MyStars webhook) updates the order to its terminal status, edits the customer's order card in place, and sends a short notification message. /reconcile and the reconcile worker remain as manual backstops.

Admin commands

Restricted to ADMIN_TELEGRAM_IDS (put your real Telegram user id there).

  • /admin — inline panel (Orders, Stats, Balance, 📈 Margin, Reconcile).
  • /margin [pct] — show or set the global margin at runtime (persisted in the DB, overrides the DEFAULT_MARGIN_PCT seed). No restart needed.
  • /product_margin <product_id> <pct> — per-product margin override; /pricing lists products and effective margins.
  • /orders [status], /stats — order list / revenue + profit, grouped per pay currency (USDT and GRAM are different units and are never summed together).
  • /balance — operational wallet address + native gas balance.
  • /reconcile — pull terminal MyStars statuses for missed webhooks.
  • /fulfill <order_id> — manually deliver an order stuck in paid (e.g. credited while AUTO_FULFILL_PAID_ORDERS was off).
  • /refund <order_id> <destination>, /cancel_order <order_id>.
  • /broadcast <text> — message all users.
  • /reply <user_id> <text> — answer a customer's support message.

Support relay: customers use /support (or an order's 💬 Support button) to write a message; the bot relays it (as a copy) to all admins, who answer with /reply <user_id> <text>. SUPPORT_URL is optional and only adds an external contact link.

Mainnet Go-Live Checklist

  • Use a small hot wallet balance; keep treasury funds in cold storage.
  • Confirm TON_NETWORK=mainnet, TON_API_ENDPOINTS=https://toncenter.com, and a valid TON_API_KEY.
  • Verify OPERATIONAL_WALLET_ADDRESS matches the mnemonic-derived address.
  • Polling is the default and needs no public URL. Only for TELEGRAM_UPDATE_MODE=webhook: set a public HTTPS PUBLIC_WEBHOOK_URL and a TELEGRAM_WEBHOOK_SECRET_TOKEN.
  • Put your real admin id(s) in ADMIN_TELEGRAM_IDS (admin panel, support, alerts).
  • Store .env in a secret manager, not in the image or repository.
  • Confirm MyStars API key and webhook secret rotation string.
  • Run alembic upgrade head.
  • Place a low-value test order for each rail before opening sales.
  • Confirm Redis lock prevents multiple payment monitor instances from processing the same payment.
  • Confirm admin ID allowlist and rate limits.
  • Set AUTO_REFUND_LATE_PAYMENTS only after validating refund destination parsing on your chosen TON API.

Test Plan

  • Quote and markup: mock get_pricing(...) for Stars and Premium on both currencies; assert apply_retail_markup(...) total, subtotal, fee, and profit are persisted.
  • Recipient validation: mock eligible and ineligible check_recipient(...); ensure ineligible users never receive payment instructions.
  • Deposit matching: feed monitor fixtures for correct payment (USDT jetton and native GRAM), underpayment, wrong asset, duplicate tx, missing memo, unknown memo, and expired late payment.
  • Fulfillment: after a valid deposit, assert local order moves awaiting_payment -> paid -> fulfilling, MyStars create_order(...) receives the local UUID idempotency key, and wallet payment uses the SDK payment memo.
  • Webhook: post raw body with valid and invalid X-Faas-Signature; assert valid terminal events are deduped by MyStars order ID and update local orders once.
  • Reconcile: simulate a missed terminal order from aiter_orders(...); assert local state catches up.
  • Admin: verify IsAdmin protects the runtime /margin, /fulfill, /reply, order listing, reconcile, refund, broadcast, and balance commands.
  • Deployment: run alembic upgrade head, python -m compileall app, and a dry-run container boot.

License

Released under the MIT License — free to use, modify, and distribute. See LICENSE.

The MIT license covers this project's own code only. Dependencies keep their own (permissive) licenses — see THIRD_PARTY_NOTICES.md; preserve upstream notices when redistributing.

About

Telegram Stars/Premium reseller bot blueprint — aiogram v3, MyStars FaaS fulfillment, TON-network crypto payments

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages