Introduced Resume pre-existing invoice. Feature is stable for individual profiles.

This commit is contained in:
SimplifiedPrivacy 2026-10-07 18:38:30 -04:00
parent 40ee53197e
commit 41ac3a75ff
3 changed files with 66 additions and 39 deletions

View file

@ -1,37 +1,66 @@
from core.services.payment_phase.payment_state import PaymentData # core
from core.services.subscriptions.check_payment_loop import CheckPaidTimer from core.services.subscriptions.check_payment_loop import CheckPaidTimer
from core.services.subscriptions.subscriptions_by_protocol import create_invoice, save_billing_code, return_pre_existing_payment_data, get_profile_subscription from core.services.subscriptions.subscriptions_by_protocol import create_invoice, save_billing_code, get_profile_subscription
from core.models.enum_models import PaymentType from core.models.enum_models import PaymentType
from core.models.pydantic_models.ServerReply import SubscriptionInvoice
from core.models.Result import Result
from core.models.Subscription import Subscription
from core.errors.logger import logger from core.errors.logger import logger
# gui # gui
from gui.v2.infrastructure.ThreadSafetyTool import ThreadSafetyTool from gui.v2.infrastructure.ThreadSafetyTool import ThreadSafetyTool
from gui.v2.infrastructure.take_plumbing_action import take_action from gui.v2.infrastructure.take_plumbing_action import take_action
from gui.v2.infrastructure.setup_observers import connection_observer from gui.v2.infrastructure.setup_observers import connection_observer
# generic
from typing import Callable, Optional from typing import Callable, Optional
def gui_starts_invoice(profile_id: int, ui_caller: Callable) -> Result: def gui_starts_invoice(profile_id: int, invoice_ui_caller: Callable, timer_resume_caller: Callable) -> str | None:
"""Create a new invoice if we don't have one yet, or re-use the saved data.""" """Create a new invoice if we don't have one yet, or re-use the saved data. Returns any errors as strings for UI to display."""
subscription = get_profile_subscription(profile_id) subscription = get_profile_subscription(profile_id)
print(f"in gui_starts_invoice, subscription is {subscription}")
#----------------------------
# INVOICE DATA
#----------------------------
take_action(
action=create_invoice,
ui_caller=invoice_ui_caller,
profile_id=profile_id,
connection_observer=connection_observer
)
#----------------------------
# FILTER
#----------------------------
if not subscription: if not subscription:
logger.info(f"Starting a New Invoice for Profile {profile_id}..") logger.info(f"Started a New Invoice for Profile {profile_id}..")
take_action( return None
action=create_invoice,
ui_caller=ui_caller, if not subscription.billing_pending:
profile_id=profile_id, logger.error("This function should not have been called if the invoice is already paid.")
connection_observer=connection_observer return "Invoice already paid."
)
return Result(valid=True) if not subscription.billing_code:
else: return "Previous subscription didn't have a billing code."
return return_pre_existing_payment_data(profile_id=profile_id, subscription=subscription)
#----------------------------
# CHECK PAID ON PRE-EXISTING INVOICE
#----------------------------
logger.info("Resuming a pre-existing payment..")
# Setup PyQt6 GUI as mainthread to bring back to:
pyqt_threader = ThreadSafetyTool()
# setup the timer with the callback:
# it doesn't return result here. It calls the view_callback function.
check_paid_timer = CheckPaidTimer(
view_callback=timer_resume_caller,
billing_code=subscription.billing_code,
ui_dispatcher=pyqt_threader,
payment_type=PaymentType.CRYPTO,
connection_observer=connection_observer,
profile_id=profile_id
)
return None
def save_and_begin_timer( def save_and_begin_timer(
@ -71,25 +100,8 @@ def save_and_begin_timer(
billing_code=billing_code, billing_code=billing_code,
ui_dispatcher=pyqt_threader, ui_dispatcher=pyqt_threader,
payment_type=payment_type, payment_type=payment_type,
connection_observer=connection_observer connection_observer=connection_observer,
profile_id=profile_id
) )
return True # this is just that it started return True # this is just that it started
# def create_an_invoice(ui_caller: Callable, profile_id: int, connection_observer: ConnectionObserver) -> SubscriptionInvoice|str:
# """Create an Invoice for regular single profile billing"""
# take_action(
# action=create_invoice,
# ui_caller=ui_caller,
# profile_id=profile_id,
# connection_observer=connection_observer
# )
# subscription_plan_id=subscription_plan_id,
# location_id=location_id,
# protocol_choice=protocol_choice,
# currency=currency_choice,

View file

@ -182,9 +182,19 @@ def _enable(request: dict[str, Any], emit: MenuCallback, max_resolution: str | N
if needs_download and current_connection() != "tor" and not request.get("allow_uninstalled_tor"): if needs_download and current_connection() != "tor" and not request.get("allow_uninstalled_tor"):
return _confirmation(request, "tor_download", "The Tor browser is not installed; switch to Tor to download it or proceed over clearweb.") return _confirmation(request, "tor_download", "The Tor browser is not installed; switch to Tor to download it or proceed over clearweb.")
request["allow_uninstalled_tor"] = True request["allow_uninstalled_tor"] = True
# Payment not finished:
try:
if profile.subscription.billing_pending:
print("This is resuming a pre-done payment")
return {"status": "navigate", "destination": "payment_details"}
except Exception as e:
print(f"Skipping billing resume check")
preparation = _prepare_subscription(profile, request) preparation = _prepare_subscription(profile, request)
if preparation is not None: if preparation is not None:
return preparation return preparation
ignored = {"ignore_endpoint_verification": EndpointVerificationError, ignored = {"ignore_endpoint_verification": EndpointVerificationError,
"ignore_profile_state_conflict": ProfileStateConflictError} "ignore_profile_state_conflict": ProfileStateConflictError}
observer = ProfileObserver() observer = ProfileObserver()

View file

@ -57,8 +57,10 @@ class PaymentDetailsPage(Page):
return # error is already displayed in subfunction. return # error is already displayed in subfunction.
# launches background thread tasks: # launches background thread tasks:
gui_starts_invoice(profile_id=self.profile_id, ui_caller=self.receive_invoice) error_msg = gui_starts_invoice(profile_id=self.profile_id, invoice_ui_caller=self.receive_invoice, timer_resume_caller=self.timer_updates_gui)
if error_msg:
self.show_a_popup(error_msg)
def create_initial_elements(self): def create_initial_elements(self):
container = QWidget(self) container = QWidget(self)
@ -250,6 +252,9 @@ class PaymentDetailsPage(Page):
) )
self.fields["address"].setText(elided_text) self.fields["address"].setText(elided_text)
if self.currency:
self.fields["currency"].setText(self.currency.value)
# Now that we have the data populated, show the QR code: # Now that we have the data populated, show the QR code:
self.qr_code_button.setDisabled(False) self.qr_code_button.setDisabled(False)