diff --git a/core/controllers/ConfigurationController.py b/core/controllers/ConfigurationController.py index dcd17d3..8bd6e6c 100644 --- a/core/controllers/ConfigurationController.py +++ b/core/controllers/ConfigurationController.py @@ -152,3 +152,28 @@ class ConfigurationController: configuration.singbox = new_value configuration.save() return True + + @staticmethod + def change_random_ticket(new_value: bool) -> bool: + configuration = ConfigurationController.get() + configuration.use_random = new_value + configuration.save() + return True + + @staticmethod + def use_random() -> bool: + configuration = ConfigurationController.get() + return configuration.use_random + + @staticmethod + def tickets_setup(new_value: bool) -> bool: + configuration = ConfigurationController.get() + configuration.tickets_setup = new_value + configuration.save() + return True + + @staticmethod + def are_tickets_setup() -> bool: + configuration = ConfigurationController.get() + return configuration.tickets_setup + diff --git a/core/errors/error_shortcuts.py b/core/errors/error_shortcuts.py new file mode 100644 index 0000000..c8fe151 --- /dev/null +++ b/core/errors/error_shortcuts.py @@ -0,0 +1,10 @@ +from core.models.Result import Result, ResultError +from core.errors.logger import logger + + + +def not_supported(): + NOT_SUPPORTED_PAYMENT = "This is not a supported payment category" + logger.error(NOT_SUPPORTED_PAYMENT) + return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=NOT_SUPPORTED_PAYMENT) + diff --git a/core/infrastructure/plumbing.py b/core/infrastructure/plumbing.py index f046a93..e9391da 100644 --- a/core/infrastructure/plumbing.py +++ b/core/infrastructure/plumbing.py @@ -51,27 +51,35 @@ class ThreadPlumbing: def _on_complete(self, future: Future): """Handle thread completion""" try: - logger.info("The plumbing module started it's 'on complete' phase..") + logger.info("The plumbing module started it's 'on complete' phase with this result:") result = future.result() if not isinstance(result, Result): result = convert_to_result(result) + + logger.info(result) if not result.valid: if not result.function_choice: result.function_choice = resolve_prescription(result, self.ENUM_MAP, *self.args, **self.kwargs) + logger.info(f"result.function_choice is {result.function_choice}") else: logger.info("Using the pre-existing function choice for this error.") + except Exception as e: error_msg = f"There was an error: {str(e)}" # notify(error_msg) logger.error(error_msg) result = Result(valid=False, error_type=ResultError.UNKNOWN, message=str(e)) - - # Back to main thread - print(f"result in plumbing is {result}") - self.ui_dispatcher.run_on_main_thread(self._on_result_ready, result) + + finally: + # Back to main thread + print(f"result in plumbing is {result}") + self.ui_dispatcher.run_on_main_thread(self._on_result_ready, result) def _on_result_ready(self, result: Result): """Decide: retry, show popup, or final callback""" + + logger.info(f"We are on the main thread with result {result}") + if result.valid or self.attempt_count >= MAX_ATTEMPTS: if self.ui_caller: print("calling back with result") @@ -89,11 +97,11 @@ class ThreadPlumbing: if self.ui_caller: self.ui_caller(result) - # notify(f"Error prompt {self.attempt_count} of {MAX_ATTEMPTS}") - # Show popup and wait for user choice choice = bool(result.function_choice) - print(f"is there a choice? {choice}") + logger.info(f"On the main thread, the choice is {choice}") + + popup = self.error_implementation( choice=bool(result.function_choice), title=f"Error: {result.error_type.value}", @@ -131,17 +139,21 @@ def resolve_prescription(result: Result, ENUM_MAP: dict, *args, **kwargs): And then add the args from either the original caller's args, or the defaults for that error type """ + logger.info(f"resolve_prescription is being called for {result}.") # Priority 1: If the function explicitly provided a full partial, use it. if result.function_choice: + logger.info("The result object itself has a function choice!") return result.function_choice # Priority 2: Look up the Enum map - # logger.info(f"self.ENUM_MAP is {self.ENUM_MAP}") + logger.info("checking enum map") if result.error_type not in ENUM_MAP: + logger.info(f"No solution in the ENUM_MAP of {ENUM_MAP}") return None # No solution enum_value = ENUM_MAP[result.error_type] + logger.info(f"got enum_value {enum_value}") # Handle the None case (no solution) if enum_value is None: diff --git a/core/models/BaseProfile.py b/core/models/BaseProfile.py index 5243ba1..d4ea4e9 100644 --- a/core/models/BaseProfile.py +++ b/core/models/BaseProfile.py @@ -36,6 +36,7 @@ import shutil import tempfile from sqlalchemy.orm import Session from enum import Enum +import inspect from core.models.manage.session_management import init_session, get_session diff --git a/core/models/Configuration.py b/core/models/Configuration.py index a383c94..1632b06 100644 --- a/core/models/Configuration.py +++ b/core/models/Configuration.py @@ -33,7 +33,8 @@ class Configuration(BaseModel): dns: Optional[bool] = False did_sudo_setup: Optional[bool] = False singbox: Optional[str] = None - + tickets_setup: Optional[bool] = False + use_random: Optional[bool] = False @field_validator('last_synced_at', mode='before') diff --git a/core/models/Result.py b/core/models/Result.py index a86f679..3888b2b 100644 --- a/core/models/Result.py +++ b/core/models/Result.py @@ -26,6 +26,8 @@ class ResultError(Enum): STATE_CONFLICT = "state_conflict" TICKET = "ticket" NO_TICKET = "no_ticket" + RANDOM_TICKETS_OFF = "random_tickets_off" + TICKET_SETUP = "ticket_setup" ALREADY_EXISTS = "already_exists" BILLING_CODE_EXISTS = "billing_code_exists" PROCESS_GOT_KILLED = "process_got_killed" diff --git a/core/models/manage/server_reply_tools.py b/core/models/manage/server_reply_tools.py index 8f59af0..d314423 100644 --- a/core/models/manage/server_reply_tools.py +++ b/core/models/manage/server_reply_tools.py @@ -135,7 +135,7 @@ def api_call_and_extraction( #----------------------------- # Layer 2: Validity of Server's Reply: pre_validated_reply = api_response.data - logger.info("Validating the Validity of the Server's Reply..") + logger.info(f"Validating the Validity of the Server's Reply {pre_validated_reply}") worked, server_reply = validate_model(ServerReply, pre_validated_reply) error_msg = f"Validation of the Server's Reply had issues! Pre-validate: {pre_validated_reply}. Post validate: {server_reply}" @@ -152,7 +152,7 @@ def api_call_and_extraction( logger.info("Validation successfull. Checking if the server's reply says the operation worked..") if not server_reply.valid: - error_message = f"Server reply was correctly structured, but the actual invoice failed to be created. The raw data is {raw_reply}" + error_message = f"Server reply was correctly structured, but the actual invoice failed to be created. The raw data is {server_reply}" logger.error(error_message) # translate by enum error code: return translate_server_failure(server_reply) diff --git a/core/services/payment_phase/check_payment_loop.py b/core/services/payment_phase/check_payment_loop.py index 4b291cd..b3475af 100644 --- a/core/services/payment_phase/check_payment_loop.py +++ b/core/services/payment_phase/check_payment_loop.py @@ -1,5 +1,5 @@ from core.infrastructure.thread_dispatch import MainThreadDispatcher -from core.services.subscriptions.subscriptions_by_protocol import check_paid, mark_as_paid +from core.services.payment_phase.payment_update_tools import check_paid, mark_as_paid from core.controllers.tickets.TicketPayController import check_if_ticket_paid from core.observers.ConnectionObserver import ConnectionObserver diff --git a/core/services/payment_phase/payment_state.py b/core/services/payment_phase/payment_state.py index 3caadcc..d2cf9ff 100644 --- a/core/services/payment_phase/payment_state.py +++ b/core/services/payment_phase/payment_state.py @@ -151,7 +151,7 @@ class TicketPaymentData(StatePersistenceMixin): how_many_profiles: int = HOW_MANY_PROFILES_DEFAULT use_random: bool = False - temp_billing_code: Optional[str] = None + billing_code: Optional[str] = None billing_pending: bool = True paid: bool = False @@ -204,7 +204,7 @@ class OverallPaymentCycle(): already_polling: bool = False profile_id: int = None active: bool = False - active_data: Any = None + billing_data: Any = None got_invoice: bool = False billing_code: str = None @@ -215,7 +215,7 @@ class OverallPaymentCycle(): def wipe(self): """Wipe all data and start over.""" self.active = False - self.active_data = None + self.billing_data = None self.category = PaymentCategory.INDIVIDUAL self.already_polling = False self.profile_id = None diff --git a/core/services/payment_phase/payment_update_tools.py b/core/services/payment_phase/payment_update_tools.py new file mode 100644 index 0000000..606cc79 --- /dev/null +++ b/core/services/payment_phase/payment_update_tools.py @@ -0,0 +1,114 @@ +from core.models.manage.server_reply_tools import api_call_and_extraction, ApiCall +from core.controllers.ProfileController import ProfileController +from core.services.payment_phase.payment_state import OverallPaymentCycle +from core.models.enum_models import PaymentCategory +from core.observers.ConnectionObserver import ConnectionObserver +from core.Constants import Constants +from core.models.Result import Result, ResultError +from core.errors.logger import logger +from core.errors.error_shortcuts import not_supported + + +def _mark_profile_as_paid(profile_id: int): + """Save to disk that an individual profile is paid.""" + + logger.info(f"Marking profile_id {str(profile_id)} as paid..") + + # Get real profile object: + profile = ProfileController.get(profile_id) + if not profile: + error_msg = f"Invalid profile id of {profile_id}" + logger.error(error_msg) + return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT) + + profile.subscription.billing_pending = False + ProfileController.update(profile) + return Result(valid=True) + + + +def mark_as_paid() -> Result: + """Conclude the payment phase for ALL profiles.""" + + logger.info("Marking as paid..") + + payment_cycle = OverallPaymentCycle() + + if not payment_cycle: + error_msg = "There is no current billing cycle." + return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) + + billing_data = payment_cycle.instance + + if not billing_data: + error_msg = "There is no current billing data." + return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) + + category = payment_cycle.category + + if category == PaymentCategory.INDIVIDUAL: + profile_id = billing_data.instance_id + result = _mark_profile_as_paid(profile_id) + if not result.valid: + return result + + elif category == PaymentCategory.TICKET: + # saving global ticket data: + billing_data.billing_pending = False + billing_data.paid = True + billing_data.save() + + else: + return not_supported() + + # finally: + payment_cycle.wipe() + return Result(valid=True) + + +def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Result: + """ + Purpose: + Evaluates Payment Status for EITHER payment category + + Requires: + Valid OverallPaymentCycle + billing_code is provided by the timer for BOTH types. + """ + + payment_cycle = OverallPaymentCycle() + category = payment_cycle.category + + logger.info(f"Checking paid with category of {category}") + + # Get endpoint: + if category == PaymentCategory.INDIVIDUAL: + url = f'{Constants.SP_BASE_URL}/v2/check_paid' + elif category == PaymentCategory.TICKET: + url = f'{Constants.TICKET_API_BASE_URL}/check_ticket_paid' + else: + return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="This is not a supported payment category to check") + + submit_data = { + "billing_code": billing_code + } + + result = api_call_and_extraction( + desired_type="payment_status", + method=ApiCall.POST, + url=url, + connection_observer=connection_observer, + payload=submit_data + ) + + if not result.valid: + return result + + payment_status = result.data + + # paid + if payment_status: + return Result(valid=True, message="paid") + else: + return Result(valid=False, error_type=ResultError.UNPAID, message="The API call went through, but this subscription is not yet paid.") + diff --git a/core/services/prepare_tickets/ticket_tracker.py b/core/services/prepare_tickets/ticket_tracker.py index 22c6280..8ade3f8 100644 --- a/core/services/prepare_tickets/ticket_tracker.py +++ b/core/services/prepare_tickets/ticket_tracker.py @@ -1,3 +1,4 @@ +from core.controllers.ConfigurationController import ConfigurationController from core.utils.basic_operations.write_or_read_from_json import read_entire_json, update_json, write_json_to_file from core.utils.basic_operations.does_file_exist import does_file_exist from core.errors.exceptions import * @@ -11,6 +12,52 @@ from pathlib import Path import shutil + +def does_ticket_tracker_exist() -> tuple: + if os.path.exists(Constants.TICKET_TRACKER_PATH): + return (True, Constants.TICKET_TRACKER_PATH) + else: + return (False, Constants.TICKET_TRACKER_PATH) + +def ticket_tracker_exists() -> bool: + return os.path.exists(Constants.TICKET_TRACKER_PATH) + + +def ticket_config_exists(): + filepath = f"{Constants.HV_TICKETING_CONFIG_HOME}/billing_choices.json" + return does_file_exist(filepath) + + +def get_a_random_ticket() -> Result: + """Get a random ticket""" + + results = get_all_unused_tickets() + + valid = results.get("valid") + tickets = results.get("data") + if valid and tickets: + one_random_ticket = tickets.random() + return Result(valid=True, data=one_random_ticket) + + message = result.get("message") + + if message == "no_tickets_left": + return Result(valid=False, error_type=ResultError.NO_TICKET, message="You do not have any tickets left, would you like to start a new phase to get them?") + + elif message == "invalid_data": + return Result(valid=False, error_type=ResultError.TICKET_SETUP, message="The ticket tracker file is either missing values or corrupted.") + + elif not ticket_config_exists(): + return Result(valid=False, error_type=ResultError.TICKET_SETUP, message="You have not yet setup the ticketing config.") + + elif not does_ticket_tracker_exist(): + return Result(valid=False, error_type=ResultError.TICKET_SETUP, message="You have not yet setup the ticket tracker. The file is gone, corrupted, or missing.") + + else: + return Result(valid=False, error_type=ResultError.TICKET_SETUP, message="For unknown reasons, we are unable to read the ticket tracker.") + + + def delete_ticket_data(): ticket_folders = [Constants.HV_TICKETING_CONFIG_HOME, Constants.HV_TICKETING_DATA_HOME] quantity_to_delete = len(ticket_folders) @@ -66,13 +113,6 @@ def is_given_ticket_able_to_be_read(which_ticket: int) -> bool: return False -def does_ticket_tracker_exist() -> tuple: - if os.path.exists(Constants.TICKET_TRACKER_PATH): - return (True, Constants.TICKET_TRACKER_PATH) - else: - return (False, Constants.TICKET_TRACKER_PATH) - - def get_data_for_a_single_ticket(which_ticket_as_int: int) -> tuple: data = read_entire_json(Constants.TICKET_TRACKER_PATH) diff --git a/core/services/subscriptions/subscriptions_by_protocol.py b/core/services/subscriptions/subscriptions_by_protocol.py index 87cce25..8bfda71 100644 --- a/core/services/subscriptions/subscriptions_by_protocol.py +++ b/core/services/subscriptions/subscriptions_by_protocol.py @@ -1,3 +1,4 @@ +from core.services.payment_phase.check_payment_loop import CheckPaidTimer from core.models.manage.server_reply_tools import api_call_and_extraction, ApiCall from core.controllers.ProfileController import ProfileController from core.models.BaseProfile import BaseProfile as Profile @@ -10,16 +11,12 @@ from core.models.Subscription import Subscription from core.Constants import Constants from core.models.Result import Result, ResultError from core.errors.logger import logger +from core.errors.error_shortcuts import not_supported # generic from datetime import datetime from typing import Optional -def not_supported(): - NOT_SUPPORTED_PAYMENT = "This is not a supported payment category" - logger.error(NOT_SUPPORTED_PAYMENT) - return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=NOT_SUPPORTED_PAYMENT) - def invalid_server_format() -> Result: error_msg = "The server gave an invalid response format." @@ -91,53 +88,125 @@ def is_payment_active_for(profile_id: int) -> tuple: return False, False -def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int] = None) -> Result: +def error_about_mismatch(category: PaymentCategory) -> Result: + """Display an error for the billing process already running, with a choice to end it""" + if category == PaymentCategory.INDIVIDUAL: + error_msg = "You want an individual process, but there's already a ticketing billing process active right now, do you want to stop that?" + else: + error_msg = "You want a ticketing process, but there's already an individual billing process active right now, do you want to stop that?" + + logger.error(error_msg) + return Result(valid=False, error_type=ResultError.ACTIVE_BILLING, message=error_msg) + + + +def evaluate_cycle(payment_cycle: OverallPaymentCycle, profile_id: int) -> Result: + if the_same_payment_cycle(payment_cycle=payment_cycle, profile_id=profile_id): + logger.info("It's the same as before, returning the data") + return Result(valid=True, data=payment_cycle.billing_data) + else: + error_msg = f"You activated profile {profile_id}, but profile {payment_cycle.billing_data.instance_id} has active billing, stop that?" + logger.error(error_msg) + return Result(valid=False, error_type=ResultError.ACTIVE_BILLING, message=error_msg) + + +def start_new_payment_cycle(category: PaymentCategory, profile_id: Optional[int] = None) -> Result: + """ + Purpose: + Starts the overall payment cycle, and the individual category's billing data. + + On Conflict: + If there's already a cycle running, this function addresses that, + by returning the cycle if it's the same, or offering to end it if different. + """ + + # PRE-REQS: if not profile_id and category == PaymentCategory.INDIVIDUAL: error_msg = "Need a profile id for an individual profile." logger.error(error_msg) return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) + logger.info("Got pre-reqs for individual.") - # Is there currently a core process? + # This is the singleton: payment_cycle = OverallPaymentCycle() + logger.info(f"payment_cycle is {payment_cycle}") - if payment_cycle.active: - if the_same_payment_cycle(payment_cycle=payment_cycle, profile_id=profile_id): - logger.info("It's the same as before, returning the code") - return Result(valid=True, data=payment_cycle.billing_code) - else: - error_msg = f"There's already a billing process active, do you want to stop that?" - logger.error(error_msg) + # ---------------------------- + # MISMATCH IN CATEGORY FROM WHAT EXISTS: + # ---------------------------- + if category != payment_cycle.category: + return error_about_mismatch(category) + logger.info("Made it past the mismatch check.") + + # ---------------------------- + # ALREADY ACTIVE INDIVIDUAL CYCLE: + # ---------------------------- + if all([ # all are true: + payment_cycle.billing_data, + payment_cycle.active, + payment_cycle.category == PaymentCategory.INDIVIDUAL, + category == PaymentCategory.INDIVIDUAL + ]): + profile_id_already_running = payment_cycle.billing_data.instance_id + if profile_id_already_running: + logger.info(f"Triggering the evaluate_cycle reply because of {profile_id_already_running} running already") + return evaluate_cycle(payment_cycle=payment_cycle, profile_id=profile_id) + + # ---------------------------- + # ALREADY POLLING TIMER: + # ---------------------------- + try: + timer_loop = CheckPaidTimer.get_instance() + if timer_loop.is_running: + error_msg = "There's already a billing cycle that's actively checking if paid. Do you want to stop that?" return Result(valid=False, error_type=ResultError.ACTIVE_BILLING, message=error_msg) + except Exception as e: + logger.info("All good, the timer isn't running, and we were afraid it was.") + logger.info("Process is NOT yet active, starting..") payment_cycle.begin() payment_cycle.category = category + # ---------------------------- + # START INDIVIDUAL: + # ---------------------------- if category == PaymentCategory.INDIVIDUAL: payment_cycle.profile_id = profile_id - payment_cycle.active_data = PaymentData(instance_id=profile_id) + payment_cycle.billing_data = PaymentData(instance_id=profile_id) subscription = get_profile_subscription(profile_id=profile_id, save_pending=True) # save to disk that it's pending if subscription: billing_code = subscription.billing_code else: billing_code = None + logger.info("Individual profile initial setup complete.") + # ---------------------------- + # START TICKETS: + # ---------------------------- elif category == PaymentCategory.TICKET: billing_data = TicketPaymentData() - payment_cycle.active_data = billing_data # saves - billing_code = billing_data.temp_billing_code + payment_cycle.billing_data = billing_data # saves + billing_code = billing_data.billing_code # might be empty if new + logger.info("Ticketing initial setup complete.") else: error_msg = f"Invalid PaymentCategory selected of {str(category)}" logger.error(error_msg) return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) - # save in cache: + # ---------------------------- + # COMMON FOR BOTH CATEGORIES: + # ---------------------------- payment_cycle.billing_code = billing_code payment_cycle.active = True - return Result(valid=True, data=billing_code) + logger.info(f"Returning the entire payment_cycle") + return Result(valid=True, data=payment_cycle) + +# else: +# return Result(valid=False, error_type=ResultError.UNKNOWN, message="We failed to create new billing cycle data.") def create_new_invoice(connection_observer: ConnectionObserver) -> Result: @@ -187,16 +256,16 @@ def return_pre_existing_invoice(billing_code: str, connection_observer: Connecti def create_ticket_invoice(payment_cycle: OverallPaymentCycle, connection_observer: ConnectionObserver) -> Result: logger.info("Creating a new TICKET invoice..") - billing_data = payment_cycle.active_data + billing_data = payment_cycle.billing_data if not billing_data: error_msg = "You are currently missing billing data to begin an invoice" return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) # already has billing id: - billing_code = billing_data.temp_billing_code + billing_code = billing_data.billing_code if billing_code: - logger.info("We already have a temp_billing_code id for this ticket prep.") + logger.info("We already have a billing_code id for this ticket prep.") return return_pre_existing_invoice(billing_code=billing_code, connection_observer=connection_observer) #----------------------------- @@ -229,7 +298,7 @@ def create_ticket_invoice(payment_cycle: OverallPaymentCycle, connection_observe #----------------------------- billing_code = final_invoice.billing_code - billing_data.temp_billing_code = billing_code + billing_data.billing_code = billing_code billing_data.save() payment_cycle.got_invoice = True @@ -260,7 +329,7 @@ def create_individual_invoice(payment_cycle: OverallPaymentCycle, connection_obs # MAKE PAYLOAD. PIECE BY PIECE #----------------------------- # get the payment data singleton: - billing_data = payment_cycle.active_data + billing_data = payment_cycle.billing_data # make sure the protocol is saved in the singleton already, if not billing_data.protocol_choice: @@ -322,51 +391,51 @@ def create_individual_invoice(payment_cycle: OverallPaymentCycle, connection_obs -def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Result: - """ - Purpose: - Evaluates Payment Status for EITHER payment category +# def check_paid(billing_code: str, connection_observer: ConnectionObserver) -> Result: +# """ +# Purpose: +# Evaluates Payment Status for EITHER payment category - Requires: - Valid OverallPaymentCycle - billing_code is provided by the timer for BOTH types. - """ +# Requires: +# Valid OverallPaymentCycle +# billing_code is provided by the timer for BOTH types. +# """ - payment_cycle = OverallPaymentCycle() - category = payment_cycle.category +# payment_cycle = OverallPaymentCycle() +# category = payment_cycle.category - logger.info(f"Checking paid with category of {category}") +# logger.info(f"Checking paid with category of {category}") - # Get endpoint: - if category == PaymentCategory.INDIVIDUAL: - url = f'{Constants.SP_BASE_URL}/v2/check_paid' - elif category == PaymentCategory.TICKET: - url = f'{Constants.TICKET_API_BASE_URL}/check_ticket_paid' - else: - return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="This is not a supported payment category to check") +# # Get endpoint: +# if category == PaymentCategory.INDIVIDUAL: +# url = f'{Constants.SP_BASE_URL}/v2/check_paid' +# elif category == PaymentCategory.TICKET: +# url = f'{Constants.TICKET_API_BASE_URL}/check_ticket_paid' +# else: +# return Result(valid=False, error_type=ResultError.INVALID_INPUT, message="This is not a supported payment category to check") - submit_data = { - "billing_code": billing_code - } +# submit_data = { +# "billing_code": billing_code +# } - result = api_call_and_extraction( - desired_type="payment_status", - method=ApiCall.POST, - url=url, - connection_observer=connection_observer, - payload=submit_data - ) +# result = api_call_and_extraction( +# desired_type="payment_status", +# method=ApiCall.POST, +# url=url, +# connection_observer=connection_observer, +# payload=submit_data +# ) - if not result.valid: - return result +# if not result.valid: +# return result - payment_status = result.data +# payment_status = result.data - # paid - if payment_status: - return Result(valid=True, message="paid") - else: - return Result(valid=False, error_type=ResultError.UNPAID, message="The API call went through, but this subscription is not yet paid.") +# # paid +# if payment_status: +# return Result(valid=True, message="paid") +# else: +# return Result(valid=False, error_type=ResultError.UNPAID, message="The API call went through, but this subscription is not yet paid.") def paying_for_tickets() -> bool: @@ -404,7 +473,7 @@ def get_current_billing_data(category: Optional[PaymentCategory] = None) -> Resu # if not payment_cycle and category: - billing_data = payment_cycle.active_data + billing_data = payment_cycle.billing_data if not billing_data: error_msg = "There is no current billing data." @@ -416,9 +485,21 @@ def get_current_billing_data(category: Optional[PaymentCategory] = None) -> Resu def wipe_billing_cycle() -> Result: """Wipes the entire overall cycle""" logger.info("Wiping the overall cycle, so this cycle no longer blocks other new ones") + + # Timer: + try: + timer_loop = CheckPaidTimer.get_instance() + if timer_loop.is_running: + timer_loop.stop_polling() + except Exception as e: + logger.info("Skipping Timer since it's not yet created.") + + # Overall State: payment_cycle = OverallPaymentCycle() if payment_cycle: payment_cycle.wipe() + + # This has to return a Result object, even if not checked, because of it's use in plumbing, return Result(valid=True) @@ -462,59 +543,59 @@ def wipe_subscription(profile_id: int) -> Result: return Result(valid=True) -def mark_as_paid() -> Result: - """Conclude the payment phase""" +# def mark_as_paid() -> Result: +# """Conclude the payment phase""" - logger.info("Marking as paid..") +# logger.info("Marking as paid..") - payment_cycle = OverallPaymentCycle() +# payment_cycle = OverallPaymentCycle() - if not payment_cycle: - error_msg = "There is no current billing cycle." - return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) +# if not payment_cycle: +# error_msg = "There is no current billing cycle." +# return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) - billing_data = payment_cycle.instance +# billing_data = payment_cycle.instance - if not billing_data: - error_msg = "There is no current billing data." - return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) +# if not billing_data: +# error_msg = "There is no current billing data." +# return Result(valid=False, error_type=ResultError.INVALID_INPUT, message=error_msg) - category = payment_cycle.category +# category = payment_cycle.category - if category == PaymentCategory.INDIVIDUAL: - profile_id = billing_data.instance_id - result = mark_profile_as_paid(profile_id) - if not result.valid: - return result +# if category == PaymentCategory.INDIVIDUAL: +# profile_id = billing_data.instance_id +# result = mark_profile_as_paid(profile_id) +# if not result.valid: +# return result - elif category == PaymentCategory.TICKET: - # saving global ticket data: - billing_data.billing_pending = False - billing_data.paid = True - billing_data.save() +# elif category == PaymentCategory.TICKET: +# # saving global ticket data: +# billing_data.billing_pending = False +# billing_data.paid = True +# billing_data.save() - else: - return not_supported() +# else: +# return not_supported() - # finally: - payment_cycle.wipe() - return Result(valid=True) +# # finally: +# payment_cycle.wipe() +# return Result(valid=True) -def mark_profile_as_paid(profile_id: int): +# def mark_profile_as_paid(profile_id: int): - logger.info(f"Marking profile_id {str(profile_id)} as paid..") +# logger.info(f"Marking profile_id {str(profile_id)} as paid..") - # Get real profile object: - profile = ProfileController.get(profile_id) - if not profile: - error_msg = f"Invalid profile id of {profile_id}" - logger.error(error_msg) - return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT) +# # Get real profile object: +# profile = ProfileController.get(profile_id) +# if not profile: +# error_msg = f"Invalid profile id of {profile_id}" +# logger.error(error_msg) +# return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT) - profile.subscription.billing_pending = False - ProfileController.update(profile) - return Result(valid=True) +# profile.subscription.billing_pending = False +# ProfileController.update(profile) +# return Result(valid=True) def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime) -> Result: