# Plugin Development Guide ## Overview This guide explains how to develop custom plugin adapters for integrating core banking systems with the DBIS Nostro/Vostro API. ## Plugin Architecture ### Base Adapter Interface All plugins must implement the `IPluginAdapter` interface: ```typescript interface IPluginAdapter { getName(): string; getVersion(): string; isAvailable(): Promise; mapParticipant(internalData: unknown): ParticipantCreateRequest; mapAccount(internalData: unknown): AccountCreateRequest; mapTransfer(internalData: unknown): TransferCreateRequest; mapTransferToInternal(transfer: NostroVostroTransfer): unknown; mapAccountToInternal(account: NostroVostroAccount): unknown; postTransfer(transfer: NostroVostroTransfer): Promise<{success: boolean; internalId?: string; error?: string}>; getAccountBalance(accountId: string): Promise<{balance: string; available: string; hold: string}>; reconcile(accountId: string, asOfDate: Date): Promise<{matched: number; breaks: unknown[]}>; } ``` ### Base Class Extend `BasePluginAdapter` for common functionality: ```typescript import { BasePluginAdapter } from '@/integration/plugins/generic-adapter'; export class MyCustomAdapter extends BasePluginAdapter { constructor(config: Record = {}) { super('MyCustomAdapter', '1.0.0', config); } // Implement required methods } ``` ## Implementation Steps ### 1. Create Adapter Class ```typescript // src/integration/plugins/my-custom-adapter.ts import { BasePluginAdapter } from './generic-adapter'; import { ParticipantCreateRequest, AccountCreateRequest, TransferCreateRequest, NostroVostroTransfer, NostroVostroAccount, } from '@/core/nostro-vostro/nostro-vostro.types'; export class MyCustomAdapter extends BasePluginAdapter { private apiEndpoint: string; private apiKey: string; constructor(config: Record = {}) { super('MyCustomAdapter', '1.0.0', config); this.apiEndpoint = config.apiEndpoint as string; this.apiKey = config.apiKey as string; } async isAvailable(): Promise { // Check connectivity to your system try { const response = await fetch(`${this.apiEndpoint}/health`, { headers: { 'Authorization': `Bearer ${this.apiKey}` } }); return response.ok; } catch { return false; } } // Implement other methods... } ``` ### 2. Implement Mapping Methods #### Participant Mapping ```typescript mapParticipant(internalData: unknown): ParticipantCreateRequest { const customer = internalData as YourInternalCustomer; return { participantId: customer.id, name: customer.name, bic: customer.bic, lei: customer.lei, country: customer.countryCode, regulatoryTier: this.mapRegulatoryTier(customer.category), metadata: { internalId: customer.id, // Additional metadata }, }; } ``` #### Account Mapping ```typescript mapAccount(internalData: unknown): AccountCreateRequest { const account = internalData as YourInternalAccount; return { ownerParticipantId: account.customerId, counterpartyParticipantId: account.correspondentId, ibanOrLocalAccount: account.accountNumber, currency: account.currency, accountType: account.type === 'NOSTRO' ? 'NOSTRO' : 'VOSTRO', metadata: { internalAccountId: account.id, }, }; } ``` #### Transfer Mapping ```typescript mapTransfer(internalData: unknown): TransferCreateRequest { const transaction = internalData as YourInternalTransaction; return { fromAccountId: transaction.debitAccount, toAccountId: transaction.creditAccount, amount: transaction.amount.toString(), currency: transaction.currency, valueDate: transaction.valueDate, reference: transaction.reference, metadata: { internalTransactionId: transaction.id, }, }; } ``` ### 3. Implement Posting Methods ```typescript async postTransfer(transfer: NostroVostroTransfer): Promise<{success: boolean; internalId?: string; error?: string}> { try { const internalTransaction = this.mapTransferToInternal(transfer); const response = await fetch(`${this.apiEndpoint}/transactions`, { method: 'POST', headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' }, body: JSON.stringify(internalTransaction) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); return { success: true, internalId: result.transactionId }; } catch (error) { return this.handleError(error, 'postTransfer'); } } ``` ### 4. Register Plugin ```typescript import { pluginRegistry } from '@/integration/plugins/plugin-registry'; import { MyCustomAdapter } from './my-custom-adapter'; // Register during application startup pluginRegistry.register('my-custom', new MyCustomAdapter({ apiEndpoint: process.env.MY_CUSTOM_API_ENDPOINT, apiKey: process.env.MY_CUSTOM_API_KEY, })); ``` ## Best Practices ### 1. Error Handling Always use the base class error handling: ```typescript try { // Your code } catch (error) { return this.handleError(error, 'methodName'); } ``` ### 2. Idempotency Support idempotency keys in your internal system: ```typescript mapTransferToInternal(transfer: NostroVostroTransfer): unknown { return { transactionId: transfer.transferId, idempotencyKey: transfer.idempotencyKey, // Pass through // ... other fields }; } ``` ### 3. Validation Validate data before mapping: ```typescript mapTransfer(internalData: unknown): TransferCreateRequest { const tx = internalData as YourTransaction; if (!tx.amount || tx.amount <= 0) { throw new Error('Invalid amount'); } // Continue mapping... } ``` ### 4. Logging Log important operations: ```typescript async postTransfer(transfer: NostroVostroTransfer): Promise<...> { console.log(`[${this.name}] Posting transfer ${transfer.transferId}`); // ... implementation } ``` ## Testing ### Unit Tests ```typescript describe('MyCustomAdapter', () => { let adapter: MyCustomAdapter; beforeEach(() => { adapter = new MyCustomAdapter({ apiEndpoint: 'http://localhost:3000', apiKey: 'test-key', }); }); it('should map participant correctly', () => { const internal = { id: '123', name: 'Test Bank', ... }; const result = adapter.mapParticipant(internal); expect(result.name).toBe('Test Bank'); }); }); ``` ### Integration Tests Test against a sandbox environment of your core banking system. ## Reference Implementations See existing adapters for examples: - `temenos-adapter.ts` - Temenos T24 integration - `flexcube-adapter.ts` - Oracle Flexcube integration - `swift-adapter.ts` - SWIFT message handling - `iso20022-adapter.ts` - ISO 20022 message handling