Refactor subscription renewal process to utilize panel webhook
- Removed the recurring billing task from the bot, shifting the auto-renew functionality to the panel webhook service, which now triggers renewals 24 hours before expiry. - Updated service dependencies to wire the subscription service with the panel webhook for seamless renewal handling. - Adjusted the Subscription model to enable auto-renew by default, enhancing subscription management.
This commit is contained in:
@@ -54,6 +54,15 @@ def build_core_services(
|
|||||||
settings_obj=settings,
|
settings_obj=settings,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Wire services that depend on each other
|
||||||
|
try:
|
||||||
|
# Attach YooKassa to subscription service for auto-renew charges
|
||||||
|
setattr(subscription_service, "yookassa_service", yookassa_service)
|
||||||
|
# Allow panel webhook to trigger renewals through subscription service
|
||||||
|
setattr(panel_webhook_service, "subscription_service", subscription_service)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"panel_service": panel_service,
|
"panel_service": panel_service,
|
||||||
"subscription_service": subscription_service,
|
"subscription_service": subscription_service,
|
||||||
|
|||||||
+1
-30
@@ -293,36 +293,7 @@ async def run_bot(settings_param: Settings):
|
|||||||
|
|
||||||
main_tasks.append(asyncio.create_task(web_server_task(), name="AIOHTTPServerTask"))
|
main_tasks.append(asyncio.create_task(web_server_task(), name="AIOHTTPServerTask"))
|
||||||
|
|
||||||
async def recurring_billing_task():
|
# Recurring billing moved to panel webhook (24h before expiry). No periodic task needed here.
|
||||||
# Run periodic check to bill 1 day before expiry
|
|
||||||
async_session_factory = dp.get("async_session_factory")
|
|
||||||
subscription_service = dp.get("subscription_service")
|
|
||||||
if not async_session_factory or not subscription_service:
|
|
||||||
logging.warning("Recurring billing task: dependencies missing; task not started")
|
|
||||||
return
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
async with async_session_factory() as session:
|
|
||||||
# Find subscriptions ending in 1 day
|
|
||||||
subs = await subscription_service.get_subscriptions_ending_soon(session, 1)
|
|
||||||
# We need actual Subscription objects; reuse DAL directly
|
|
||||||
from db.dal import subscription_dal
|
|
||||||
subs_models = await subscription_dal.get_subscriptions_near_expiration(session, 1)
|
|
||||||
handled = 0
|
|
||||||
for sub in subs_models:
|
|
||||||
try:
|
|
||||||
ok = await subscription_service.charge_subscription_renewal(session, sub)
|
|
||||||
handled += 1 if ok else 0
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Auto-renew attempt failed")
|
|
||||||
if handled:
|
|
||||||
await session.commit()
|
|
||||||
except Exception:
|
|
||||||
logging.exception("Recurring billing iteration failed")
|
|
||||||
# Sleep 1 hour between scans
|
|
||||||
await asyncio.sleep(3600)
|
|
||||||
|
|
||||||
main_tasks.append(asyncio.create_task(recurring_billing_task(), name="RecurringBillingTask"))
|
|
||||||
|
|
||||||
logging.info("Starting bot in Webhook mode with AIOHTTP server...")
|
logging.info("Starting bot in Webhook mode with AIOHTTP server...")
|
||||||
logging.info(f"Starting bot with main tasks: {[task.get_name() for task in main_tasks]}")
|
logging.info(f"Starting bot with main tasks: {[task.get_name() for task in main_tasks]}")
|
||||||
|
|||||||
@@ -185,6 +185,26 @@ class PanelWebhookService:
|
|||||||
|
|
||||||
if event_name in EVENT_MAP:
|
if event_name in EVENT_MAP:
|
||||||
days_left, msg_key = EVENT_MAP[event_name]
|
days_left, msg_key = EVENT_MAP[event_name]
|
||||||
|
if days_left == 1:
|
||||||
|
# Trigger auto-renew via SubscriptionService (wired in at factory)
|
||||||
|
try:
|
||||||
|
subscription_service = getattr(self, "subscription_service", None)
|
||||||
|
if subscription_service:
|
||||||
|
async with self.async_session_factory() as session:
|
||||||
|
from db.dal import subscription_dal
|
||||||
|
sub = await subscription_dal.get_active_subscription_by_user_id(session, user_id)
|
||||||
|
if sub and sub.auto_renew_enabled and sub.provider != 'tribute':
|
||||||
|
try:
|
||||||
|
ok = await subscription_service.charge_subscription_renewal(session, sub)
|
||||||
|
if ok:
|
||||||
|
await session.commit()
|
||||||
|
else:
|
||||||
|
await session.rollback()
|
||||||
|
except Exception:
|
||||||
|
await session.rollback()
|
||||||
|
logging.exception("Auto-renew attempt (24h) failed")
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Auto-renew trigger (24h) failed pre-check")
|
||||||
if days_left <= self.settings.SUBSCRIPTION_NOTIFY_DAYS_BEFORE:
|
if days_left <= self.settings.SUBSCRIPTION_NOTIFY_DAYS_BEFORE:
|
||||||
await self._send_message(
|
await self._send_message(
|
||||||
user_id,
|
user_id,
|
||||||
|
|||||||
@@ -509,6 +509,7 @@ class SubscriptionService:
|
|||||||
"traffic_limit_bytes": self.settings.user_traffic_limit_bytes,
|
"traffic_limit_bytes": self.settings.user_traffic_limit_bytes,
|
||||||
"provider": provider,
|
"provider": provider,
|
||||||
"skip_notifications": provider == "tribute" and self.settings.TRIBUTE_SKIP_NOTIFICATIONS,
|
"skip_notifications": provider == "tribute" and self.settings.TRIBUTE_SKIP_NOTIFICATIONS,
|
||||||
|
"auto_renew_enabled": True,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
new_or_updated_sub = await subscription_dal.upsert_subscription(
|
new_or_updated_sub = await subscription_dal.upsert_subscription(
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ class Subscription(Base):
|
|||||||
last_notification_sent = Column(DateTime(timezone=True), nullable=True)
|
last_notification_sent = Column(DateTime(timezone=True), nullable=True)
|
||||||
provider = Column(String, nullable=True)
|
provider = Column(String, nullable=True)
|
||||||
skip_notifications = Column(Boolean, default=False)
|
skip_notifications = Column(Boolean, default=False)
|
||||||
auto_renew_enabled = Column(Boolean, default=False, index=True)
|
auto_renew_enabled = Column(Boolean, default=True, index=True)
|
||||||
|
|
||||||
user = relationship("User", back_populates="subscriptions")
|
user = relationship("User", back_populates="subscriptions")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user