Урок 8.1 — Интеграция с aiogram 3.x
Учимся интегрировать aiosend с aiogram 3.x: параллельный запуск polling, создание инвойсов из сообщений бота и полный цикл оплаты.
aiogram 3.x — это асинхронная библиотека для работы с Telegram Bot API. Интеграция с aiosend строится на параллельном запуске двух polling-циклов: бот слушает команды пользователей, aiosend ожидает оплаты инвойсов. Оба цикла работают одновременно через asyncio.gather().
Параллельный запуск polling
Для одновременной работы aiogram и aiosend используется asyncio.gather(). Оба polling-цикла запускаются параллельно в одном event loop.
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiosend import CryptoPay
BOT_TOKEN = "123456:ABCdefGHIjklmNOP"
CP_TOKEN = "1234:ABCDefGHIJklmnOPQRST"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
cp = CryptoPay(token=CP_TOKEN)
@dp.message(Command("start"))
async def start(message: types.Message):
await message.answer(
"Привет! Я бот с приёмом крипто-платежей!\n"
"Напиши /pay 10 USDT для создания счёта."
)
@dp.message(Command("pay"))
async def pay(message: types.Message):
args = message.text.split()[1:] # ["10", "USDT"]
if len(args) < 2:
await message.answer("Использование: /pay 10 USDT")
return
amount, asset = float(args[0]), args[1].upper()
invoice = await cp.create_invoice(
amount=amount,
asset=asset,
payload=str(message.from_user.id),
)
# Отправляем пользователю ссылку на оплату
await message.answer(
f"Счёт на {amount} {asset}:\n"
f"{invoice.bot_invoice_url}\n\n"
f"Mini App: {invoice.mini_app_invoice_url}"
)
@cp.invoice_paid()
async def on_paid(invoice):
user_id = int(invoice.payload)
await bot.send_message(
user_id,
f"✅ Оплата {invoice.amount} {invoice.asset} получена!\n"
f"Спасибо за покупку!"
)
async def main():
await asyncio.gather(
dp.start_polling(bot), # aiogram polling
cp.start_polling(), # aiosend polling
)
asyncio.run(main())
💡 Как это работает?
aiogram слушает команды от Telegram и отвечает пользователям. aiosend параллельно опрашивает Crypto Pay API на предмет оплаты созданных инвойсов. Когда платёж подтверждён, aiosend вызывает хендлер, который через aiogram отправляет пользователю уведомление.
Создание инвойса из сообщения
Метод invoice.poll(message=message) позволяет привязать ожидание оплаты к конкретному сообщению в Telegram. Когда пользователь оплачивает инвойс, бот автоматически редактирует сообщение.
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiosend import CryptoPay
bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()
cp = CryptoPay(token="CP_TOKEN")
@dp.message(Command("buy"))
async def buy(message: types.Message):
# Создаём инвойс
invoice = await cp.create_invoice(
amount=15,
asset="USDT",
description="Премиум-доступ",
payload=str(message.from_user.id),
)
# Отправляем сообщение с ссылкой на оплату
msg = await message.answer(
f"💳 Счёт на 15 USDT\n"
f"Ссылка: {invoice.bot_invoice_url}\n\n"
f"⏳ Ожидаем оплату..."
)
# Ожидаем оплату с привязкой к сообщению
paid = await invoice.poll(message=msg)
# После оплаты сообщение автоматически обновится
# Можно отредактировать вручную:
await msg.edit_text(
f"✅ Оплачено {paid.paid_amount} {paid.paid_asset}!\n"
f"Спасибо за покупку!"
)
async def main():
await asyncio.gather(
dp.start_polling(bot),
cp.start_polling(),
)
asyncio.run(main())
⚠️ Особенности message в poll()
Параметр message должен быть объектом aiogram Message. aiosend использует его для вызова message.bot.edit_message_text() при успешной оплате. Убедись, что у бота есть права на редактирование сообщений.
Полный пример: магазин на aiogram 3.x
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton
from aiosend import CryptoPay
BOT_TOKEN = "123456:ABCdefGHIjklmNOP"
CP_TOKEN = "1234:ABCDefGHIJklmnOPQRST"
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
cp = CryptoPay(token=CP_TOKEN)
# Товары
PRODUCTS = {
"premium": {"name": "Премиум-подписка", "price": 15, "asset": "USDT"},
"vip": {"name": "VIP-доступ", "price": 50, "asset": "USDT"},
"ton_pack": {"name": "Набор TON", "price": 10, "asset": "TON"},
}
@dp.message(Command("start"))
async def start(message: types.Message):
kb = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="🛒 Купить Премиум", callback_data="buy_premium")],
[InlineKeyboardButton(text="⭐ Купить VIP", callback_data="buy_vip")],
[InlineKeyboardButton(text="💎 Набор TON", callback_data="buy_ton_pack")],
])
await message.answer("Добро пожаловать в магазин!", reply_markup=kb)
@dp.callback_query(lambda c: c.data.startswith("buy_"))
async def handle_buy(callback: types.CallbackQuery):
product_key = callback.data.replace("buy_", "")
product = PRODUCTS.get(product_key)
if not product:
await callback.answer("Товар не найден")
return
await callback.answer("Создаём счёт...")
invoice = await cp.create_invoice(
amount=product["price"],
asset=product["asset"],
description=product["name"],
payload=f"{callback.from_user.id}:{product_key}",
)
msg = await callback.message.answer(
f"💳 {product['name']}\n"
f"Сумма: {product['price']} {product['asset']}\n\n"
f"🔗 Оплатить в Crypto Bot\n"
f"🖥 Mini App: {invoice.mini_app_invoice_url}\n\n"
f"⏳ Ожидаем оплату...",
parse_mode="HTML",
)
paid_invoice = await invoice.poll(message=msg)
await msg.edit_text(
f"✅ Оплачено!\n"
f"{paid_invoice.paid_amount} {paid_invoice.paid_asset}\n"
f"Товар: {product['name']}\n"
f"Спасибо за покупку!",
parse_mode="HTML",
)
@cp.invoice_paid()
async def on_paid(invoice):
"""Логируем все оплаты."""
user_id, product_key = invoice.payload.split(":")
product = PRODUCTS.get(product_key, {"name": "Неизвестный"})
print(
f"Платёж #{invoice.invoice_id}: "
f"user={user_id}, "
f"product={product['name']}, "
f"amount={invoice.paid_amount} {invoice.paid_asset}"
)
async def main():
print("Бот запущен...")
await asyncio.gather(
dp.start_polling(bot),
cp.start_polling(),
)
asyncio.run(main())
🔍 Ключевые моменты
asyncio.gather()запускает два polling-цикла параллельноpayloadхранит user_id и название товара для обработкиinvoice.poll(message=msg)автоматически обновляет сообщение при оплате@cp.invoice_paid()— глобальный хендлер для логирования- Inline-кнопки с callback_data для выбора товара
Обработка ошибок и таймаутов
При ожидании оплаты через invoice.poll() может возникнуть таймаут (пользователь не оплатил). Обрабатывай это исключение и уведомляй пользователя.
from aiosend.exceptions import APITimeoutError
@dp.message(Command("buy"))
async def buy_safe(message: types.Message):
invoice = await cp.create_invoice(
amount=10, asset="USDT",
expires_in=300, # 5 минут
)
msg = await message.answer(
f"Счёт на 10 USDT\n{invoice.bot_invoice_url}"
)
try:
paid = await invoice.poll(message=msg)
await msg.edit_text(f"✅ Оплачено: {paid.paid_amount} USDT")
except APITimeoutError:
await msg.edit_text("⏱ Время оплаты истекло. Попробуйте снова.")
except Exception as e:
await msg.edit_text(f"❌ Ошибка: {e}")
Что важно запомнить
Практическое задание
Задание: Бот для приёма донатов
Создай бота на aiogram 3.x + aiosend, который:
- По команде /donate [сумма] [валюта] создаёт инвойс
- Отправляет ссылку на оплату
- Ожидает оплату через invoice.poll(message=msg)
- Благодарит пользователя после оплаты
- Логирует все донаты через @cp.invoice_paid()
Подсказка:
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiosend import CryptoPay
bot = Bot(token="BOT_TOKEN")
dp = Dispatcher()
cp = CryptoPay(token="CP_TOKEN")
@dp.message(Command("donate"))
async def donate(message: types.Message):
args = message.text.split()
if len(args) < 3:
await message.answer("Пример: /donate 10 USDT")
return
amount, asset = float(args[1]), args[2].upper()
invoice = await cp.create_invoice(
amount=amount, asset=asset,
payload=str(message.from_user.id),
)
msg = await message.answer(
f"💛 Спасибо за поддержку!\n"
f"Счёт на {amount} {asset}\n"
f"{invoice.bot_invoice_url}"
)
paid = await invoice.poll(message=msg)
await msg.edit_text(
f"✅ Получено {paid.paid_amount} {paid.paid_asset}!\n"
f"Огромное спасибо за донат! ❤️"
)
@cp.invoice_paid()
async def log_donate(invoice):
print(f"Донат: user={invoice.payload} сумма={invoice.amount}")
async def main():
await asyncio.gather(dp.start_polling(bot), cp.start_polling())
asyncio.run(main())
Урок 8.1: Интеграция с aiogram 3.x
5 вопросов