Building a Telegram bot is fun until you hit the "payments" part.
I recently built a bot that sells digital products, and I wanted to accept USDT on Tron (TRC20) because the fees are low and everyone uses it.
I thought: "How hard can it be? I just need to check if an address received money."
Turns out, it's a headache.
If you are a solo dev like me, you usually have two bad options:
Use BitPay/Coinbase Commerce: Requires KYC, business docs, and they hold your funds. (No go for me).
Self-host: Query the blockchain manually.
I tried the self-host route first. Here is what I learned, and the solution I eventually built to fix it.
The "Hard" Way: Polling with TronWeb
To listen for payments, you typically use the tronweb library to interact with the Tron network.
You need to poll the most recent transactions or set up an event listener. Here is a simplified version of the script I initially wrote:
const TronWeb = require('tronweb');
// You need an API Key from TronGrid, or you'll get rate-limited instantly.
const tronWeb = new TronWeb({
fullHost: '[https://api.trongrid.io](https://api.trongrid.io)',
headers: { "TRON-PRO-API-KEY": 'your-api-key' }
});
const USDT_CONTRACT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"; // USDT Address
async function checkRecentTransactions(myWalletAddress) {
// This is where it gets messy.
// Public nodes often time out or return incomplete data.
try {
const contract = await tronWeb.contract().at(USDT_CONTRACT);
// You have to parse the Transfer event logs
// And handle hex conversion for amounts
console.log("Listening for events...");
// ... complex logic to filter events by 'to' address ...
} catch (error) {
console.error("RPC Error:", error);
}
}
The Problem with this approach
It works locally, but in production:
Rate Limits: The free TronGrid API limits you heavily. If you have 10 users checking payments, you get banned.
Missed Events: Public WebSocket connections drop all the time. You need robust retry logic.
Maintenance: You are basically maintaining a mini-indexer just to receive $20.
The "Easy" Way: Webhooks (ChainHook)
I got tired of debugging RPC errors at 3 AM, so I decided to wrap this entire logic into a dedicated microservice.
I built ChainHook.
It's a "Set and Forget" middleware.
You give it a wallet address (Public key only).
It monitors the chain using premium nodes.
It sends a Webhook (POST) to your bot when a payment confirms.
Integration Example
Instead of maintaining the script above, your bot just needs one endpoint to receive the data:
// Your Bot's Backend (Express/Fastify/Python)
app.post('/webhook/payment', (req, res) => {
const { event, data } = req.body;
if (event === 'payment.confirmed') {
const { amount, from, tx_hash } = data;
console.log(`Received ${amount} USDT from ${from}`);
console.log(`Hash: ${tx_hash}`);
// TODO: Auto-deliver product to user
}
res.status(200).send('OK');
});
The payload looks like this:
{
"event": "payment.confirmed",
"data": {
"network": "TRC20",
"currency": "USDT",
"amount": "29.99",
"tx_hash": "89d3a...",
"from": "TMwF...",
"to": "YOUR_WALLET_ADDRESS",
"timestamp": 171092834
}
}
Looking for Beta Testers
I built this primarily for myself and a few friends running Telegram bots, but I'm opening it up for a public beta.
If you are:
Building a Telegram Bot / Discord Bot
Selling digital goods
Hate manual verification (or fake screenshot scams)
I'd love for you to try it out. It's non-custodial, meaning the money goes straight to your wallet—I just act as the notifier.
👉 Join the waiting list here: [https://chainhook-landing.vercel.app/]
(Early testers will get lifetime access to the starter plan for free).
Let me know in the comments if you have questions about handling Tron transactions manually, happy to help with the code too!
Top comments (0)