Разные ID платежей YooKassa (Telegram Bot)
не понимаю, почему разные идентификаторы платежей
что не так в моем коде?
в логах такая ошибка:
2025-04-14 02:48:54,533 - main - ERROR - Ошибка проверки платежа extend: {'type': 'error', 'id': '0196318e-4ba1-77c8-85fb-913eb5708ec4', 'code': 'not_found', 'description': "Incorrect payment_id. Payment doesn't exist or access denied. Specify the payment ID created in your store.", 'parameter': 'payment_id'}
в БД тот самый payment_id, который нужен для проверки, в логах ЮКассы тот же payment_id, что и в БД. Не понимаю, где ошибка
@router.message(Command(commands=['extend']))
async def extend_key_handler(message: types.Message):
user_id = message.from_user.id
try:
# 1. Получаем текущий список клиентов из X-UI
inbound_info = await asyncio.to_thread(
xui_api.get_inbound_info,
INBOUND_ID
)
if not inbound_info.get('success'):
await message.answer("⚠️ Не удалось проверить ключи. Попробуйте позже.")
return
settings = json.loads(inbound_info['obj']['settings'])
xui_clients = {client['id']: client for client in settings.get('clients', [])}
# 2. Получаем ключи пользователя из базы
db_keys = await db.get_user_keys(user_id)
# 3. Фильтруем активные ключи (есть в X-UI и не истек срок)
current_time = int(datetime.now().timestamp() * 1000)
active_keys = []
for key in db_keys:
client = xui_clients.get(key['uuid'])
if client and client['expiryTime'] > current_time:
active_keys.append({
**key,
'expiry_time': client['expiryTime'] # Используем актуальное время из X-UI
})
if not active_keys:
await message.answer(
"У вас нет активных ключей для продления.\n"
"Для покупки нового ключа нажмите /buy"
)
return
# 4. Создаем клавиатуру с выбором ключей
keyboard = []
for key in active_keys:
expiry_date = datetime.fromtimestamp(key['expiry_time'] / 1000).strftime('%d.%m.%Y')
days_left = (datetime.fromtimestamp(key['expiry_time'] / 1000) - datetime.now()).days
keyboard.append([
InlineKeyboardButton(
text=f"Ключ до {expiry_date} ({days_left} дней осталось)",
callback_data=f"extend_{key['key_id']}"
)
])
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
await message.answer("Выберите ключ для продления:", reply_markup=reply_markup)
except Exception as e:
logger.error(f"Ошибка в extend_key_handler: {str(e)}")
await message.answer("⚠️ Произошла ошибка. Попробуйте позже.")
@router.callback_query(lambda c: c.data.startswith('extend_'))
async def process_extend_callback(callback: types.CallbackQuery):
try:
key_id = int(callback.data.split('_')[1])
user_id = callback.from_user.id
# Проверяем ключ в X-UI
inbound_info = await asyncio.to_thread(xui_api.get_inbound_info, INBOUND_ID)
if not inbound_info.get('success'):
await callback.answer("Ошибка проверки ключа", show_alert=True)
return
settings = json.loads(inbound_info['obj']['settings'])
clients = {c['id']: c for c in settings.get('clients', [])}
async with db.pool.acquire() as conn:
# Получаем ключ из базы
key = await conn.fetchrow(
"SELECT * FROM user_keys WHERE key_id = $1 AND user_id = $2",
key_id, user_id
)
if not key:
await callback.answer("Ключ не найден", show_alert=True)
return
# Проверяем активность ключа
client = clients.get(key['uuid'])
if not client or client['expiryTime'] <= int(datetime.now().timestamp() * 1000):
await callback.answer("Ключ неактивен или истёк", show_alert=True)
return
# Создаем платеж с уникальным idempotence_key
idempotence_key = str(uuid.uuid4())
try:
payment = Payment.create({
"amount": {
"value": PRICE,
"currency": "RUB"
},
"confirmation": {
"type": "redirect",
"return_url": "https://t.me/your_bot"
},
"capture": True,
"metadata": {
"user_id": user_id,
"key_id": key_id,
"action": "extend"
},
"description": f"Продление VPN на {SUBSCRIPTION_DAYS} дней"
}, idempotence_key)
payment_url = payment.confirmation.confirmation_url
payment_id = payment.id
# Сохраняем платеж в базу
await conn.execute(
"INSERT INTO key_extensions (user_id, key_id, payment_id, status) VALUES ($1, $2, $3, 'pending')", user_id,
key_id, payment_id
)
# Отправляем пользователю кнопки
markup = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text="? Оплатить", url=payment_url)],
[InlineKeyboardButton(text="? Проверить", callback_data=f"check_extend_{payment_id}")]
])
await callback.message.edit_text(
f"? Оплата продления на {SUBSCRIPTION_DAYS} дней\n"
f"Сумма: {PRICE} руб\n\n"
"Нажмите кнопку ниже для оплаты:",
reply_markup=markup
)
await callback.answer()
except Exception as e:
logger.error(f"Ошибка создания платежа: {str(e)}")
await callback.answer("Ошибка при создании платежа", show_alert=True)
except Exception as e:
logger.error(f"Ошибка в process_extend_callback: {str(e)}")
await callback.answer("Ошибка обработки запроса", show_alert=True)
@router.callback_query(lambda c: c.data.startswith('check_extend_'))
async def check_extend_payment(callback: types.CallbackQuery):
payment_id = callback.data.split('_')[2]
user_id = callback.from_user.id
try:
# Добавляем задержку перед проверкой
await asyncio.sleep(3)
# Получаем информацию о платеже
try:
payment = Payment.find_one(payment_id)
if not payment:
raise ValueError("Платеж не найден")
except Exception as e:
logger.error(f"Ошибка получения платежа {payment_id}: {str(e)}")
await callback.answer("Платеж не найден. Попробуйте позже.", show_alert=True)
return
# Обрабатываем разные статусы платежа
if payment.status == 'succeeded':
async with db.pool.acquire() as conn:
# Проверяем, не обработан ли уже платеж
extension = await conn.fetchrow(
"SELECT * FROM key_extensions WHERE payment_id = $1 AND status = 'pending' FOR UPDATE",
payment_id
)
if not extension:
await callback.answer("Этот платеж уже был обработан", show_alert=True)
return
# Получаем ключ
key = await conn.fetchrow(
"SELECT * FROM user_keys WHERE key_id = $1 AND user_id = $2",
extension['key_id'], user_id
)
if not key:
await callback.answer("Ключ не найден", show_alert=True)
return
# Обновляем срок действия
new_expiry = int((datetime.now() + timedelta(days=SUBSCRIPTION_DAYS)).timestamp() * 1000)
# Обновляем X-UI
inbound_info = await asyncio.to_thread(xui_api.get_inbound_info, INBOUND_ID)
settings = json.loads(inbound_info['obj']['settings'])
for client in settings['clients']:
if client['id'] == key['uuid']:
client['expiryTime'] = new_expiry
break
await asyncio.to_thread(
xui_api.update_inbound,
INBOUND_ID,
{
"settings": json.dumps(settings),
"streamSettings": inbound_info['obj']['streamSettings']
}
)
# Обновляем базу данных
await conn.execute(
"UPDATE user_keys SET expiry_time = $1 WHERE key_id = $2",
new_expiry, key['key_id']
)
await conn.execute(
"UPDATE key_extensions SET status = 'completed' WHERE payment_id = $1",
payment_id
)
# Уведомляем пользователя
expiry_date = datetime.fromtimestamp(new_expiry / 1000).strftime('%d.%m.%Y')
await callback.message.edit_text(
f"✅ Ключ продлен до {expiry_date}",
reply_markup=None
)
await callback.answer()
elif payment.status == 'pending':
await callback.answer("Платеж в обработке. Попробуйте позже.", show_alert=True)
else:
await callback.answer("Платеж не был завершен", show_alert=True)
except Exception as e:
logger.error(f"Ошибка проверки платежа: {str(e)}")
await callback.answer("Ошибка при проверке платежа", show_alert=True)