/** * ElectronCheckoutConfirmationAdapter * Implements ICheckoutConfirmationPort using Electron IPC for main-renderer communication. */ import type { BrowserWindow } from 'electron'; import { ipcMain } from 'electron'; import { Result } from '@core/shared/application/Result'; import type { CheckoutConfirmationPort } from '../../../application/ports/CheckoutConfirmationPort'; import type { CheckoutConfirmationRequestDTO } from '../../../application/dto/CheckoutConfirmationRequestDTO'; import { CheckoutConfirmation } from '../../../domain/value-objects/CheckoutConfirmation'; export class ElectronCheckoutConfirmationAdapter implements CheckoutConfirmationPort { private mainWindow: BrowserWindow; private pendingConfirmation: { resolve: (confirmation: CheckoutConfirmation) => void; reject: (error: Error) => void; timeoutId: NodeJS.Timeout; } | null = null; constructor(mainWindow: BrowserWindow) { this.mainWindow = mainWindow; this.setupIpcHandlers(); } private setupIpcHandlers(): void { // Listen for confirmation response from renderer ipcMain.on('checkout:confirm', (_event, decision: 'confirmed' | 'cancelled' | 'timeout') => { if (!this.pendingConfirmation) { return; } // Clear timeout clearTimeout(this.pendingConfirmation.timeoutId); // Create confirmation based on decision const confirmation = CheckoutConfirmation.create(decision); this.pendingConfirmation.resolve(confirmation); this.pendingConfirmation = null; }); } async requestCheckoutConfirmation( request: CheckoutConfirmationRequestDTO ): Promise> { try { // Only allow one pending confirmation at a time if (this.pendingConfirmation) { return Result.err(new Error('Confirmation already pending')); } // Send request to renderer this.mainWindow.webContents.send('checkout:request-confirmation', { price: request.price.toDisplayString(), state: request.state.isReady() ? 'ready' : 'insufficient_funds', sessionMetadata: request.sessionMetadata, timeoutMs: request.timeoutMs, }); // Wait for response with timeout const confirmation = await new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { this.pendingConfirmation = null; const timeoutConfirmation = CheckoutConfirmation.create('timeout'); resolve(timeoutConfirmation); }, request.timeoutMs); this.pendingConfirmation = { resolve, reject, timeoutId, }; }); return Result.ok(confirmation); } catch (error) { this.pendingConfirmation = null; return Result.err( error instanceof Error ? error : new Error('Failed to request confirmation') ); } } public cleanup(): void { if (this.pendingConfirmation) { clearTimeout(this.pendingConfirmation.timeoutId); this.pendingConfirmation = null; } ipcMain.removeAllListeners('checkout:confirm'); } }