diff --git a/backend/bot/services/subscription_service_impl/lifecycle.py b/backend/bot/services/subscription_service_impl/lifecycle.py index 3f8102e..e4154ad 100644 --- a/backend/bot/services/subscription_service_impl/lifecycle.py +++ b/backend/bot/services/subscription_service_impl/lifecycle.py @@ -118,9 +118,21 @@ class SubscriptionLifecycleMixin: include_premium=not bool(updated.premium_is_limited), ) panel_payload.update(self._panel_identity_payload_for_user(db_user)) - await self.panel_service.update_user_details_on_panel( + updated_panel = await self.panel_service.update_user_details_on_panel( db_user.panel_user_uuid, panel_payload ) + if not updated_panel or updated_panel.get("error"): + # The tariff row is already swapped locally; if the panel rejects + # the squad/limit update the user sees the new tariff in the app + # but stays on the old squads on Remnawave. Surface the failure + # so the caller can roll back. + logging.warning( + "Panel user details update FAILED for tariff switch user %s -> %s. Response: %s", + user_id, + target.key, + updated_panel, + ) + return None if converted_bytes: await tariff_dal.create_traffic_topup( session, diff --git a/backend/bot/services/subscription_service_impl/traffic.py b/backend/bot/services/subscription_service_impl/traffic.py index 13cd3d1..ed5d88e 100644 --- a/backend/bot/services/subscription_service_impl/traffic.py +++ b/backend/bot/services/subscription_service_impl/traffic.py @@ -249,9 +249,18 @@ class TrafficMixin: include_premium=not bool(getattr(updated_sub, "premium_is_limited", False)), ) panel_payload.update(self._panel_identity_payload_for_user(db_user)) - await self.panel_service.update_user_details_on_panel( + updated_panel = await self.panel_service.update_user_details_on_panel( db_user.panel_user_uuid, panel_payload ) + if not updated_panel or updated_panel.get("error"): + # Otherwise the user pays for top-up bytes that are recorded locally + # but never reach Remnawave — they cannot actually use the traffic. + logging.warning( + "Panel user details update FAILED for traffic top-up user %s. Response: %s", + user_id, + updated_panel, + ) + return None await tariff_dal.create_traffic_topup( session, subscription_id=sub.subscription_id, @@ -347,9 +356,19 @@ class TrafficMixin: include_premium=not premium_is_limited, ), } - await self.panel_service.update_user_details_on_panel( + updated_panel = await self.panel_service.update_user_details_on_panel( db_user.panel_user_uuid, panel_payload ) + if not updated_panel or updated_panel.get("error"): + # Otherwise the user pays for premium top-up but the panel never + # re-grants premium squad access (the most common case here is + # transitioning from premium_is_limited=True back to False). + logging.warning( + "Panel user details update FAILED for premium top-up user %s. Response: %s", + user_id, + updated_panel, + ) + return None await tariff_dal.create_traffic_topup( session, subscription_id=sub.subscription_id,