91 lines
3.0 KiB
TypeScript
91 lines
3.0 KiB
TypeScript
/**
|
|
* 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<Result<CheckoutConfirmation>> {
|
|
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<CheckoutConfirmation>((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');
|
|
}
|
|
} |