$ sudo teach IT
Модуль 4 · Checks — крипто-чеки

Урок 4.1 — Создание чеков

Научимся создавать крипто-чеки через aiosend: разберём метод create_check(), все его параметры, типы чеков, публичные и привязанные чеки, активацию и полный цикл работы.

Крипто-чек (Check) — это специальный инструмент Crypto Pay, который позволяет отправить фиксированную сумму криптовалюты другому пользователю. Получатель активирует чек и средства зачисляются на его баланс. В отличие от инвойсов (запрос на оплату), чек — это предложение средств. В этом уроке мы научимся создавать чеки, настраивать их параметры и управлять их статусом.

🏷️

Что такое крипто-чек в Crypto Pay

Крипто-чек — это предоплаченный ваучер на определённую сумму криптовалюты. Ты создаёшь чек, резервируя средства со своего баланса, и отправляешь ссылку получателю. Получатель активирует чек и получает средства на свой баланс в @CryptoBot.

Чеки бывают двух типов:

🌐 Публичный чек

  • Может активировать ЛЮБОЙ пользователь
  • Параметры pin_to_user_id и pin_to_username не указаны
  • Подходит для розыгрышей, бонусов, конкурсов
  • Кто первый активировал — тому и достались средства

📌 Привязанный чек

  • Может активировать ТОЛЬКО указанный пользователь
  • Привязка по pin_to_user_id (Telegram ID) или pin_to_username
  • Подходит для выплат, возвратов, наград конкретному пользователю
  • Никто другой не сможет забрать чек

Схема работы с чеками:

1. Включи функцию createCheck в настройках приложения Crypto Pay

2. Создай чек через await cp.create_check(amount=10, asset="USDT")

3. Получи объект Check со ссылкой и хешем

4. Отправь ссылку bot_check_url получателю

5. Получатель активирует чек в @CryptoBot

6. Средства списываются с твоего баланса и зачисляются получателю

Важное отличие от инвойсов: при создании чека средства сразу резервируются на твоём балансе. Ты не сможешь потратить эти деньги, пока чек не будет активирован или отменён. Поэтому убедись, что на балансе достаточно средств.

🔐

Включение createCheck в настройках

Прежде чем создавать чеки через API, нужно включить соответствующее разрешение в настройках твоего приложения Crypto Pay. По умолчанию метод createCheck отключён из соображений безопасности.

Как включить:

  1. Открой @CryptoBot (или @CryptoTestnetBot для тестов)
  2. Перейди в раздел Crypto Pay
  3. Выбери своё приложение (или создай новое)
  4. Перейди в Security → Checks
  5. Включи флаг createCheck
  6. Сохрани изменения

После этого метод create_check() станет доступен. Если попытаться создать чек без включённого разрешения, API вернёт ошибку.

⚠️ Проверь перед созданием!

Перед вызовом create_check() убедись, что:
• Функция createCheck включена в Security → Checks
• На балансе твоего приложения достаточно средств
• Ты используешь правильный токен (MAINNET/TESTNET)

Python · Проверка баланса перед созданием чека
import asyncio
from aiosend import CryptoPay

async def check_balance(cp: CryptoPay, asset: str = "USDT"):
    """Проверяет баланс приложения перед созданием чека."""
    me = await cp.get_me()
    print(f"Приложение: {me.name}")
    print(f"Балансы: {me.balances}")

    for balance in me.balances:
        if balance.currency_code == asset:
            available = float(balance.available)
            print(f"Доступно {asset}: {available}")
            return available

    print(f"Актив {asset} не найден на балансе")
    return 0

async def main():
    cp = CryptoPay(token="YOUR_TOKEN")
    balance = await check_balance(cp, "USDT")
    if balance > 10:
        print("✅ Баланса достаточно для создания чека")
    else:
        print("❌ Недостаточно средств")

asyncio.run(main())
✅

Метод create_check() — создание чека

Метод create_check() — основной способ создания крипто-чеков. Он доступен у экземпляра CryptoPay и у Network.

Python · Сигнатура метода
async def create_check(
    self,
    amount: str,
    asset: str,
    pin_to_user_id: int | None = None,
    pin_to_username: str | None = None,
) -> Check

Параметры:

