import type { Plan } from "../types/plan"; import { generatePacs008 } from "./iso20022"; import { logger } from "../logging/logger"; /** * Bank-instruction client — two-phase-commit adapter for the payment * leg (arch §4.3 Payment Messaging / Settlement Layer). * * Until `d-bis/dbis_core` is reachable as a live API, every call here * is a deterministic mock. That corresponds to blocker EXT-DBIS-CORE * in proxmox/docs/03-deployment/EXTERNAL_DEPENDENCY_BLOCKERS.md and * flips to real once DBIS_CORE_URL is set (see services/dbisCore/). */ const BLOCKER_ID = "EXT-DBIS-CORE"; function bankMode(): "live" | "mock" { return process.env.DBIS_CORE_URL ? "live" : "mock"; } /** * Prepare bank instruction (2PC prepare phase) * Sends provisional ISO-20022 message */ export async function prepareBankInstruction(plan: Plan): Promise { const mode = bankMode(); logger.info( { planId: plan.plan_id, mode, ...(mode === "mock" ? { blockerId: BLOCKER_ID } : {}), }, "[Bank] prepareBankInstruction()", ); // Mock: In real implementation, this would: // 1. Generate provisional ISO-20022 message (pacs.008 with conditional settlement) // 2. Send to bank connector // 3. Receive provisional acceptance await new Promise((resolve) => setTimeout(resolve, 100)); return true; } /** * Commit bank instruction (2PC commit phase) * Confirms final settlement */ export async function commitBankInstruction(plan: Plan): Promise<{ success: boolean; isoMessageId?: string; error?: string; }> { const mode = bankMode(); logger.info( { planId: plan.plan_id, mode, ...(mode === "mock" ? { blockerId: BLOCKER_ID } : {}), }, "[Bank] commitBankInstruction()", ); try { // Generate final ISO-20022 message const isoMessage = await generatePacs008(plan); // Mock: In real implementation, this would: // 1. Send ISO message to bank connector // 2. Receive confirmation and message ID // 3. Store message ID for audit trail const isoMessageId = `MSG-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; // Simulate processing delay await new Promise((resolve) => setTimeout(resolve, 300)); return { success: true, isoMessageId, }; } catch (err) { const error = err instanceof Error ? err.message : String(err); return { success: false, error, }; } } /** * Abort bank instruction (2PC abort phase) * Cancels provisional instruction */ export async function abortBankInstruction(planId: string): Promise { const mode = bankMode(); logger.info( { planId, mode, ...(mode === "mock" ? { blockerId: BLOCKER_ID } : {}), }, "[Bank] abortBankInstruction()", ); // Mock: In real implementation, this would: // 1. Generate cancellation message (camt.056) // 2. Send to bank connector // 3. Confirm cancellation await new Promise((resolve) => setTimeout(resolve, 100)); }