How to Add Crypto Payments (USDT) to a Telegram Bot
Telegram bots sell subscriptions, VIP channel access, signals, courses and licences to users all over the world. Cards are hard to accept in that setting; USDT is easy. Here is the complete payment flow with code.
Why crypto fits Telegram bots
A bot's users are spread across countries, banks and card networks; many cannot pay with a card at all. Chargebacks are a constant threat for digital goods. USDT on TRON solves both: anyone with a wallet can pay, the fee is tiny, and the transfer is irreversible. There is no store approval, no country restriction and no monthly fee.
The flow in six steps
- The user sends
/buyor taps a product button. - Your bot server calls
POST /api/create-invoicewith the price. - The bot replies with an inline button linking to
checkout_url, plus a "Check payment" button. - The user pays from their wallet; the checkout page updates itself.
- KriptoGo sends a signed webhook to your server.
- Your server looks up the chat id for the invoice and delivers the product.
Setup: account and keys
Create a KriptoGo account, choose "Telegram bot" as the project type and submit for approval. Store KRIPTOGO_API_KEY, KRIPTOGO_SECRET_KEY and your bot token as environment variables. Your bot needs a small HTTPS server for the webhook; the same server can host the Telegram webhook if you use one.
Handling the purchase command
bot.command('buy', async (ctx) => { const r = await fetch('https://kriptogo.com/api/create-invoice', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ apiKey: process.env.KRIPTOGO_API_KEY, amount: 9.99, callbackUrl: 'https://yourbot.com/kriptogo-webhook' }) }); const inv = await r.json(); await db.orders.insert({ invoiceId: inv.invoice_id, chatId: ctx.chat.id, product: 'vip-30d', status: 'pending' }); await ctx.reply('Pay 9.99 USDT (TRC-20) within 10 minutes:', { reply_markup: { inline_keyboard: [[{ text: 'Pay with USDT', url: inv.checkout_url }], [{ text: 'Check payment', callback_data: 'check:' + inv.invoice_id }]] } }); });
The "Check payment" button calls GET /api/status/:id and tells the user whether the invoice is still pending, paid or expired. It is a convenience; delivery itself should be driven by the webhook.
Receiving the webhook
app.use('/kriptogo-webhook', express.json({ verify: (req, _res, buf) => { req.rawBody = buf.toString('utf8'); } })); app.post('/kriptogo-webhook', async (req, res) => { const expected = crypto.createHmac('sha256', process.env.KRIPTOGO_SECRET_KEY).update(req.rawBody).digest('hex'); const got = req.get('X-Kriptogo-Signature') || ''; if (got.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))) { return res.status(401).send('bad signature'); } res.sendStatus(200); // respond first const { invoice_id, status, paid_amount } = req.body; if (status === 'PAID') await orders.markPaidOnce(invoice_id, paid_amount); // idempotent });
Verify the signature over the raw body, respond 200 immediately, then look up the order by invoice_id. If the order is already marked paid, stop: retries can deliver the same notification more than once.
Delivering the product
For channel access, create a single-use invite link with createChatInviteLink (member limit 1, short expiry) and send it to the chat id. For files, send the document. For licences, generate the key and send it. Record the delivery in the order so a repeated webhook does not deliver twice.
Subscriptions and renewals
Crypto has no automatic recurring charge, so treat each renewal as a new invoice. Store an expiry date on the order; a daily job messages users a few days before expiry with a new payment button, and removes them from the channel with banChatMember followed by unbanChatMember once the grace period passes. Users who pay again get a new expiry date.
Practical tips
- State the network ("USDT on TRON / TRC-20") in every payment message.
- Create the invoice only when the user asks to pay; the 10-minute timer starts at creation.
- Handle the EXPIRED state gracefully: offer a new invoice with one tap.
- Keep the bot token and the KriptoGo keys out of the code and out of Git.
- Log invoice ids with chat ids; support questions almost always start with "I paid but got nothing".
See the Telegram bot payments page for an overview, and the API docs for the full reference.
Frequently asked questions
Can the bot work without a public server?
The webhook needs a public HTTPS endpoint. If you cannot host one, poll the status endpoint from the bot process every few seconds while an invoice is open.
How do I handle a user who underpays?
The invoice will not become PAID if the shortfall exceeds the tolerance. Tell the user the remaining amount, or create a new invoice; never deliver on a partially paid order.
Can I sell in my local currency?
Price in USDT internally and show the local equivalent in the message if you like. The invoice amount must be USDT.
Is there a Telegram-specific SDK?
No SDK is needed. The API is two HTTP calls and one webhook, and works with any bot framework in any language.
Accept crypto payments in 30 minutes with KriptoGo
No setup fee, no monthly fee. Just 1% on successful payments.