Параметр Тип Обязательный Описание
amount str ✅ Да Сумма чека в криптовалюте. Передаётся строкой для сохранения точности (например "10.50").
asset str ✅ Да Криптовалюта чека (например "USDT", "TON").
pin_to_user_id int | None Нет Telegram ID пользователя, который сможет активировать чек. Если указан — чек привязан к этому пользователю.
pin_to_username str | None Нет Username получателя (без @). Чек сможет активировать только пользователь с этим username.

Важно: amount передаётся строкой!

В отличие от create_invoice(), где amount может быть float | str, в create_check() сумма принимается только как строка. Это связано с высокой точностью криптовалютных сумм — float может дать погрешность. Передавай сумму строкой: "10.50", "0.000001".

Python · Простейшее создание чека
import asyncio
from aiosend import CryptoPay

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

    # Публичный чек на 10 USDT — любой может активировать
    check = await cp.create_check(amount="10", asset="USDT")

    print(f"Чек #{check.check_id} создан!")
    print(f"Сумма: {check.amount} {check.asset}")
    print(f"Статус: {check.status}")
    print(f"Ссылка: {check.bot_check_url}")
    print(f"Хеш: {check.hash}")
    print(f"Тип: публичный (никому не привязан)")

asyncio.run(main())
📌

Привязанные чеки (pin_to_user_id / pin_to_username)

Если ты хочешь отправить чек конкретному пользователю (например, выплату или возврат), используй параметры pin_to_user_id или pin_to_username. Такой чек сможет активировать только указанный пользователь.

pin_to_user_id

Привязывает чек к Telegram ID пользователя. Это числовой идентификатор (например, 123456789). Самый надёжный способ — ID не меняется.

pin_to_username

Привязывает чек к username (без @). Например, "ivanov". Менее надёжно — пользователь может сменить username, и чек станет недоступным.

Python · Привязанные чеки
import asyncio
from aiosend import CryptoPay

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

    # 1. Чек, привязанный к Telegram ID
    check_by_id = await cp.create_check(
        amount="25",
        asset="USDT",
        pin_to_user_id=123456789,  # ID получателя
    )
    print(f"Чек для user_id=123456789: {check_by_id.bot_check_url}")

    # 2. Чек, привязанный к username
    check_by_username = await cp.create_check(
        amount="50",
        asset="TON",
        pin_to_username="ivanov",  # username без @
    )
    print(f"Чек для @ivanov: {check_by_username.bot_check_url}")

    # 3. Можно указать оба параметра (но достаточно одного)
    check_both = await cp.create_check(
        amount="100",
        asset="USDT",
        pin_to_user_id=987654321,
        pin_to_username="petrov",
    )
    print(f"Чек для user_id или @petrov: {check_both.bot_check_url}")

asyncio.run(main())

⚠️ Что если привязанный чек не активировали?

Средства остаются зарезервированными на твоём балансе, пока чек не будет активирован. Чек нельзя отменить или удалить через API (на момент написания aiosend). Средства вернутся на баланс только после активации чека получателем. Учитывай это при планировании выплат.

📄

Тип Check — все поля объекта

Метод create_check() возвращает объект Check (Pydantic-модель). Вот все его поля:

Поле Тип Описание
check_id int Уникальный ID чека в системе Crypto Pay
amount str Сумма чека (строка для сохранения точности)
asset str Криптовалюта чека (например "USDT")
status str Статус чека: "active" (активен) или "activated" (активирован)
hash str Уникальный хеш чека. Используется в ссылке для активации
bot_check_url str Ссылка на чек в @CryptoBot. Отправь её получателю
created_at str Дата создания чека в формате ISO 8601
activated_at str | None Дата активации чека (если активирован)
pin_to_user_id int | None Telegram ID пользователя, к которому привязан чек (если есть)
pin_to_username str | None Username, к которому привязан чек (если есть)
Python · Доступ к полям чека
import asyncio
from aiosend import CryptoPay

async def main():
    cp = CryptoPay(token="YOUR_TOKEN")
    check = await cp.create_check(amount="15", asset="USDT")

    print(f"ID чека:        {check.check_id}")
    print(f"Сумма:          {check.amount} {check.asset}")
    print(f"Статус:         {check.status}")
    print(f"Хеш:            {check.hash}")
    print(f"Ссылка:         {check.bot_check_url}")
    print(f"Создан:         {check.created_at}")
    print(f"Активирован:    {check.activated_at or '—'}")
    print(f"Привязан к ID:  {check.pin_to_user_id or '—'}")
    print(f"Привязан к @:   {check.pin_to_username or '—'}")

asyncio.run(main())
⚖️

Сравнение: публичный vs привязанный чек

Характеристика Публичный Привязанный (pin)
Кто может активировать Любой пользователь Только указанный пользователь
Параметры привязки pin_to_user_id=None pin_to_user_id=123 или pin_to_username="ivan"
Сценарий использования Розыгрыши, бонусы, конкурсы, крауддропы Выплаты, возвраты, зарплата, награды
Риски Кто угодно может забрать (нужно быстро отправить ссылку) При смене username чек становится недоступным
Рекомендация Для массовых раздач и промо-акций Для адресных выплат конкретным пользователям
Python · Публичный vs привязанный чек
import asyncio
from aiosend import CryptoPay

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

    # Публичный чек — любой может активировать
    public = await cp.create_check(amount="5", asset="USDT")
    print(f"Публичный: {public.bot_check_url}")
    print(f"  pin_to_user_id: {public.pin_to_user_id}")
    print(f"  pin_to_username: {public.pin_to_username}")
    # → pin_to_user_id = None, pin_to_username = None

    # Привязанный чек — только для user_id=123456
    pinned = await cp.create_check(
        amount="100",
        asset="USDT",
        pin_to_user_id=123456,
    )
    print(f"Привязанный: {pinned.bot_check_url}")
    print(f"  pin_to_user_id: {pinned.pin_to_user_id}")
    # → pin_to_user_id = 123456

    # Привязанный по username
    pinned_u = await cp.create_check(
        amount="25",
        asset="TON",
        pin_to_username="crypto_user",
    )
    print(f"Привязанный к @: {pinned_u.bot_check_url}")
    print(f"  pin_to_username: {pinned_u.pin_to_username}")
    # → pin_to_username = "crypto_user"

asyncio.run(main())
⚡

Активация чека получателем

После создания чека ты отправляешь получателю ссылку bot_check_url. Процесс активации выглядит так:

  1. Получатель открывает ссылку t.me/CryptoBot?start=check_XXXX
  2. @CryptoBot показывает информацию о чеке: сумма, валюта, отправитель
  3. Получатель нажимает «Активировать»
  4. Средства списываются с баланса отправителя и зачисляются получателю
  5. Статус чека меняется с "active" на "activated"
  6. В поле activated_at появляется дата активации

Ты можешь отслеживать статус чека через метод get_checks(), который мы подробно рассмотрим в следующем уроке.

Python · Мониторинг статуса чека
import asyncio
from aiosend import CryptoPay

async def monitor_check(cp: CryptoPay, check_id: int, timeout: int = 300):
    """Ожидает активации чека в течение timeout секунд."""
    import time
    start = time.time()

    while time.time() - start < timeout:
        checks = await cp.get_checks(check_ids=[check_id])
        if checks:
            check = checks[0]
            if check.status == "activated":
                print(f"✅ Чек #{check_id} активирован!")
                print(f"   Активирован в: {check.activated_at}")
                return check
            elif check.status == "active":
                print(f"⏳ Чек ещё активен, ждём...")
        await asyncio.sleep(2)

    print(f"❌ Таймаут ожидания активации чека #{check_id}")
    return None

async def main():
    cp = CryptoPay(token="YOUR_TOKEN")
    check = await cp.create_check(amount="10", asset="USDT")
    print(f"Отправьте ссылку получателю: {check.bot_check_url}")

    # Ждём активации (до 5 минут)
    activated = await monitor_check(cp, check.check_id)

asyncio.run(main())

💡 Разница с инвойсами

В инвойсах ты получаешь средства от покупателя. В чеках ты отдаёшь средства получателю. Чеки всегда создаются с твоего баланса. Комиссия за создание чека обычно ниже, чем комиссия за перевод (transfer).

🧩

Практические примеры

Пример 1: Массовая раздача (airdrop)

Создаём несколько публичных чеков для розыгрыша среди подписчиков.

Python · Airdrop публичных чеков
import asyncio
from aiosend import CryptoPay

async def create_airdrop(cp: CryptoPay, total_amount: str, asset: str, count: int):
    """
    Создаёт несколько чеков на равные суммы для розыгрыша.
    total_amount — общая сумма на все чеки.
    """
    total = float(total_amount)
    per_check = str(round(total / count, 8))

    checks = []
    for i in range(count):
        check = await cp.create_check(
            amount=per_check,
            asset=asset,
        )
        checks.append(check)
        print(f"Чек #{i+1}: {check.bot_check_url}")

    print(f"\nСоздано {len(checks)} чеков по {per_check} {asset}")
    print(f"Общая сумма: {total_amount} {asset}")
    return checks

async def main():
    cp = CryptoPay(token="YOUR_TOKEN")
    # Раздаём 100 USDT: 10 чеков по 10 USDT
    await create_airdrop(cp, "100", "USDT", 10)

asyncio.run(main())

Пример 2: Выплата пользователю

Отправляем привязанный чек конкретному пользователю по его Telegram ID.

Python · Адресная выплата
import asyncio
from aiosend import CryptoPay

async def payout(cp: CryptoPay, user_id: int, amount: str, asset: str):
    """Создаёт привязанный чек для выплаты пользователю."""
    check = await cp.create_check(
        amount=amount,
        asset=asset,
        pin_to_user_id=user_id,
    )

    result = {
        "check_id": check.check_id,
        "amount": check.amount,
        "asset": check.asset,
        "status": check.status,
        "url": check.bot_check_url,
        "user_id": check.pin_to_user_id,
        "created_at": check.created_at,
    }

    print(f"💸 Выплата {amount} {asset} пользователю #{user_id}")
    print(f"   Чек: {check.bot_check_url}")
    print(f"   Статус: {check.status}")
    return result

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

    # Выплата бонуса пользователю с ID 123456789
    await payout(
        cp=cp,
        user_id=123456789,
        amount="50",
        asset="USDT",
    )

asyncio.run(main())

Пример 3: Система бонусов с проверкой баланса

Проверяем баланс, создаём чек, логируем результат.

Python · Бонус с валидацией
import asyncio
import json
from aiosend import CryptoPay
from aiosend.exceptions import APIError, MethodValuesError

class BonusSystem:
    """Система бонусов через крипто-чеки."""

    def __init__(self, token: str):
        self.cp = CryptoPay(token=token)

    async def get_asset_balance(self, asset: str) -> float:
        """Возвращает доступный баланс по активу."""
        me = await self.cp.get_me()
        for balance in me.balances:
            if balance.currency_code == asset:
                return float(balance.available)
        return 0.0

    async def send_bonus(
        self,
        user_id: int,
        amount: str,
        asset: str,
    ) -> dict:
        """Отправляет бонус пользователю. Проверяет баланс."""
        # 1. Проверка баланса
        balance = await self.get_asset_balance(asset)
        if balance < float(amount):
            return {
                "success": False,
                "error": f"Недостаточно {asset}. Доступно: {balance}",
            }

        # 2. Создание чека
        try:
            check = await self.cp.create_check(
                amount=amount,
                asset=asset,
                pin_to_user_id=user_id,
            )

            result = {
                "success": True,
                "check_id": check.check_id,
                "amount": check.amount,
                "asset": check.asset,
                "status": check.status,
                "url": check.bot_check_url,
                "user_id": check.pin_to_user_id,
                "created_at": check.created_at,
                "balance_after": await self.get_asset_balance(asset),
            }
            return result

        except MethodValuesError as e:
            return {"success": False, "error": f"Ошибка параметров: {e}"}
        except APIError as e:
            return {"success": False, "error": f"Ошибка API: {e}"}

# Использование
async def main():
    bonus = BonusSystem("YOUR_TOKEN")
    result = await bonus.send_bonus(
        user_id=123456789,
        amount="25",
        asset="USDT",
    )
    print(json.dumps(result, indent=2, ensure_ascii=False))

asyncio.run(main())
⚠️

Граничные случаи и ограничения

Недостаточно средств на балансе

При создании чека средства резервируются с твоего баланса. Если на балансе недостаточно средств, API вернёт ошибку. Всегда проверяй баланс перед созданием чека, особенно если сумма большая.

