feat: настоящая оплата Telegram Stars вместо имитации
- payments.py: sendInvoice (XTR, 100 звёзд) -> pre_checkout_query -> successful_payment - /subscribe больше не активирует подписку сам — только отправляет счёт - handle_pre_checkout подтверждает только свой payload в XTR на 100 звёзд - таблица payments по telegram_payment_charge_id защищает от двойного начисления - 16 новых тестов платежей, старый тест заглушки переписан; 75 тестов OK - Dockerfile копирует payments.py, VERSION 0.1.0+25
This commit is contained in:
1 parent
b586d3aa36
commit
3c8917c20a
8 files changed
+424
-29
No files matched your search
@@ -14,10 +14,12 @@ from aiogram.client.telegram import TEST
|
||||
from aiogram.filters import CommandStart
|
||||
from aiogram.types import CallbackQuery, InputRichMessage, Message
|
||||
|
||||
import payments
|
||||
import user_store
|
||||
|
||||
from apinex_client import APINEX_STT_URL, POST_HISTORY_CHAR_LIMIT, POST_HISTORY_LIMIT, classify_intent, create_tone_profile, extract_posts_from_images, extract_transcript, generate_ideas, generate_posts, transcribe_audio
|
||||
from help_message import WELCOME_RICH_HTML, help_plain_view, help_view, rich_to_plain
|
||||
from payments import send_subscription_invoice
|
||||
from user_store import (
|
||||
add_post_samples,
|
||||
clear_user_data,
|
||||
@@ -36,7 +38,6 @@ from user_store import (
|
||||
SUBSCRIPTION_PRICE_STARS,
|
||||
ensure_subscription,
|
||||
subscription_row,
|
||||
activate_subscription,
|
||||
)
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
@@ -210,16 +211,22 @@ async def subscribe_command(message: Message) -> None:
|
||||
await message.answer("Не удалось определить пользователя.")
|
||||
return
|
||||
await asyncio.to_thread(ensure_subscription, message.from_user.id)
|
||||
try:
|
||||
await send_subscription_invoice(
|
||||
message.bot, message.chat.id, message.message_thread_id
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.error("Invoice send failed (%s): %s", type(exc).__name__, str(exc)[:180])
|
||||
if DEPLOY_ENV == "development":
|
||||
await report_error(message, "отправка счёта на оплату", exc)
|
||||
await message.answer(
|
||||
"Не получилось открыть счёт на оплату. Попробуй ещё раз через минуту "
|
||||
"или напиши поддержке."
|
||||
)
|
||||
return
|
||||
await message.answer(
|
||||
f"Активирую подписку на 30 дней. "
|
||||
f"Для оплаты пришли {SUBSCRIPTION_PRICE_STARS} звёзд в этом чате.\n\n"
|
||||
f"Жду оплаты..."
|
||||
)
|
||||
days = await asyncio.to_thread(activate_subscription, message.from_user.id, months=1)
|
||||
await asyncio.sleep(0.6)
|
||||
await message.answer(
|
||||
f"Подписка активирована на {days} дн. Спасибо за оплату {SUBSCRIPTION_PRICE_STARS} звёзд!\n\n"
|
||||
f"Теперь доступны генерация постов, идей и голосовые."
|
||||
f"Счёт на {SUBSCRIPTION_PRICE_STARS} звёзд на 30 дней выше.\n\n"
|
||||
f"Подписка активируется сразу после оплаты — отдельного подтверждения не нужно."
|
||||
)
|
||||
|
||||
|
||||
@@ -517,6 +524,7 @@ async def handle_text(message: Message) -> None:
|
||||
|
||||
async def main() -> None:
|
||||
init_db()
|
||||
payments.init_payments_table()
|
||||
token = os.environ.get("BOT_TOKEN", "").strip()
|
||||
if not token:
|
||||
raise RuntimeError("BOT_TOKEN is required")
|
||||
@@ -526,6 +534,7 @@ async def main() -> None:
|
||||
bot = Bot(token=token, session=session)
|
||||
await notify_development_deploy(bot)
|
||||
dp = Dispatcher()
|
||||
dp.include_router(payments.router)
|
||||
dp.include_router(router)
|
||||
|
||||
try:
|
||||
|
||||
Reference in new issue
Block a user