New refactored Use of tickets and payment of tickets loops/state, integrated with existing system
This commit is contained in:
parent
6a445eba41
commit
10e46cd8ea
8 changed files with 97 additions and 39 deletions
|
|
@ -179,3 +179,7 @@ def change_systemwide_connection(profile: Profile, new_value: str) -> Result:
|
|||
profile.connection.code = new_value
|
||||
# final_data = "edit_session"
|
||||
return Result(valid=True, data=profile)
|
||||
|
||||
|
||||
def save_billing_code(profile: Profile, billing_code: str) -> bool:
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ def initiate_payment(
|
|||
payload = {
|
||||
"which_key": which_key,
|
||||
"payment_type": "crypto",
|
||||
"which_cryptocurrency": which_cryptocurrency,
|
||||
"currency": which_cryptocurrency,
|
||||
"how_many_profiles": how_many_profiles,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ ENUM_MAP = {
|
|||
ResultError.NEED_SYNC: (new_sync),
|
||||
ResultError.INVALID_API_REPLY: None,
|
||||
ResultError.ENCRYPTION: None,
|
||||
# ResultError.NO_TICKETS: (start_billing),
|
||||
# ResultError.NO_TICKET: (start_billing),
|
||||
|
||||
# ResultError.NO_PROFILE: (go_to_new_profile_page),
|
||||
# ResultError.ALREADY_EXISTS: (get_invoice), # bypass_existing: True
|
||||
# ResultError.BILLING_CODE_EXISTS: (get_invoice) # bypass_existing: True
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class ResultError(Enum):
|
|||
SUBSCRIPTION = "subscription"
|
||||
EXPIRED = "expired" # meant for invoice
|
||||
TICKET = "ticket"
|
||||
NO_TICKET = "no_ticket"
|
||||
ALREADY_EXISTS = "already_exists"
|
||||
BILLING_CODE_EXISTS = "billing_code_exists"
|
||||
PROCESS_GOT_KILLED = "process_got_killed"
|
||||
|
|
|
|||
|
|
@ -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.subscriptions.subscriptions_by_protocol import check_paid, mark_as_paid, mark_global_tickets_paid
|
||||
|
||||
from core.controllers.tickets.TicketPayController import check_if_ticket_paid
|
||||
|
||||
|
|
@ -8,6 +8,7 @@ from core.models.enum_models import PaymentType
|
|||
from core.models.Result import Result, ResultError
|
||||
# from infrastructure.observer_service import notify
|
||||
from core.errors.logger import logger
|
||||
from core.models.Result import Result, ResultError
|
||||
|
||||
from threading import Thread
|
||||
from typing import Callable
|
||||
|
|
@ -112,8 +113,19 @@ class CheckPaidTimer:
|
|||
# Reset instance:
|
||||
CheckPaidTimer._instance = None
|
||||
|
||||
# SAVE:
|
||||
marked = mark_as_paid(profile_id=self.profile_id)
|
||||
# SAVE INDIVIDUAL:
|
||||
if self.payment_type == PaymentType.CRYPTO:
|
||||
marked = mark_as_paid(profile_id=self.profile_id)
|
||||
|
||||
# SAVE TICKETS PAID:
|
||||
elif self.payment_type == PaymentType.TICKET:
|
||||
marked = mark_global_tickets_paid()
|
||||
|
||||
else:
|
||||
logger.error("Invalid payment type picked.")
|
||||
marked = Result(valid=False, error_type=ResultError.INVALID_INPUT, message="Unsupported protocol.")
|
||||
|
||||
# Either way evaluate:
|
||||
if not marked.valid:
|
||||
message = marked.message
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import json
|
|||
import os
|
||||
from functools import wraps
|
||||
from enum import Enum
|
||||
from typing import Any
|
||||
from typing import Any, Optional
|
||||
|
||||
def singleton(cls):
|
||||
instances = {}
|
||||
|
|
@ -100,7 +100,10 @@ class StatePersistenceMixin(ValidatedDataclass):
|
|||
def load(self):
|
||||
"""Load state from file, if each item passes a type check. With Enum translation"""
|
||||
|
||||
logger.info("Starting load of pre-existing data")
|
||||
|
||||
if not os.path.exists(self._data_file):
|
||||
logger.info("does NOT exist already")
|
||||
return
|
||||
|
||||
with open(self._data_file, "r") as f:
|
||||
|
|
@ -108,18 +111,20 @@ class StatePersistenceMixin(ValidatedDataclass):
|
|||
|
||||
for field in fields(self):
|
||||
value = data.get(field.name)
|
||||
logger.info(f"testing {field.name} of {value}")
|
||||
if value is None:
|
||||
continue
|
||||
|
||||
# Translation: If the expected type is an Enum, convert the raw value back into an Enum member
|
||||
if isinstance(field.type, type) and issubclass(field.type, Enum):
|
||||
logger.info("testing enum")
|
||||
try:
|
||||
value = field.type(value)
|
||||
except ValueError:
|
||||
logger.error(f"Invalid enum value {value} for {field.name}")
|
||||
continue
|
||||
|
||||
# Now the type check works because 'value' is now an actual Enum member
|
||||
# type check works because 'value' is now an actual Enum member
|
||||
if isinstance(value, field.type):
|
||||
setattr(self, field.name, value)
|
||||
|
||||
|
|
@ -133,30 +138,32 @@ HOW_MANY_PROFILES_DEFAULT = 6
|
|||
@singleton
|
||||
@dataclass
|
||||
class TicketPaymentData(StatePersistenceMixin):
|
||||
PAYLOAD_FIELDS = {"billing_code", "currency", "which_key", "how_many_profiles", "payment_type"}
|
||||
PAYLOAD_FIELDS = {"currency", "which_key", "how_many_profiles", "payment_type"}
|
||||
|
||||
# metadata on it's own GLOBAL instance:
|
||||
parent_folder = None
|
||||
folder_path: str = None
|
||||
_data_file: str = None
|
||||
|
||||
# actual payload data:
|
||||
# billing_code: str = None
|
||||
currency: Currency = None
|
||||
payment_type: PaymentType = PaymentType.CRYPTO
|
||||
|
||||
# for only tickets:
|
||||
which_key: str = None
|
||||
how_many_profiles: int = HOW_MANY_PROFILES_DEFAULT
|
||||
|
||||
use_random: bool = False
|
||||
temp_billing_code: Optional[str] = None
|
||||
billing_pending: bool = True
|
||||
|
||||
def __post_init__(self):
|
||||
# Global mode, assuming tickets here:
|
||||
self.folder_path = Constants.HV_TICKETING_CONFIG_HOME
|
||||
self._data_file = "billing.json"
|
||||
os.makedirs(self.folder_path, exist_ok=True)
|
||||
self._data_file = os.path.join(self.folder_path, "billing_choices.json")
|
||||
|
||||
self.load()
|
||||
|
||||
|
||||
|
||||
@singleton
|
||||
@dataclass
|
||||
class PaymentData(StatePersistenceMixin):
|
||||
|
|
@ -175,7 +182,6 @@ class PaymentData(StatePersistenceMixin):
|
|||
location_id: int = None
|
||||
protocol_choice: ProtocolChoice = None
|
||||
|
||||
|
||||
def __post_init__(self):
|
||||
# Localized mode: each ID gets its own folder
|
||||
self.parent_folder = Constants.HV_PROFILE_CONFIG_HOME
|
||||
|
|
@ -183,6 +189,8 @@ class PaymentData(StatePersistenceMixin):
|
|||
os.makedirs(self.folder_path, exist_ok=True)
|
||||
self._data_file = os.path.join(self.folder_path, "billing.json")
|
||||
|
||||
logger.info(f"full path of self._data_file {self._data_file}")
|
||||
|
||||
self.load()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -100,11 +100,12 @@ def start_billing_state(category: PaymentCategory, profile_id: Optional[int] = N
|
|||
return Result(valid=True, data=billing_data)
|
||||
|
||||
elif category == PaymentCategory.TICKET:
|
||||
billing_data = TicketPaymentData(
|
||||
which_key=None,
|
||||
how_many_profiles=HOW_MANY_PROFILES_DEFAULT,
|
||||
payment_type=None
|
||||
)
|
||||
billing_data = TicketPaymentData()
|
||||
# billing_data = TicketPaymentData(
|
||||
# which_key=None,
|
||||
# how_many_profiles=HOW_MANY_PROFILES_DEFAULT,
|
||||
# payment_type=None
|
||||
# )
|
||||
payment_cycle.instance = billing_data
|
||||
return Result(valid=True, data=billing_data)
|
||||
|
||||
|
|
@ -330,8 +331,19 @@ def get_profile_subscription(profile_id: int) -> Subscription:
|
|||
return profile.subscription
|
||||
|
||||
|
||||
def mark_global_tickets_paid() -> Result:
|
||||
"""Conclude the payment phase, by marking the global tickets as paid"""
|
||||
try:
|
||||
ticket_payments = TicketPaymentData()
|
||||
ticket_payments.billing_pending = False
|
||||
ticket_payments.save()
|
||||
return Result(valid=True)
|
||||
except Exception as e:
|
||||
return Result(valid=False, error_type=ResultError.UNKNOWN, message=str(e))
|
||||
|
||||
|
||||
def mark_as_paid(profile_id: int) -> Result:
|
||||
"""Conclude the payment phase, by marking the sub as paid"""
|
||||
"""Conclude the payment phase, by marking an individual subscription as paid"""
|
||||
|
||||
logger.info("Marking as paid..")
|
||||
|
||||
|
|
@ -379,25 +391,17 @@ def save_billing_code(profile_id: int, billing_code: str, expires_at: datetime)
|
|||
return Result(valid=False, message=error_msg, error_type=ResultError.INVALID_INPUT)
|
||||
|
||||
|
||||
# this can be modified to accept an id, once the UI has a cache layer:
|
||||
def save_subscription(profile: Profile, subscription: Subscription) -> bool:
|
||||
"""Saves subscription with the code and date."""
|
||||
|
||||
# # get the payment data singleton:
|
||||
# billing_data = PaymentData(instance_id=profile_id)
|
||||
PRE_REQS = [profile, subscription]
|
||||
if None in PRE_REQS:
|
||||
return False
|
||||
|
||||
# # new
|
||||
# if billing_data.billing_code is None:
|
||||
# billing_data.billing_code = billing_code
|
||||
# billing_data.save()
|
||||
# logger.info("saved billing_code")
|
||||
# return Result(valid=True)
|
||||
|
||||
# # Already exists and is different:
|
||||
# if regular_billing.billing_code != billing_code:
|
||||
# error_msg = "Critical Error! Billing codes do NOT match"
|
||||
# logger.error(error_msg)
|
||||
# return Result(valid=False, error_type=ResultError.UNKNOWN, message=error_msg)
|
||||
|
||||
# # already saved:
|
||||
# return Result(valid=True, message="Already saved.")
|
||||
profile.subscription = subscription
|
||||
profile.save()
|
||||
return True
|
||||
|
||||
|
||||
def return_pre_existing_payment_data(profile_id: int, subscription: SubscriptionInvoice) -> Result:
|
||||
|
|
@ -469,3 +473,25 @@ def return_pre_existing_payment_data(profile_id: int, subscription: Subscription
|
|||
# "payment_type": "crypto"
|
||||
# }
|
||||
|
||||
|
||||
|
||||
# Was cut from the end of save a billing code:
|
||||
|
||||
# # get the payment data singleton:
|
||||
# billing_data = PaymentData(instance_id=profile_id)
|
||||
|
||||
# # new
|
||||
# if billing_data.billing_code is None:
|
||||
# billing_data.billing_code = billing_code
|
||||
# billing_data.save()
|
||||
# logger.info("saved billing_code")
|
||||
# return Result(valid=True)
|
||||
|
||||
# # Already exists and is different:
|
||||
# if regular_billing.billing_code != billing_code:
|
||||
# error_msg = "Critical Error! Billing codes do NOT match"
|
||||
# logger.error(error_msg)
|
||||
# return Result(valid=False, error_type=ResultError.UNKNOWN, message=error_msg)
|
||||
|
||||
# # already saved:
|
||||
# return Result(valid=True, message="Already saved.")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ if TYPE_CHECKING:
|
|||
from core.services.using_tickets.send_unblinded import send_unblinded_ticket_to_server
|
||||
from core.utils.basic_operations.write_string_to_text_file import write_string_to_text_file
|
||||
from core.services.prepare_tickets.ticket_tracker import update_ticket_tracker
|
||||
from core.models.Subscription import Subscription
|
||||
from core.models.Result import Result, ResultError
|
||||
from core.errors.logger import logger
|
||||
from core.Constants import Constants
|
||||
|
|
@ -71,6 +72,7 @@ def use_ticket_orchestrator(
|
|||
###################################
|
||||
valid = raw_data_dict.get("valid", None)
|
||||
billing_code = raw_data_dict.get("billing_code", None)
|
||||
expires_at = raw_data_dict.get("expires_at", None)
|
||||
message = raw_data_dict.get("message", None)
|
||||
|
||||
logger.info(f"Was the ticket use valid? {valid}")
|
||||
|
|
@ -94,9 +96,13 @@ def use_ticket_orchestrator(
|
|||
billing_code=billing_code
|
||||
)
|
||||
|
||||
subscription = Subscription(
|
||||
billing_code=billing_code,
|
||||
expires_at=expires_at,
|
||||
)
|
||||
|
||||
# even if not saved return it back, so it can be saved with the profile by other functions,
|
||||
return Result(valid=True, data=billing_code)
|
||||
# return {"valid": True, "data": billing_code}
|
||||
return Result(valid=True, data=subscription)
|
||||
|
||||
# EXPIRED
|
||||
if not valid and message == "expired":
|
||||
|
|
|
|||
Loading…
Reference in a new issue