createCheck не включён

Если функция createCheck отключена в настройках приложения (Security → Checks), API вернёт ошибку при попытке создания чека. Включи её через @CryptoBot перед использованием.

Смена username после привязки

Если ты привязал чек к username (pin_to_username), а пользователь сменил свой username, чек станет недоступным для активации. Рекомендуется использовать pin_to_user_id — Telegram ID не меняется.

Минимальная сумма чека

Каждый актив имеет минимальную сумму чека. Например, для USDT это обычно 1 USDT, для TON — 0.1 TON. Точные лимиты можно получить через get_currencies(). При попытке создать чек на меньшую сумму API вернёт ошибку.

Чек нельзя отменить

На данный момент API Crypto Pay не предоставляет метод для отмены или удаления чека. После создания чек будет висеть в статусе active до активации (или «навсегда»). Средства заблокированы на балансе до активации. Учитывай это при создании чеков.

📌

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

1️⃣
create_check(amount, asset) — основной метод. amount только строка! Обязательные параметры: amount и asset.
2️⃣
Публичный чек — может активировать любой. Привязанный чек — только указанный пользователь (через pin_to_user_id или pin_to_username).
3️⃣
Включи createCheck в настройках приложения (@CryptoBot → Crypto Pay → Security → Checks), иначе метод не сработает.
4️⃣
Средства резервируются сразу при создании чека. Убедись, что на балансе достаточно средств.
5️⃣
Чек нельзя отменить. После создания чек будет активен до активации. Средства заблокированы на балансе.
6️⃣
Тип Check содержит поля: check_id, amount, asset, status, hash, bot_check_url, created_at, activated_at, pin_to_user_id, pin_to_username.
7️⃣
pin_to_user_id надёжнее, чем pin_to_username. ID не меняется, username может быть изменён.
🎯

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

Задание: Система бонусов для подписчиков

Создай класс BonusDistributor, который:

  • Принимает список Telegram ID и сумму бонуса для каждого
  • Проверяет баланс перед созданием всех чеков
  • Создаёт привязанные чеки для каждого пользователя
  • Ведёт лог: успешные и неуспешные создания
  • Возвращает словарь с результатами: user_id → check_url или ошибка
  • Обрабатывает исключения для каждого пользователя отдельно
  • Подсчитывает итоговую сумму и комиссию

Подсказка:

Python · Шаблон решения
import asyncio
import json
from aiosend import CryptoPay
from aiosend.exceptions import APIError, MethodValuesError

class BonusDistributor:
    """Распределитель бонусов для списка пользователей."""

    def __init__(self, token: str):
        self.cp = CryptoPay(token=token)

    async def distribute(
        self,
        user_ids: list[int],
        amount: str,
        asset: str,
    ) -> dict:
        """
        Создаёт привязанные чеки для каждого пользователя.
        Возвращает словарь с результатами.
        """
        # 1. Проверка баланса
        me = await self.cp.get_me()
        balances = {b.currency_code: float(b.available) for b in me.balances}
        total_needed = float(amount) * len(user_ids)
        available = balances.get(asset, 0)

        if available < total_needed:
            return {
                "success": False,
                "error": (
                    f"Недостаточно {asset}. "
                    f"Нужно: {total_needed}, доступно: {available}"
                ),
            }

        # 2. Создание чеков
        results = {}
        success_count = 0
        fail_count = 0

        for uid in user_ids:
            try:
                check = await self.cp.create_check(
                    amount=amount,
                    asset=asset,
                    pin_to_user_id=uid,
                )
                results[str(uid)] = {
                    "success": True,
                    "check_id": check.check_id,
                    "url": check.bot_check_url,
                    "status": check.status,
                }
                success_count += 1
            except (APIError, MethodValuesError) as e:
                results[str(uid)] = {
                    "success": False,
                    "error": str(e),
                }
                fail_count += 1

        # 3. Итог
        summary = {
            "success": True,
            "asset": asset,
            "amount_per_user": amount,
            "total_users": len(user_ids),
            "success_count": success_count,
            "fail_count": fail_count,
            "total_amount": str(total_needed),
            "results": results,
        }

        print(json.dumps(summary, indent=2, ensure_ascii=False))
        return summary

