Шаг 1: Первый платёж с сохранением токена
1from yookassa import Payment
2
3# Первый платёж — сохраняем payment_method_id
4first_payment = Payment.create({
5 "amount": {"value": "990.00", "currency": "RUB"},
6 "confirmation": {"type": "redirect", "return_url": "https://t.me/your_bot"},
7 "capture": True,
8 "description": "Подписка — первый месяц",
9 "save_payment_method": True, # Сохраняем токен карты
10 "metadata": {
11 "user_id": "123456789",
12 "subscription": "monthly"
13 }
14})
15
16# После оплаты получаем payment_method_id из webhook
17# payment.payment_method.id — это токен для рекуррентов
Шаг 2: Автоматическое списание
1import asyncio
2from datetime import datetime
3
4async def charge_subscription(user_id: int, payment_method_id: str, amount: float):
5 """Автоматическое списание подписки"""
6 payment = Payment.create({
7 "amount": {"value": f"{amount:.2f}", "currency": "RUB"},
8 "capture": True,
9 "payment_method_id": payment_method_id, # Токен карты
10 "description": f"Подписка — {datetime.now().strftime('%B %Y')}",
11 "metadata": {
12 "user_id": str(user_id),
13 "type": "recurring"
14 }
15 })
16
17 if payment.status == 'succeeded':
18 await notify_user(user_id, "✅ Подписка продлена!")
19 await extend_subscription(user_id)
20 else:
21 await notify_user(user_id, "❌ Не удалось списать оплату. Обновите карту.")
Шаг 3: Планировщик (cron)
1from apscheduler.schedulers.asyncio import AsyncIOScheduler
2
3scheduler = AsyncIOScheduler()
4
5# Запуск каждый день в 10:00
6@scheduler.scheduled_job('cron', hour=10)
7async def process_subscriptions():
8 """Обработка подписок, срок которых истекает сегодня"""
9 subscriptions = await db.fetch(
10 "SELECT * FROM subscriptions WHERE expires_at::date = CURRENT_DATE"
11 )
12
13 for sub in subscriptions:
14 await charge_subscription(
15 user_id=sub['user_id'],
16 payment_method_id=sub['payment_method_id'],
17 amount=sub['amount']
18 )
19
20scheduler.start()