Skip to content
✦ AddThisBot
For developers

Build Telegram bots that earn Stars.

We run 35 Telegram bots on Cloudflare Workers. Here is how they take payment, how Mini Apps work, and how to get your own bot listed or built.

bot.ts — sell Pro for 150 Stars
await tg("sendInvoice", {
  chat_id: chatId,
  title: "Pro",
  description: "Unlock Pro for this group. One payment.",
  payload: `pro:${chatId}`,     // your own ID, 1-128 bytes
  provider_token: "",            // empty string for Stars
  currency: "XTR",               // Telegram Stars
  prices: [{ label: "Pro", amount: 150 }], // exactly one item
});

That is the whole invoice. Telegram handles the checkout UI.

What you can do here

How our fleet is built

35 bots. One pattern.

Every bot follows the same path: a Telegram update hits a webhook on Cloudflare Workers, the bot answers, and paid features unlock after a Stars payment.

01 · People

Users in Telegram

Private chats, groups and channels.

/start
Hi! Here is what I can do…
/pro
⭐ Pay 150 Stars

02 · Transport

Telegram Bot API

Each update arrives as one HTTPS POST to a webhook.

  • POSTupdate → webhook
  • callsendMessage
  • callsendInvoice · XTR

03 · Compute

Cloudflare Workers

One bot per job. 35 bots. No servers to patch.

04 · Revenue

Stars checkout

  1. 1 Invoicecurrency XTR
  2. 2 Pre-checkoutanswer < 10 s
  3. 3 Paymentsuccessful_payment
  4. 4 Pro onuser or group

Free

Free to use in Telegram. No sign-up. Pro is optional.

⭐ 75 or 150

One-time Pro in 30 bots. Paid in Stars inside the bot.

1 payment

Unlocks Pro for a whole group in 16 bots. Per user in 12, per chat in 2.

8

Bots that also sell a 30-day Stars subscription.

01

One bot, one job

Small bots are easy to name, easy to explain, easy to find in search. Each of ours does one thing.

02

Free first, Pro second

Everyone can use the free tier. 30 bots sell a one-time Pro in Stars, scoped per user or per group.

03

Stars only for digital goods

Telegram requires Stars (XTR) for digital goods in bots and Mini Apps. No card forms, no payment provider setup.

04

Edge, not servers

Webhooks land on Cloudflare Workers. No machines to patch. One deploy per bot.

Copy and run

The code that takes the money.

A Worker that sells Pro for Stars fits in under 100 lines. No SDK. Copy it, add your token, deploy.

  1. 1

    Create a bot

    Talk to @BotFather. Copy the token.

  2. 2

    Deploy the Worker

    wrangler deploy. Store the token as a secret.

  3. 3

    Set the webhook

    One curl call. Add a secret token.

  4. 4

    Test in the test environment

    Separate accounts, no real Stars.

Read the full Stars guide →
1 · set the webhook
# Point Telegram at your Worker. Telegram sends the secret back in a header.
curl "https://api.telegram.org/bot$BOT_TOKEN/setWebhook" \
  -d "url=https://my-bot.example.workers.dev/" \
  -d "secret_token=$WEBHOOK_SECRET" \
  -d 'allowed_updates=["message","pre_checkout_query"]'
2 · src/index.ts — take the Stars payment
// The two updates that matter for payments (excerpt of src/index.ts)
if (update.pre_checkout_query) {
  const q = update.pre_checkout_query;
  // Answer within 10 seconds, or Telegram cancels the payment.
  await tg(env, "answerPreCheckoutQuery", {
    pre_checkout_query_id: q.id,
    ok: q.currency === "XTR" && q.total_amount === PRO_STARS,
    error_message: "This invoice is out of date. Send /pro again.",
  });
}

const p = update.message?.successful_payment;
if (p) {
  // Save the charge ID first: refundStarPayment needs it.
  await savePurchase(env, p.telegram_payment_charge_id, update.message.from.id, p.invoice_payload);
  await unlockPro(env, p.invoice_payload); // per user or per group
}

Excerpt. The full Worker, with tg(), /terms and /paysupport, is in the Stars guide. Checked against the Bot API docs on 2026-10-28.

3 · validate.ts — trust Mini App data
// validate.ts — check Telegram.WebApp.initData on a Cloudflare Worker (Web Crypto, no deps)
const enc = new TextEncoder();

async function hmac(key: BufferSource, msg: string): Promise<ArrayBuffer> {
  const k = await crypto.subtle.importKey("raw", key, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
  return crypto.subtle.sign("HMAC", k, enc.encode(msg));
}
const hex = (b: ArrayBuffer) => [...new Uint8Array(b)].map((x) => x.toString(16).padStart(2, "0")).join("");

export async function validateInitData(initData: string, botToken: string, maxAgeSec = 3600) {
  const params = new URLSearchParams(initData);
  const hash = params.get("hash");
  if (!hash) return null;
  params.delete("hash");

  // data-check-string: every other field, sorted by key, "key=value", joined by "\n"
  const dataCheckString = [...params.entries()]
    .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
    .map(([k, v]) => `${k}=${v}`)
    .join("\n");

  const secretKey = await hmac(enc.encode("WebAppData"), botToken); // key = "WebAppData"
  const expected = hex(await hmac(secretKey, dataCheckString));

  // Constant-time compare
  if (expected.length !== hash.length) return null;
  let diff = 0;
  for (let i = 0; i < hash.length; i++) diff |= expected.charCodeAt(i) ^ hash.charCodeAt(i);
  if (diff !== 0) return null;

  const authDate = Number(params.get("auth_date"));
  if (!authDate || Date.now() / 1000 - authDate > maxAgeSec) return null; // too old

  return { user: JSON.parse(params.get("user") ?? "null"), startParam: params.get("start_param") };
}
See it live

Try the checkout yourself.

These bots sell a monthly Stars subscription next to the one-time Pro. Open one in Telegram, pick the Pro option and see a real Stars invoice.

For builders

Have a bot? Need one?

Two ways to work with us. Pick the one that fits.

Questions first? Message us in Telegram.