Improved error handling for payment switching

This commit is contained in:
SimplifiedPrivacy 2026-10-19 19:42:44 -04:00
parent 8938643338
commit 817b797701
7 changed files with 73 additions and 24 deletions

View file

@ -13,3 +13,7 @@ def empty_filler(result: Result) -> Result:
def evaluate_bad_state() -> Result:
"""Start core's evaluation of state with a GUI observer."""
return coordinate_evaluation(connection_observer=connection_observer)
@register_action(ResultError.SUBSCRIPTION)
def navigate_to_new_sub_page() -> Result:
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="id")

View file

@ -21,6 +21,7 @@ from gui.v2.infrastructure.setup_observers import (
application_version_observer, connection_observer,
profile_observer, ticket_observer,
)
from gui.v2.actions.error_handlers import navigate_to_new_sub_page
# generic
from typing import Callable, Optional
@ -34,21 +35,6 @@ def get_profile(profile_id: int):
return Result(valid=True, data=profile)
@register_action(ResultError.SUBSCRIPTION)
def navigate_to_new_sub_page() -> Result:
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="id")
@register_action(ResultError.EXPIRED_SUB)
def wipe_expired_subscription(profile_id: int) -> Result:
"""Call core to wipe a subscription, then navigate to the billing page"""
wiped = wipe_subscription(profile_id)
if wiped.valid:
return navigate_to_new_sub_page()
else:
return wiped
@register_action(ResultError.NO_TICKET)
def start_and_navigate_to_ticketing():
"""Starts a new ticketing cycle. (assumes the user opted in)."""

View file

@ -4,11 +4,14 @@ from core.services.payment_phase.check_payment_loop import CheckPaidTimer
from core.services.subscriptions.subscriptions_by_protocol import (
create_new_invoice, save_billing_code,
get_profile_subscription, wipe_billing_cycle,
start_new_payment_cycle
start_new_payment_cycle, return_pre_existing_invoice,
wipe_subscription,
)
from core.models.enum_models import PaymentType, PaymentCategory
from core.models.Result import Result, ResultError
from core.errors.logger import logger
from core.utils.type_checker import type_checker
# gui
from gui.v2.infrastructure.action_registry import register_action
@ -16,11 +19,56 @@ from gui.v2.actions.ticket_actions import fetch_ticket_invoice
from gui.v2.infrastructure.ThreadSafetyTool import ThreadSafetyTool
from gui.v2.infrastructure.take_plumbing_action import take_action
from gui.v2.infrastructure.setup_observers import connection_observer
from core.utils.type_checker import type_checker
from gui.v2.actions.error_handlers import navigate_to_new_sub_page
# generic
from typing import Callable, Optional
def _wipe_and_navigate(profile_id: Optional[int] = None):
cleared_cycle = wipe_billing_cycle(profile_id)
if cleared_cycle:
return navigate_to_new_sub_page()
else:
return cleared_cycle
@register_action(ResultError.EXPIRED_INVOICE)
def reset_individual_billing_cycle() -> Result:
payment_cycle = OverallPaymentCycle()
if not payment_cycle.category == PaymentCategory.INDIVIDUAL:
return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="This function isn't for tickets.")
billing_data = payment_cycle.billing_data
if not billing_data:
return _wipe_and_navigate()
profile_id = billing_data.instance_id
if profile_id:
return wipe_subscription(profile_id)
else:
return _wipe_and_navigate()
@register_action(ResultError.EXPIRED_SUB)
def wipe_expired_subscription(profile_id: int) -> Result:
"""Call core to wipe a subscription, then navigate to the billing page"""
logger.info(f"Wiping the subscription for {profile_id}")
wiped_result = wipe_subscription(profile_id)
if wiped_result.valid:
logger.info("Successfully wiped")
# also clear the whole billing cycle, so it's ready for an individual purchase,
return _wipe_and_navigate(profile_id)
else:
return wiped_result
@type_checker
def start_individual_payment(profile_id: int, ui_caller: Callable) -> Result:
logger.info(f"Attempting to start individual billing state for profile {str(profile_id)}")
@ -148,7 +196,6 @@ def stop_payment_timer() -> Result:
# def new_individual_billing(profile_id: int):
# """Wipes pre-existing global payment cycle. And Calls upon core to setup a new individual profile cycle & data state."""
# logger.info("starting a new individual profile state. And wiping the old one")

