$ sudo teach IT
Модуль 7 · Продвинутые техники

Урок 7.2 — Shortcut-методы объектов API

Изучаем shortcut-методы объектов Invoice, Check, Balance, ExchangeRate — удобные инструменты для быстрых операций без прямого обращения к клиенту.

Shortcut-методы — это методы, которые вызываются непосредственно на объекте (например, invoice.update()) и внутри обращаются к клиенту, который создал этот объект. Тебе не нужно хранить ссылку на CryptoPay — объект сам знает, через какой клиент был получен, и использует его для API-запросов.

🧾

Shortcut-методы Invoice

Объект Invoice предоставляет несколько shortcut-методов для управления счётом. Рассмотрим каждый подробно.

Invoice.update()

Обновляет данные объекта из API. Полезно, когда нужно получить актуальный статус инвойса.

Python · Обновление инвойса
from aiosend import CryptoPay

cp = CryptoPay(token="1234:TOKEN")

# Создаём инвойс
invoice = await cp.create_invoice(amount=10, asset="USDT")
print(f"Статус: {invoice.status}")  # active

# ...проходит время, инвойс оплачивают...

# Обновляем данные из API
await invoice.update()
print(f"Новый статус: {invoice.status}")  # paid
print(f"Оплачено: {invoice.paid_amount} {invoice.paid_asset}")
print(f"Время оплаты: {invoice.paid_at}")

Invoice.delete()

Удаляет инвойс. Доступно только для неоплаченных счетов.

Python · Удаление инвойса
# Создаём и сразу удаляем (например, если ошиблись)
invoice = await cp.create_invoice(amount=100, asset="USDT")
print(f"Создан инвойс #{invoice.invoice_id}")

# Удаляем
result = await invoice.delete()
print(f"Удалён: {result}")  # True

# Попытка удалить оплаченный инвойс вызовет ошибку
paid_invoice = await cp.get_invoice(invoice_id=123)
try:
    await paid_invoice.delete()
except Exception as e:
    print(f"Нельзя удалить: {e}")

Invoice.poll()

Ожидает оплату конкретного инвойса. Возвращает обновлённый объект после оплаты.

Python · Ожидание оплаты
import asyncio
from aiosend import CryptoPay

async def main():
    cp = CryptoPay(token="1234:TOKEN")

    invoice = await cp.create_invoice(
        amount=0.5, asset="TON",
        description="Тестовый платёж",
    )
    print(f"Ссылка на оплату: {invoice.bot_invoice_url}")

    # Ждём оплаты (по умолчанию таймаут 300 сек)
    paid = await invoice.poll()
    print(f"Инвойс #{paid.invoice_id} оплачен!")
    print(f"Сумма: {paid.paid_amount} {paid.paid_asset}")

asyncio.run(main())

Метод poll() также принимает необязательные параметры:

Параметр Тип Описание
on_paid Callable | None Функция-колбэк, вызываемая после оплаты. Принимает invoice и **kwargs.
**kwargs Any Произвольные аргументы, передаваемые в on_paid.
Python · Poll с колбэком
async def after_payment(invoice, **kwargs):
    user_id = kwargs.get("user_id")
    print(f"Пользователь {user_id} оплатил инвойс #{invoice.invoice_id}")

invoice = await cp.create_invoice(amount=15, asset="USDT")

# Передаём колбэк и дополнительные данные
await invoice.poll(
    on_paid=after_payment,
    user_id=12345,
)

Invoice.qr

Свойство, возвращающее URL QR-кода для быстрой оплаты.

Python · Получение QR-кода инвойса
invoice = await cp.create_invoice(amount=25, asset="USDT")

# QR-код — это свойство (property), не метод
qr_url = invoice.qr
print(f"QR-код: {qr_url}")
# → https://qr.crypt.bot/?url=https://pay.crypt.bot/invoice/...

# Можно отправить пользователю как изображение
# в Telegram: bot.send_photo(chat_id, qr_url)
✅

Shortcut-методы Check

Check (крипто-чек) — это объект, представляющий криптовалютный чек. У него есть свои shortcut-методы.

Check.update()

Обновляет данные чека из API.

Python · Обновление чека
from aiosend import CryptoPay

cp = CryptoPay(token="1234:TOKEN")

# Получаем существующий чек
check = await cp.get_checks(check_ids=[123])[0]

print(f"Статус: {check.status}")  # active

