Accept crypto payments in your Telegram bot
This guide walks you through accepting USDC/USDT payments inside a Telegram bot using the Inzi API.
What you’ll build:
- User taps “Pay” in your bot
- Bot creates an Inzi checkout
- User pays in crypto on the hosted checkout page
- Your bot receives a webhook and fulfills the order
Get API keys
- Sign up at inzipay.com
- Go to Settings → API Keys and generate a test key (
sk_test_...) - Copy your Webhook Secret from Settings → Webhook
Create checkout when user taps “Pay”
When a user initiates a payment, create a checkout on your backend:
Node.js (grammY)
import { Bot, InlineKeyboard } from 'grammy'
const INZI_KEY = process.env.INZI_API_KEY // sk_test_... or sk_live_...
bot.command('pay', async (ctx) => {
const amount = ctx.match || '10.00'
const userId = String(ctx.from.id)
// Create Inzi checkout
const res = await fetch('https://api.inzilink.com/api/v1/checkouts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${INZI_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency: 'USD',
description: `Top-up $${amount}`,
metadata: {
user_id: userId,
type: 'topup',
telegram_chat_id: String(ctx.chat.id),
},
webhook_url: 'https://yourbot.com/webhooks/inzi',
expires_in: 1800,
}),
})
const { checkout_url } = await res.json()
// Send payment button
const keyboard = new InlineKeyboard()
.url('Pay now', checkout_url)
await ctx.reply(`Pay $${amount} with crypto:`, {
reply_markup: keyboard,
})
})The checkout page works inside Telegram’s built-in browser. Users select a chain (TON, Polygon, etc.), see a QR code + address, and pay from any wallet.
Handle the webhook
When payment is confirmed, Inzi sends a checkout.completed webhook to your server:
Node.js (Express)
import express from 'express'
import crypto from 'crypto'
const app = express()
app.use('/webhooks/inzi', express.raw({ type: 'application/json' }))
const WEBHOOK_SECRET = process.env.INZI_WEBHOOK_SECRET
app.post('/webhooks/inzi', async (req, res) => {
// Verify signature
const timestamp = req.headers['x-inzi-timestamp']
const signature = req.headers['x-inzi-signature'].replace('sha256=', '')
const signedPayload = `${timestamp}.${req.body.toString()}`
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(signedPayload)
.digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body.toString())
if (event.event === 'checkout.completed') {
const { user_id, telegram_chat_id } = event.data.metadata
const amount = event.data.amount
// Credit user balance in your database
await creditBalance(user_id, parseFloat(amount))
// Notify user in Telegram
await bot.api.sendMessage(
telegram_chat_id,
`Payment of $${amount} received! Your balance has been updated.`
)
}
res.json({ ok: true })
})Test the flow
- Use
sk_test_key — mock webhook fires in 5 seconds - Send
/pay 10to your bot - Tap “Pay now” — see the checkout page
- Your webhook endpoint receives
checkout.completed - Bot sends confirmation message
Go live
Switch to sk_live_ and update your webhook URL to production.
Telegram WebApp support: The checkout page detects Telegram’s WebView and calls Telegram.WebApp.close() after payment, returning the user to your bot.