Сгенерируешь invoice link на бэкенде и откроешь оплату Stars из Mini App через WebApp.openInvoice.
Оплата Stars из Mini App
В Mini App нет прямого «списать Stars» с клиента. Клиент только открывает нативный платёжный UI: бэкенд создаёт ссылку через createInvoiceLink, фронт вызывает WebApp.openInvoice(url). Подтверждение оплаты и выдача товара — на стороне бота (pre_checkout_query / successful_payment).
Бэкенд: ссылка + фронт: openInvoice
typescript
// Telegram Bot API 8.0+ / WebApp API 6.1+ (openInvoice)// Бэкенд: Node 20+, фронт: страница внутри Telegram Mini Appconst BOT_TOKEN = process.env.BOT_TOKEN!;const API = `https://api.telegram.org/bot${BOT_TOKEN}`;type InvoiceRequest = { userId: number; sku: "pro_pack"; initData: string; // обязательно валидируй HMAC на сервере};export async function createStarsInvoiceLink(req: InvoiceRequest): Promise<string> { // validateInitData(req.initData) — см. статью про initData const payload = JSON.stringify({ sku: req.sku, userId: req.userId, nonce: crypto.randomUUID(), }); const res = await fetch(`${API}/createInvoiceLink`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: "Pro Pack", description: "Набор Pro-функций в Mini App", payload, // до 128 байт provider_token: "", currency: "XTR", prices: [{ label: "Pro Pack", amount: 100 }], }), }); const data = (await res.json()) as { ok: boolean; result: string; description?: string }; if (!data.ok) throw new Error(data.description ?? "createInvoiceLink failed"); return data.result; // https://t.me/$... или invoice-ссылка Telegram}// --- фронт Mini App ---declare global { interface Window { Telegram: { WebApp: { openInvoice: ( url: string, callback?: (status: "paid" | "cancelled" | "failed" | "pending") => void, ) => void; showAlert: (message: string) => void; initData: string; initDataUnsafe: { user?: { id: number } }; }; }; }}export async function payProPack(): Promise<void> { const tg = window.Telegram.WebApp; const userId = tg.initDataUnsafe.user?.id; if (!userId) throw new Error("Нет user.id в initDataUnsafe"); const res = await fetch("/api/stars/invoice", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ userId, sku: "pro_pack", initData: tg.initData, }), }); if (!res.ok) throw new Error("Не удалось создать инвойс"); const { invoiceLink } = (await res.json()) as { invoiceLink: string }; tg.openInvoice(invoiceLink, (status) => { if (status === "paid") { // UX-сигнал. Источник истины — webhook successful_payment на боте tg.showAlert("Оплата прошла. Обновляем доступ…"); void refreshEntitlements(); return; } if (status === "cancelled") { tg.showAlert("Оплата отменена"); return; } if (status === "failed") { tg.showAlert("Оплата не удалась"); } });}declare function refreshEntitlements(): Promise<void>;
Материалы gramdocs.tech носят образовательный характер и не являются финансовой, юридической или инвестиционной рекомендацией. Работа с блокчейном TON и токеном Gram связана с рисками потери средств. Правовая информация