/** * MT202 COV — General Financial Institution Transfer (cover method). * * Arch §4.3. FIN equivalent of pacs.009 used on SWIFT networks that * have not yet migrated to ISO 20022. Generated alongside pacs.009 * during transitional period — settlement confirmation can arrive on * either channel. */ import type { Plan, PlanStep } from "../../types/plan"; export interface Mt202Options { transactionReference: string; relatedReference?: string; valueDate: string; // YYYY-MM-DD sendingInstitution: string; // BIC receivingInstitution: string;// BIC beneficiaryInstitution: string; // BIC orderingInstitution?: string;// BIC } export interface Mt202Message { sender: string; receiver: string; fin: string; fields: Record; } function yyMMdd(iso: string): string { const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso); if (!m) throw new Error(`MT202: valueDate must be YYYY-MM-DD, got '${iso}'`); return `${m[1].slice(2)}${m[2]}${m[3]}`; } function bicCheck(bic: string, field: string): void { if (!/^[A-Z0-9]{8}([A-Z0-9]{3})?$/.test(bic)) { throw new Error(`MT202: ${field} must be a valid BIC, got '${bic}'`); } } function findPayStep(plan: Plan): PlanStep { const step = plan.steps.find((s) => s.type === "pay"); if (!step) throw new Error("MT202: plan must contain a 'pay' step"); return step; } export function generateMt202(plan: Plan, opts: Mt202Options): Mt202Message { bicCheck(opts.sendingInstitution, "sendingInstitution"); bicCheck(opts.receivingInstitution, "receivingInstitution"); bicCheck(opts.beneficiaryInstitution, "beneficiaryInstitution"); if (opts.orderingInstitution) bicCheck(opts.orderingInstitution, "orderingInstitution"); const payStep = findPayStep(plan); const ccy = (payStep.asset ?? "USD").toUpperCase(); const amount = payStep.amount.toFixed(2).replace(".", ","); const field32A = `${yyMMdd(opts.valueDate)}${ccy}${amount}`; const fields: Record = { "20": opts.transactionReference, "21": opts.relatedReference ?? opts.transactionReference, "32A": field32A, "52A": opts.orderingInstitution ?? opts.sendingInstitution, "57A": opts.receivingInstitution, "58A": opts.beneficiaryInstitution, }; const block1 = `{1:F01${opts.sendingInstitution.padEnd(12, "X")}0000000000}`; const block2 = `{2:I202${opts.receivingInstitution.padEnd(12, "X")}N}`; const block4 = Object.entries(fields).map(([t, v]) => `:${t}:${v}`).join("\n"); const block4Wrapped = `{4:\n${block4}\n-}`; return { sender: opts.sendingInstitution, receiver: opts.receivingInstitution, fin: `${block1}${block2}${block4Wrapped}`, fields, }; }