import os from PyQt6.QtWidgets import QButtonGroup, QMessageBox, QPushButton from PyQt6.QtGui import QIcon from PyQt6.QtCore import QSize from PyQt6 import QtCore from core.services.prepare_tickets.ticket_tracker import delete_ticket_data 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.workers.ticketing_worker_thread import TicketingWorkerThread HOW_MANY_PROFILES_DEFAULT = 6 class TicketCryptoPickerPage(Page): def __init__(self, page_stack, main_window=None, parent=None): super().__init__("TicketCrypto", page_stack, main_window, parent) self.update_status = main_window self.selected_plan = None self.bypass_existing = False self.worker = None self.title.setText("Payment Method") self.title.setGeometry(QtCore.QRect(510, 30, 250, 40)) self.title.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.button_reverse.setVisible(True) self.button_reverse.clicked.connect(self.reverse) button_info = [ ("monero", "monero", (545, 75)), ("bitcoin", "bitcoin", (545, 290)), ("lightning", "lightnering", (545, 180)), ("litecoin", "litecoin", (545, 395)), ] self.buttonGroup = QButtonGroup(self) self.buttons = [] for j, (currency, icon_name, position) in enumerate(button_info): button = QPushButton(self) button.setGeometry(position[0], position[1], 185, 75) button.setIconSize(QSize(190, 120)) button.setCheckable(True) button.setIcon(QIcon(os.path.join(self.btn_path, f"{icon_name}.png"))) button.setProperty('currency', currency) self.buttons.append(button) self.buttonGroup.addButton(button, j) button.clicked.connect(self.on_currency_selected) def set_selected_plan(self, plan_key): self.selected_plan = plan_key self.bypass_existing = False self.update_status.update_status(f"Selected plan: {plan_key}") for btn in self.buttons: btn.setChecked(False) def on_currency_selected(self): selected_button = self.buttonGroup.checkedButton() if not selected_button or not self.selected_plan: return currency = selected_button.property('currency') self.start_initiate_payment(currency) def start_initiate_payment(self, currency): self.update_status.update_status("Initiating payment...") self.worker = TicketingWorkerThread('INITIATE_PAYMENT', params={ 'how_many_profiles': HOW_MANY_PROFILES_DEFAULT, 'which_key': self.selected_plan, 'which_cryptocurrency': currency, 'bypass_existing': self.bypass_existing, }) self.worker.invoice_ready.connect(self.on_invoice_ready) self.worker.error.connect(self.on_error) self.worker.start() def on_invoice_ready(self, invoice): if invoice is None or invoice is False: self.update_status.update_status("Could not initiate payment.") return if not getattr(invoice, 'is_valid', False): return self._handle_api_errors(invoice) self.custom_window.navigator.navigate("payment_details") payment_page = self.custom_window.navigator.get_cached("payment_details") if payment_page is not None: payment_page.set_ticket_invoice(invoice, self.selected_plan) def _handle_api_errors(self, invoice): error_code = getattr(invoice, 'error_code', None) if error_code == "already_exists" and not self.bypass_existing: self._prompt_wipe_existing() return elif error_code == "billing_code_exists" and not self.bypass_existing: temp_billing_code = getattr(invoice, 'temp_billing_code', None) self._prompt_wipe_existing(temp_billing_code) return else: error_msg = getattr(invoice, 'final_error_msg', None) or error_code or "Could not initiate payment." self.update_status.update_status(error_msg) return def _prompt_wipe_existing(self, temp_billing_code=None): msg = QMessageBox(self) if temp_billing_code: msg.setWindowTitle("Existing billing code found") msg.setText("You already have a ticket billing code. Use it, or wipe it and start over?") use_button = msg.addButton("Use existing", QMessageBox.ButtonRole.AcceptRole) wipe_button = msg.addButton("Wipe and start over", QMessageBox.ButtonRole.DestructiveRole) cancel_button = msg.addButton("Cancel", QMessageBox.ButtonRole.RejectRole) mark_confirm_button(use_button) else: msg.setWindowTitle("Existing tickets found") msg.setText("You already have ticket data. Wipe it and start over?") use_button = None wipe_button = msg.addButton("Wipe and start over", QMessageBox.ButtonRole.DestructiveRole) cancel_button = msg.addButton("Cancel", QMessageBox.ButtonRole.RejectRole) mark_confirm_button(wipe_button) style_message_box(msg) msg.exec() clicked = msg.clickedButton() if temp_billing_code and clicked == use_button: self.update_status.update_status("Reusing existing billing code") self.custom_window.navigator.navigate("payment_details") payment_page = self.custom_window.navigator.get_cached("payment_details") if payment_page is not None: payment_page.resume_ticket_billing(temp_billing_code) return if clicked == wipe_button: self._restart_payment_after_wipe() else: self.update_status.update_status("Cancelled.") def _restart_payment_after_wipe(self): if not delete_ticket_data(): self.update_status.update_status("Could not wipe ticket data.") return self.bypass_existing = False currency_btn = self.buttonGroup.checkedButton() if currency_btn: self.start_initiate_payment(currency_btn.property('currency')) def on_error(self, msg): self.update_status.update_status(f"Payment error: {msg}") def reverse(self): self.custom_window.navigator.navigate("plan_picker")