# Обновляем
await check.update()
print(f"Новый статус: {check.status}")

Check.delete()

Удаляет чек. Доступно только для неактивированных чеков.

Python · Удаление чека
check = await cp.create_check(amount=10, asset="USDT")
print(f"Создан чек #{check.check_id}")

# Удаляем
result = await check.delete()
print(f"Результат удаления: {result}")  # True

Check.poll()

Ожидает активации (получения) чека. Аналогичен invoice.poll().

Python · Ожидание активации чека
check = await cp.create_check(amount=5, asset="TON")
print(f"Чек создан: {check.check_id}")
print(f"Ссылка: {check.bot_check_url}")

# Ждём активации
activated = await check.poll()
print(f"Чек #{activated.check_id} активирован!")
print(f"Активирован пользователем: {activated.activate_by}")

Check.qr

Свойство, возвращающее URL QR-кода для чека.

Python · QR-код чека
check = await cp.create_check(amount=10, asset="USDT")
print(f"QR-код чека: {check.qr}")

Check.get_image()

Генерирует URL изображения чека. Это не async метод, так как не делает HTTP-запросов — только формирует URL.

Python · Изображение чека
check = await cp.create_check(amount=100, asset="USDT")

# Получаем URL изображения чека
image_url = check.get_image()
print(f"Изображение чека: {image_url}")

# Можно отправить как картинку в Telegram:
# await bot.send_photo(chat_id, image_url, caption="Ваш чек!")

💡 get_image() — синхронный метод

get_image() не делает HTTP-запросов. Он просто формирует URL к сервису imggen.send.tg, который генерирует PNG-изображение чека. Поэтому его не нужно await'ить.

💰

Shortcut-методы Balance

Объект Balance представляет баланс вашего приложения в определённой валюте. У него есть метод update().

Balance.update()

Запрашивает актуальный баланс из API.

Python · Обновление баланса
from aiosend import CryptoPay

cp = CryptoPay(token="1234:TOKEN")

# Получаем все балансы
balances = await cp.get_balances()
for balance in balances:
    print(f"{balance.asset}: {balance.amount}")

# Обновляем конкретный баланс
usdt_balance = next(b for b in balances if b.asset == "USDT")
print(f"Текущий USDT: {usdt_balance.amount}")

await usdt_balance.update()
print(f"Обновлённый USDT: {usdt_balance.amount}")

💡 Когда использовать Balance.update()?

После получения оплаты или создания/активации чека. Если ты работаешь через polling или webhook, обновляй баланс после каждого события, чтобы всегда иметь актуальные данные.

📊

Shortcut-методы ExchangeRate

Объект ExchangeRate представляет курс обмена одной валюты на другую. У него также есть метод update().

ExchangeRate.update()

Обновляет курс из API.

Python · Обновление курса
from aiosend import CryptoPay

cp = CryptoPay(token="1234:TOKEN")

# Получаем все курсы
rates = await cp.get_exchange_rates()

# Находим курс USDT → USD
usdt_usd = next(
    (r for r in rates if r.source == "USDT" and r.target == "USD"),
    None
)
if usdt_usd:
    print(f"1 USDT = {usdt_usd.rate} USD")
    print(f"Актуален: {usdt_usd.is_valid}")

    # Обновляем курс
    await usdt_usd.update()
    print(f"Новый курс: {usdt_usd.rate} USD")

⚠️ Курсы обновляются нечасто

Crypto Pay обновляет курсы не в реальном времени, а с некоторой периодичностью (обычно раз в несколько минут). Не ожидай, что update() даст другое значение, если прошло мало времени.

🚀

Полный пример: использование всех shortcut

Python · Все shortcut-методы в одном примере
import asyncio
from aiosend import CryptoPay

async def main():
    cp = CryptoPay(token="1234:TOKEN")

    # 1. INVOICE SHORTCUTS
    print("=== INVOICE ===")
    invoice = await cp.create_invoice(amount=10, asset="USDT")
    print(f"Создан: #{invoice.invoice_id}, статус: {invoice.status}")
    print(f"QR: {invoice.qr}")

    await invoice.update()
    print(f"Обновлён: статус={invoice.status}")

    # invoice.delete() — удалит, если не оплачен

    # 2. CHECK SHORTCUTS
    print("\n=== CHECK ===")
    check = await cp.create_check(amount=5, asset="TON")
    print(f"Создан чек: #{check.check_id}")
    print(f"QR: {check.qr}")
    print(f"Изображение: {check.get_image()}")

    await check.update()
    print(f"Обновлён: статус={check.status}")

    # 3. BALANCE SHORTCUTS
    print("\n=== BALANCE ===")
    balances = await cp.get_balances()
    for bal in balances:
        print(f"{bal.asset}: {bal.amount}")
        await bal.update()
        print(f"  → после update: {bal.amount}")

    # 4. EXCHANGE RATE SHORTCUTS
    print("\n=== EXCHANGE RATES ===")
    rates = await cp.get_exchange_rates()
    for rate in rates[:5]:  # только первые 5
        print(f"{rate.source} → {rate.target}: {rate.rate}")
        await rate.update()

    print("\nГотово!")

