feat(withdraw): add user withdrawal flow using CryptoPay
This commit is contained in:
+74
-29
@@ -1,11 +1,12 @@
|
||||
from aiogram import F, Router
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram.types import LinkPreviewOptions, CallbackQuery, Message
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
|
||||
from config import ADMIN_ID, BOT_USERNAME
|
||||
from db.storage import load_config, load_stats
|
||||
from db.storage import load_config, load_stats, save_config
|
||||
from db.users import get, register, save
|
||||
from keyboards.user import back_kb
|
||||
from keyboards.user import back_kb, activate_kb
|
||||
from runtime import get_bot, get_log_bot
|
||||
from services.withdrawals import create_crypto_check
|
||||
from utils.logging import log_admin, safe_edit
|
||||
@@ -13,6 +14,10 @@ from utils.logging import log_admin, safe_edit
|
||||
router = Router(name="user")
|
||||
|
||||
|
||||
class WithdrawState(StatesGroup):
|
||||
waiting_for_amount = State()
|
||||
|
||||
|
||||
def _get_profile(callback: CallbackQuery):
|
||||
profile = get(callback.from_user.id)
|
||||
if profile is None:
|
||||
@@ -77,6 +82,7 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
|
||||
await state.clear()
|
||||
profile = _get_profile(callback)
|
||||
cfg = load_config()
|
||||
|
||||
if profile.balance < cfg.min_withdraw:
|
||||
await safe_edit(
|
||||
callback,
|
||||
@@ -87,37 +93,76 @@ async def cb_withdraw(callback: CallbackQuery, state: FSMContext):
|
||||
)
|
||||
await callback.answer()
|
||||
return
|
||||
if profile.balance > cfg.treasury:
|
||||
await safe_edit(
|
||||
callback,
|
||||
f"⚠️ Недостаточно средств в казне: <b>{cfg.treasury:.2f} ₽</b>.",
|
||||
reply_markup=back_kb(),
|
||||
)
|
||||
await callback.answer()
|
||||
return
|
||||
|
||||
await callback.answer("⏳ Создаю чек...")
|
||||
await safe_edit(callback, "⏳ Создаю CryptoBot-чек, подождите...")
|
||||
amount = profile.balance
|
||||
check_url = await create_crypto_check(amount)
|
||||
if not check_url:
|
||||
await safe_edit(callback, "❌ Не удалось создать чек. Попробуйте позже.", reply_markup=back_kb())
|
||||
return
|
||||
|
||||
profile.balance = 0.0
|
||||
save(profile.user_id, profile)
|
||||
cfg.treasury = max(0.0, cfg.treasury - amount)
|
||||
from db.storage import save_config
|
||||
|
||||
save_config(cfg)
|
||||
await safe_edit(
|
||||
callback,
|
||||
f"✅ Чек создан на сумму <b>{amount:.2f} ₽</b>.\n"
|
||||
f"<a href='{check_url}'>Активировать чек</a>",
|
||||
f"💳 <b>Вывод средств</b>\n\n"
|
||||
f"Ваш баланс: <b>{profile.balance:.2f} ₽</b>\n"
|
||||
f"Минимум: <b>{cfg.min_withdraw:.2f} ₽</b>\n\n"
|
||||
f"Введите сумму для вывода:",
|
||||
reply_markup=back_kb(),
|
||||
disable_web_page_preview=True,
|
||||
)
|
||||
bot = get_bot() or callback.message.bot
|
||||
await state.set_state(WithdrawState.waiting_for_amount)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.message(WithdrawState.waiting_for_amount)
|
||||
async def process_withdraw_amount(message: Message, state: FSMContext):
|
||||
profile = _get_profile(message)
|
||||
cfg = load_config()
|
||||
|
||||
try:
|
||||
amount = float(message.text)
|
||||
except ValueError:
|
||||
await message.answer("❌ Введите корректную сумму числом.")
|
||||
return
|
||||
|
||||
if amount <= 0:
|
||||
await message.answer("❌ Сумма должна быть больше нуля.")
|
||||
return
|
||||
|
||||
if amount < cfg.min_withdraw:
|
||||
await message.answer(f"❌ Минимум для вывода: <b>{cfg.min_withdraw:.2f} ₽</b>.")
|
||||
return
|
||||
|
||||
if amount > profile.balance:
|
||||
await message.answer(
|
||||
f"❌ Недостаточно средств. Ваш баланс: <b>{profile.balance:.2f} ₽</b>."
|
||||
)
|
||||
return
|
||||
|
||||
if amount > cfg.treasury:
|
||||
await message.answer(
|
||||
f"❌ Недостаточно средств в казне: <b>{cfg.treasury:.2f} ₽</b>."
|
||||
)
|
||||
return
|
||||
|
||||
await state.clear()
|
||||
await message.answer("⏳ Создаю CryptoBot-чек, подождите...")
|
||||
|
||||
check_url, check_image_url = await create_crypto_check(amount)
|
||||
if not check_url:
|
||||
await message.answer(
|
||||
"❌ Не удалось создать чек. Попробуйте позже.",
|
||||
reply_markup=back_kb(),
|
||||
)
|
||||
return
|
||||
|
||||
profile.balance -= amount
|
||||
save(profile.user_id, profile)
|
||||
cfg.treasury = max(0.0, cfg.treasury - amount)
|
||||
|
||||
save_config(cfg)
|
||||
|
||||
await message.answer(
|
||||
text=f"🦋 Чек на <b>{amount:.2f} ₽</b>.",
|
||||
reply_markup=activate_kb(url=check_url, amount=amount),
|
||||
link_preview_options=LinkPreviewOptions(
|
||||
url=check_image_url, show_above_text=True
|
||||
),
|
||||
)
|
||||
|
||||
bot = get_bot() or message.bot
|
||||
await log_admin(
|
||||
bot,
|
||||
ADMIN_ID,
|
||||
|
||||
Reference in New Issue
Block a user