Files
dbis_core/docs/nostro-vostro/plugin-development-guide.md

279 lines
6.8 KiB
Markdown
Raw Permalink Normal View History

2025-12-12 15:02:56 -08:00
# 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<boolean>;
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<string, unknown> = {}) {
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<string, unknown> = {}) {
super('MyCustomAdapter', '1.0.0', config);
this.apiEndpoint = config.apiEndpoint as string;
this.apiKey = config.apiKey as string;
}
async isAvailable(): Promise<boolean> {
// 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