asyncio.run(main())
🔬

Как это работает внутри?

Каждый объект (Invoice, Check, Balance, ExchangeRate) хранит ссылку на клиента, через который он был получен. Это происходит в момент десериализации ответа API — aiosend передаёт клиент во все создаваемые объекты.

Python · Упрощённая реализация shortcut
class Invoice(BaseModel):
    invoice_id: int
    status: str
    # ... другие поля

    # Ссылка на клиента (устанавливается после создания)
    _client: CryptoPay | None = None

    async def update(self) -> None:
        """Обновляет данные инвойса из API."""
        if self._client is None:
            raise RuntimeError("Объект не привязан к клиенту")
        updated = await self._client.get_invoice(
            invoice_id=self.invoice_id
        )
        # Копируем все поля из обновлённого объекта
        for field in self.model_fields:
            setattr(self, field, getattr(updated, field))

    async def delete(self) -> bool:
        """Удаляет инвойс."""
        return await self._client.delete_invoice(
            invoice_id=self.invoice_id
        )

    @property
    def qr(self) -> str:
        """URL QR-кода."""
        return self._client.session.network.get_qr(
            self.bot_invoice_url
        )

    async def poll(self, on_paid=None, **kwargs):
        """Ожидает оплаты."""
        return await self._client.poll(
            invoice_id=self.invoice_id,
            on_paid=on_paid,
            **kwargs,
        )

🔍 Зачем это нужно?

Благодаря тому, что объекты хранят ссылку на клиента, ты можешь передавать их в любые части приложения, и они сохраняют способность делать API-запросы. Это очень удобно: например, ты можешь сохранить объект Invoice в БД, а потом восстановить и вызвать invoice.update() без доступа к исходному клиенту.

📌

Что важно запомнить

1️⃣
Shortcut-методы — удобная обёртка. Они вызывают соответствующие методы CryptoPay, используя сохранённую ссылку на клиента.
2️⃣
Invoice: update(), delete(), poll(), qr
3️⃣
Check: update(), delete(), poll(), qr, get_image()
4️⃣
Balance, ExchangeRate: update()
5️⃣
Объекты хранят ссылку на клиента. Поэтому их можно передавать в любые части приложения, и они сохраняют возможность делать API-запросы.
🎯

Практическое задание

Задание: Мониторинг счетов и чеков через shortcut

Напиши скрипт, который:

  • Создаёт 3 инвойса с разными суммами и assets
  • Для каждого выводит QR-код
  • Ожидает оплаты всех трёх через invoice.poll()
  • После оплаты обновляет баланс через balance.update()
  • Выводит финальный баланс по всем валютам

Подсказка:

Python · Шаблон решения
import asyncio
from aiosend import CryptoPay

async def monitor_payments():
    cp = CryptoPay(token="YOUR_TOKEN")

    # Создаём инвойсы
    invoices = []
    for amount, asset in [(10, "USDT"), (0.5, "TON"), (5, "USDT")]:
        inv = await cp.create_invoice(
            amount=amount, asset=asset,
            description=f"Тест {amount} {asset}",
        )
        invoices.append(inv)
        print(f"Создан: {inv.bot_invoice_url} | QR: {inv.qr}")

    # Ожидаем оплаты всех
    for inv in invoices:
        print(f"Ожидаем оплату #{inv.invoice_id}...")
        paid = await inv.poll()
        print(f"Оплачен: {paid.paid_amount} {paid.paid_asset}")

    # Обновляем баланс
    balances = await cp.get_balances()
    for bal in balances:
        await bal.update()
        print(f"Баланс {bal.asset}: {bal.amount}")

asyncio.run(monitor_payments())

Урок 7.2: Shortcut-методы

5 вопросов