# Использование
async def main():
    distributor = BonusDistributor("YOUR_TOKEN")

    # Раздаём бонусы по 10 USDT трем пользователям
    await distributor.distribute(
        user_ids=[111111, 222222, 333333],
        amount="10",
        asset="USDT",
    )

asyncio.run(main())
🔍

Метод get_checks() — получение списка чеков

Для получения информации о созданных чеках используется метод get_checks(). Он позволяет фильтровать чеки по статусу, активам и ID. Мы подробно рассмотрим его в следующем уроке, но базовое использование полезно знать уже сейчас.

Python · Сигнатура метода
async def get_checks(
    self,
    asset: str | None = None,
    check_ids: list[int] | None = None,
    status: str | None = None,
    offset: int = 0,
    count: int = 100,
) -> list[Check]
Python · Получение и фильтрация чеков
import asyncio
from aiosend import CryptoPay, CheckStatus

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

    check = await cp.create_check(amount="10", asset="USDT")
    print(f"Чек #{check.check_id}: статус={check.status}")

    checks = await cp.get_checks(check_ids=[check.check_id])
    if checks:
        c = checks[0]
        print(f"Получен чек #{c.check_id}: {c.amount} {c.asset}, статус: {c.status}")

    active = await cp.get_checks(status=CheckStatus.ACTIVE)
    print(f"Активных чеков: {len(active)}")

    activated = await cp.get_checks(status=CheckStatus.ACTIVATED)
    print(f"Активированных: {len(activated)}")

    usdt_checks = await cp.get_checks(asset="USDT")
    print(f"Чеков в USDT: {len(usdt_checks)}")

asyncio.run(main())

Статусы чеков (enum CheckStatus)

Чек может находиться в одном из двух статусов: CheckStatus.ACTIVE (ожидает активации) или CheckStatus.ACTIVATED (активирован). В отличие от инвойсов, у чеков нет статуса EXPIRED или PAID.

🛡️

Безопасность при работе с чеками

Чеки оперируют реальными средствами. Соблюдай несколько правил безопасности:

Не передавай bot_check_url третьим лицам

Ссылка на чек — это ваучер. Любой может активировать публичный чек. Для адресных выплат используй pin_to_user_id.

Проверяй баланс перед созданием

Создание чека резервирует средства. Проверяй баланс перед массовыми операциями.

Тестируй в TESTNET перед MAINNET

Используй тестовую сеть для отладки. Когда логика отлажена — смени токен на MAINNET.

Логируй все операции

Веди журнал всех созданных чеков: ID, сумма, актив, получатель, статус. Это поможет при аудите.

Python · Безопасная обёртка
import asyncio
import logging
from aiosend import CryptoPay
from aiosend.exceptions import APIError, MethodValuesError

logger = logging.getLogger("check_safe")

async def create_check_safe(
    cp: CryptoPay,
    amount: str,
    asset: str,
    pin_to_user_id: int | None = None,
) -> dict:
    result = {"success": False, "check_id": None, "url": None, "error": None}

    try:
        amount_float = float(amount)
        if amount_float <= 0:
            result["error"] = "Сумма должна быть положительной"
            return result
    except ValueError:
        result["error"] = "Некорректный формат суммы"
        return result

    try:
        me = await cp.get_me()
        balances = {b.currency_code: float(b.available) for b in me.balances}
        available = balances.get(asset, 0)
        if available < amount_float:
            result["error"] = f"Недостаточно {asset}: нужно {amount}, доступно {available}"
            return result
    except APIError as e:
        result["error"] = f"Ошибка проверки баланса: {e}"
        return result

    try:
        kwargs = {"amount": amount, "asset": asset}
        if pin_to_user_id is not None:
            kwargs["pin_to_user_id"] = pin_to_user_id
        check = await cp.create_check(**kwargs)
        result.update({
            "success": True, "check_id": check.check_id, "url": check.bot_check_url,
            "amount": check.amount, "asset": check.asset, "status": check.status,
        })
        logger.info(f"Чек создан: #{check.check_id} {check.amount} {check.asset}")
    except MethodValuesError as e:
        result["error"] = f"Ошибка параметров: {e}"
    except APIError as e:
        result["error"] = f"Ошибка API: {e}"

    return result

Урок 4.1: Создание чеков

5 вопросов