Telegram Stars are a built-in payment system for Mini Apps — no Stripe, no App Store fees, no payment gateway needed. Users pay with Stars they already have in Telegram, and you get paid directly.
In this tutorial, I'll show you exactly how to implement Stars payments in a Node.js + Express backend, based on what I built for WhisprMe — an anonymous messaging Mini App.
What Are Telegram Stars?
Stars are Telegram's in-app currency. Users buy them inside Telegram and spend them in Mini Apps and bots. As a developer, you:
- Create an invoice via the Bot API
- Handle the pre-checkout query
- Confirm the payment
- Deliver the digital good
There are zero platform fees for the first withdrawal (Telegram takes a cut on subsequent ones). The entire flow happens inside Telegram — no redirects, no card forms.
Prerequisites
- A Telegram Bot (create one via @BotFather)
- Node.js 18+
- The
telegrafpackage (ornode-telegram-bot-api) - A Telegram Mini App connected to your bot
Step 1: Create the Invoice Endpoint
Your frontend calls this endpoint to get a payment link:
// routes.js
const { Telegraf } = require('telegraf');
const bot = new Telegraf(process.env.BOT_TOKEN);
app.post('/api/create-invoice', async (req, res) => {
const { telegramId, itemType, amount } = req.body;
try {
const invoiceLink = await bot.telegram.createInvoiceLink({
title: 'Premium Feature',
description: 'Unlock premium features in WhisprMe',
payload: JSON.stringify({
userId: telegramId,
type: itemType
}),
provider_token: '', // Empty for Stars!
currency: 'XTR', // XTR = Telegram Stars
prices: [{
label: 'Premium',
amount: amount // in Stars (1 Star = 1 unit)
}]
});
res.json({ invoiceLink });
} catch (err) {
console.error('Invoice error:', err);
res.status(500).json({ error: 'Failed to create invoice' });
}
});
Key points:
-
provider_tokenmust be an empty string for Stars -
currencymust be'XTR' -
amountis in whole Stars (no decimals) - The
payloadis your custom data — you'll get it back after payment
Step 2: Handle Pre-Checkout Query
Before Telegram charges the user, it sends a pre-checkout query. You must answer within 10 seconds:
// bot.js
bot.on('pre_checkout_query', async (ctx) => {
try {
const payload = JSON.parse(ctx.preCheckoutQuery.invoice_payload);
// Validate the purchase
// - Does the user exist?
// - Is the item still available?
// - Is the price correct?
await ctx.answerPreCheckoutQuery(true);
} catch (err) {
await ctx.answerPreCheckoutQuery(false,
'Something went wrong. Please try again.'
);
}
});
Critical: If you don't respond to pre_checkout_query within 10 seconds, the payment fails silently. Make your validation fast!
Step 3: Handle Successful Payment
After the user pays, Telegram sends a successful_payment message:
bot.on('message', async (ctx) => {
if (!ctx.message.successful_payment) return;
const payment = ctx.message.successful_payment;
const payload = JSON.parse(payment.invoice_payload);
const stars = payment.total_amount;
// Record the transaction
await db.query(
\`INSERT INTO transactions
(user_id, type, stars_amount, telegram_payment_id)
VALUES ($1, $2, $3, $4)\`,
[payload.userId, payload.type, stars,
payment.telegram_payment_charge_id]
);
// Deliver the digital good
if (payload.type === 'premium') {
await db.query(
'UPDATE users SET subscription_tier = $1 WHERE telegram_id = $2',
['premium', payload.userId]
);
}
// Notify the user
await ctx.reply(
\`Payment successful! You paid ${stars} Stars.\`
);
});
Step 4: Open the Invoice from Your Mini App
In your React frontend, use the Telegram WebApp SDK:
// React component
const handlePurchase = async (itemType, amount) => {
const response = await fetch('/api/create-invoice', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
telegramId: window.Telegram.WebApp.initDataUnsafe.user.id,
itemType,
amount
})
});
const { invoiceLink } = await response.json();
// This opens Telegram's native payment UI
window.Telegram.WebApp.openInvoice(invoiceLink, (status) => {
if (status === 'paid') {
// Refresh UI, show success
showNotification('Payment successful!');
window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
}
});
};
The Complete Payment Flow
Here's what happens when a user clicks "Buy":
-
Frontend calls
/api/create-invoice - Backend creates an invoice link via Bot API
-
Frontend opens it with
WebApp.openInvoice() - Telegram shows native payment UI
- User confirms payment with Stars
-
Telegram sends
pre_checkout_query→ your bot answerstrue -
Telegram sends
successful_payment→ you deliver the good -
Frontend callback fires with
status === 'paid'
The entire flow takes about 2 seconds. No redirects, no card forms, no 3D Secure.
Real-World Tips from Building WhisprMe
After processing real payments in WhisprMe, here's what I learned:
1. Always validate in pre_checkout_query. Don't just auto-approve. Check that the item exists and the price hasn't changed.
2. Use the payload wisely. Encode everything you need to fulfill the order — user ID, item type, quantity. You'll thank yourself during debugging.
3. Idempotency matters. Store telegram_payment_charge_id and check for duplicates. Telegram can sometimes send duplicate successful_payment events.
4. Stars pricing psychology. We found that 15 Stars works better than 10 or 20 for premium features. It feels "affordable but not cheap."
5. Haptic feedback. That HapticFeedback.notificationOccurred('success') after payment makes a huge difference in user experience.
Setting Up in Production
Make sure your webhook is properly configured for payment events:
// index.js
const express = require('express');
const app = express();
// Important: set webhook with allowed_updates
await bot.telegram.setWebhook(
'https://yourdomain.com/bot-webhook',
{ allowed_updates: ['message', 'pre_checkout_query'] }
);
app.use('/bot-webhook',
express.json(),
(req, res) => bot.handleUpdate(req.body, res)
);
Conclusion
Telegram Stars make payments ridiculously simple compared to traditional payment gateways. No KYC for users, no payment forms, instant delivery — it's the kind of payment UX we've always wanted.
If you're building a Telegram Mini App, Stars should be your default monetization strategy. The conversion rates are significantly higher than traditional payment methods because users don't leave the app.
Try it yourself: @WhisprMe_bot — send anonymous messages, unlock premium features with Stars.
What payment challenges have you faced with Telegram Mini Apps? Drop a comment below!
Top comments (0)