View file

@ -4,6 +4,7 @@ from core.models.BaseProfile import BaseProfile as Profile
from core.controllers.ProfileController import ProfileController
from core.controllers.tickets.TicketPayController import initiate_payment
from core.services.subscriptions.subscriptions_by_protocol import start_new_payment_cycle, save_subscription, get_ticket_invoice
from core.services.payment_phase.ticket_cycle import wipe_ticket_cycle
from core.services.payment_phase.payment_state import OverallPaymentCycle
from core.models.enum_models import PaymentCategory
from core.services.prepare_tickets.ticket_tracker import get_all_unused_tickets
@ -11,6 +12,7 @@ from core.models.Result import Result, ResultError
from core.errors.logger import logger
# gui
from gui.v2.infrastructure.action_registry import register_action
from gui.v2.infrastructure.take_plumbing_action import take_action
from gui.v2.infrastructure.setup_observers import connection_observer, ticket_observer
@ -122,6 +124,11 @@ class TicketUser():
self.ui_caller(result)
def wipe_ticket_billing():
@register_action(ResultError.TICKET_EXPIRED)
def wipe_ticket_billing() -> Result:
"""Wipe existing data and start over."""
wiped = wipe_ticket_cycle()
if wiped.valid:
return Result(valid=True, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker")
else:
return Result(valid=False, error_type=ResultError.NAVIGATE_TO, navigate_to="plan_picker")

View file

@ -7,6 +7,7 @@ ENUM_MAP = {
ResultError.UNKNOWN: None,
ResultError.NEED_SYNC: (new_sync),
ResultError.ACTIVE_BILLING: (wipe_billing_cycle),
ResultError.EXPIRED_INVOICE: (wipe_billing_cycle),
ResultError.INVALID_API_REPLY: None,
ResultError.ENCRYPTION: None
}

View file

@ -18,7 +18,7 @@ from gui.v2.ui.pages.Page import Page
from gui.v2.ui.popups.message_box import style_message_box, mark_confirm_button
from gui.v2.actions.menu_tasks import enable_profile
from gui.v2.actions.payment_actions import start_individual_payment
from gui.v2.actions.ticket_actions import start_ticket
from gui.v2.actions.ticket_actions import start_ticket, fetch_ticket_invoice
HOW_MANY_PROFILES_DEFAULT = 6

View file

@ -189,6 +189,7 @@ class PaymentDetailsPage(Page):
# this is not used here, but this is the right place to put it. Since we should have it by now:
self.currency = self._get_currency()
if not self.currency:
self.custom_window.navigator.navigate("currency_selection")
return # error is already displayed in subfunction.
# launches background thread tasks:
@ -223,6 +224,8 @@ class PaymentDetailsPage(Page):
if message:
self.show_a_popup(str(message))
self.custom_window.navigator.navigate("menu")
# # Turn it back on if off:
# else:
# self.begin_invoice()
@ -283,6 +286,7 @@ class PaymentDetailsPage(Page):
if not invoice:
self.show_a_popup("Missing Invoice from API call's data")
self.custom_window.navigator.navigate("id")
return
# Fill in UI:
@ -431,8 +435,8 @@ class PaymentDetailsPage(Page):
currency = billing_singleton.currency
if not currency:
self.show_a_popup('Missing Currency')
return None
# self.show_a_popup('Missing Currency')
if not isinstance(currency, Currency):
self.display_error(