Telegram Stars: что это и как работает оплата

Поймёшь, чем Stars отличаются от on-chain Gram, и пройдёшь полный цикл оплаты через Bot API: инвойс → pre_checkout → successful_payment.

Что такое Telegram Stars

Telegram Stars (валюта XTR) — внутриплатформенный платёжный механизм Telegram для цифровых товаров и услуг в ботах и Mini Apps. Это не Gram и не on-chain перевод: баланс Stars ведёт Telegram, списание происходит через Bot Payments API.

Пользователь покупает Stars у Telegram (App Store / Google Play / @PremiumBot и др.), затем платит ими боту. Ты как разработчик получаешь Stars на баланс бота — отдельно от кошелька в сети TON. У Stars нет фиксированного курса к Gram.


Полный цикл оплаты

typescript
// Telegram Bot API 8.0+ (актуально на июль 2026)
// Пример на raw fetch — без привязки к конкретной JS-библиотеке

const BOT_TOKEN = process.env.BOT_TOKEN!;
const API = `https://api.telegram.org/bot${BOT_TOKEN}`;

async function tg<T>(method: string, body: Record<string, unknown>): Promise<T> {
  const res = await fetch(`${API}/${method}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const data = (await res.json()) as { ok: boolean; result: T; description?: string };
  if (!data.ok) throw new Error(data.description ?? method);
  return data.result;
}

/** 1. Отправить инвойс в чат (цифровой товар → currency XTR, provider_token пустой) */
export async function sendStarsInvoice(chatId: number) {
  return tg("sendInvoice", {
    chat_id: chatId,
    title: "Pro-доступ",
    description: "Разблокировка Pro-функций бота на 30 дней",
    payload: "pro_access_v1", // до 128 байт, вернётся в updates
    provider_token: "", // обязательно пусто для Stars
    currency: "XTR",
    prices: [{ label: "Pro-доступ", amount: 50 }], // amount — целые Stars, не «копейки»
  });
}

/** 2. Ответить на pre_checkout_query за ≤10 секунд */
export async function approveCheckout(preCheckoutQueryId: string, ok: boolean, errorMessage?: string) {
  return tg("answerPreCheckoutQuery", {
    pre_checkout_query_id: preCheckoutQueryId,
    ok,
    ...(ok ? {} : { error_message: errorMessage ?? "Заказ недоступен" }),
  });
}

type Update = {
  pre_checkout_query?: {
    id: string;
    currency: string;
    total_amount: number;
    invoice_payload: string;
  };
  message?: {
    successful_payment?: {
      currency: string;
      total_amount: number;
      invoice_payload: string;
      telegram_payment_charge_id: string;
    };
  };
};

export async function handleUpdate(update: Update) {
  const pcq = update.pre_checkout_query;
  if (pcq) {
    const valid =
      pcq.currency === "XTR" &&
      pcq.invoice_payload === "pro_access_v1" &&
      pcq.total_amount === 50;
    await approveCheckout(pcq.id, valid, "Некорректный заказ");
    return;
  }

  const payment = update.message?.successful_payment;
  if (payment) {
    // Только после successful_payment можно выдавать товар
    await saveChargeId(payment.telegram_payment_charge_id);
    await grantProAccess(payment.invoice_payload);
  }
}

declare function saveChargeId(id: string): Promise<void>;
declare function grantProAccess(payload: string): Promise<void>;

Как это работает

  • currency: "XTR" — единственная валюта для цифровых товаров внутри Telegram; другие провайдеры для digital goods в боте/Mini App нельзя.
  • provider_token: "" — токен платёжного провайдера нужен только для физических товаров; для Stars — пустая строка.
  • prices[].amount — число Stars целиком (50 = 50 Stars), не минорные единицы как у USD/EUR.
  • pre_checkout_query — Telegram спрашивает «можно ли принять заказ»; ответ обязателен ≤10 с, иначе платёж отменится.
  • successful_payment — единственный надёжный сигнал, что деньги списаны; answerPreCheckoutQuery(ok: true) сам по себе оплату не гарантирует.
  • telegram_payment_charge_id — сохрани: нужен для refund.

Частые ошибки

Путаешь Stars с переводом Gram → Stars — off-chain баланс Telegram. On-chain перевод Gram/jetton — отдельный стек (TON Connect, контракты). Не смешивай в одном «платеже» без явной архитектуры.

Выдаёшь товар после answerPreCheckoutQuery → пользователь может отменить или платёж может не пройти. Жди successful_payment.

Ставишь amount: 5000 «как копейки» → для Stars это 5000 Stars, не «50.00». Проверяй total_amount в pre-checkout.


Что дальше

Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация