CChainPay
Увійти

Прийом платежів USDT у Telegram-боті

Telegram-бот може продавати доступ, підписки чи цифрові товари за USDT без акаунта в платіжному провайдері. Боту потрібні дві речі: показати платнику адресу і мати публічну URL для підтвердження. Цей гайд використовує Python, aiogram 3 і aiohttp.

Схема

  1. Користувач надсилає /pay.
  2. Бот створює в ChainPay адресу для депозиту з унікальним externalReference і показує її.
  3. Користувач надсилає USDT.
  4. Після підтвердження ChainPay переказує кошти на ваш гаманець і викликає вебхук бота; бот пише користувачу.

Реєстрація й API-ключ не потрібні. Ваш гаманець — це forwardAddress.

Бот

import asyncio
import hmac
import os
import uuid

import aiohttp
from aiohttp import web
from aiogram import Bot, Dispatcher
from aiogram.filters import Command
from aiogram.types import Message

API = "https://api.chainpay.site"
SECRET = os.environ["WEBHOOK_SECRET"]
WALLET = os.environ["FORWARD_ADDRESS"]
PUBLIC_URL = os.environ["PUBLIC_URL"]  # e.g. https://bot.example.com

bot = Bot(os.environ["BOT_TOKEN"])
dp = Dispatcher()
orders: dict[str, int] = {}  # externalReference -> Telegram user id (use a database in production)
seen: set[str] = set()       # processed "txHash:status" pairs


@dp.message(Command("pay"))
async def pay(message: Message):
    ref = f"tg-{message.from_user.id}-{uuid.uuid4().hex[:8]}"
    orders[ref] = message.from_user.id
    async with aiohttp.ClientSession() as session:
        async with session.post(f"{API}/deposit-address", json={
            "ticker": "trc20/usdt",
            "callbackUrl": f"{PUBLIC_URL}/crypto-webhook?token={SECRET}",
            "forwardAddress": WALLET,
            "externalReference": ref,
        }) as resp:
            data = await resp.json()
    await message.answer(
        f"Send USDT (TRC20) to:\n<code>{data['address']}</code>", parse_mode="HTML"
    )


async def crypto_webhook(request: web.Request):
    if not hmac.compare_digest(request.query.get("token", ""), SECRET):
        return web.Response(status=403)
    event = await request.json()
    key = f"{event['txHash']}:{event['status']}"
    if event["status"] == "confirmed" and key not in seen:
        seen.add(key)
        user_id = orders.get(event["externalReference"])
        if user_id:
            await bot.send_message(
                user_id, f"Received {event['amount']} {event['tokenSymbol']}. Thank you!"
            )
    return web.Response(text="ok")


async def main():
    app = web.Application()
    app.router.add_post("/crypto-webhook", crypto_webhook)
    runner = web.AppRunner(app)
    await runner.setup()
    await web.TCPSite(runner, "0.0.0.0", 8080).start()
    await dp.start_polling(bot)


asyncio.run(main())

Запускайте за HTTPS (зворотний проксі, наприклад Caddy або nginx): ChainPay відхиляє callbackUrl, що вказують на localhost чи приватну IP, тож для локальних тестів потрібен тунель із публічною адресою.

Що доробити перед продакшеном

Дивіться також