Initial commit
This commit is contained in:
278
docs/nostro-vostro/plugin-development-guide.md
Normal file
278
docs/nostro-vostro/plugin-development-guide.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user