601 lines
14 KiB
Markdown
601 lines
14 KiB
Markdown
|
|
# Compliance Integration Specification
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
This document specifies compliance integration requirements for the ISO-20022 Combo Flow system, including LEI/DID/KYC/AML injection into ISO messages, compliance engine API contract, real-time status checks, identity assertion format, and audit trail requirements for hybrid workflows.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Compliance Requirements
|
||
|
|
|
||
|
|
### Required Compliance Attributes
|
||
|
|
|
||
|
|
| Attribute | Required For | Description | Format |
|
||
|
|
|-----------|-------------|-------------|--------|
|
||
|
|
| **LEI** | All workflows | Legal Entity Identifier | 20-character alphanumeric (e.g., `5493000IBP32UQZ0KL24`) |
|
||
|
|
| **DID** | Notarized workflows | Decentralized Identifier | W3C DID format (e.g., `did:web:example.com:user:123`) |
|
||
|
|
| **KYC** | Fiat/DTL steps | Know Your Customer verification | Level 1-3 (Level 2+ for fiat) |
|
||
|
|
| **AML** | Payments > threshold | Anti-Money Laundering check | Pass/Fail with risk level |
|
||
|
|
|
||
|
|
### Compliance Levels by Workflow Type
|
||
|
|
|
||
|
|
#### DeFi-Only Workflows
|
||
|
|
- **LEI**: Optional (recommended)
|
||
|
|
- **DID**: Optional
|
||
|
|
- **KYC**: Not required
|
||
|
|
- **AML**: Not required
|
||
|
|
|
||
|
|
#### Hybrid Workflows (DeFi + Fiat/DTL)
|
||
|
|
- **LEI**: Required
|
||
|
|
- **DID**: Required for notarization
|
||
|
|
- **KYC**: Level 2+ required
|
||
|
|
- **AML**: Required for payments > 10,000 EUR
|
||
|
|
|
||
|
|
#### Fiat-Only Workflows
|
||
|
|
- **LEI**: Required
|
||
|
|
- **DID**: Required
|
||
|
|
- **KYC**: Level 2+ required
|
||
|
|
- **AML**: Required for all payments
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Compliance Engine API Contract
|
||
|
|
|
||
|
|
### Interface: `IComplianceEngine`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface IComplianceEngine {
|
||
|
|
/**
|
||
|
|
* Check compliance status for a user
|
||
|
|
*/
|
||
|
|
getComplianceStatus(userId: string): Promise<ComplianceStatus>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Validate compliance for a workflow
|
||
|
|
*/
|
||
|
|
validateWorkflowCompliance(workflow: Workflow): Promise<ComplianceCheckResult>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Run KYC verification
|
||
|
|
*/
|
||
|
|
verifyKYC(userId: string, level: number): Promise<KYCResult>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Run AML screening
|
||
|
|
*/
|
||
|
|
screenAML(userId: string, amount: number, currency: string): Promise<AMLResult>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register LEI for user
|
||
|
|
*/
|
||
|
|
registerLEI(userId: string, lei: string): Promise<boolean>;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Register DID for user
|
||
|
|
*/
|
||
|
|
registerDID(userId: string, did: string): Promise<boolean>;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Compliance Status Response
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface ComplianceStatus {
|
||
|
|
userId: string;
|
||
|
|
lei: string | null;
|
||
|
|
did: string | null;
|
||
|
|
kyc: {
|
||
|
|
level: number;
|
||
|
|
provider: string;
|
||
|
|
verified: boolean;
|
||
|
|
expiresAt: string | null;
|
||
|
|
};
|
||
|
|
aml: {
|
||
|
|
passed: boolean;
|
||
|
|
provider: string;
|
||
|
|
lastCheck: string;
|
||
|
|
riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
|
||
|
|
};
|
||
|
|
valid: boolean;
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Workflow Compliance Check
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface ComplianceCheckResult {
|
||
|
|
valid: boolean;
|
||
|
|
required: string[]; // ['LEI', 'DID', 'KYC', 'AML']
|
||
|
|
missing: string[];
|
||
|
|
warnings: string[];
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. LEI/DID/KYC/AML Injection into ISO Messages
|
||
|
|
|
||
|
|
### ISO-20022 Message Structure with Compliance
|
||
|
|
|
||
|
|
#### pacs.008 (Payment Instruction) Example
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
||
|
|
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.10">
|
||
|
|
<FIToFICstmrCdtTrf>
|
||
|
|
<GrpHdr>
|
||
|
|
<MsgId>MSG-2025-01-15-001</MsgId>
|
||
|
|
<CreDtTm>2025-01-15T10:30:00Z</CreDtTm>
|
||
|
|
<NbOfTxs>1</NbOfTxs>
|
||
|
|
<CtrlSum>78000.00</CtrlSum>
|
||
|
|
<InitgPty>
|
||
|
|
<Nm>Example Corp Ltd.</Nm>
|
||
|
|
<Id>
|
||
|
|
<OrgId>
|
||
|
|
<Othr>
|
||
|
|
<Id>5493000IBP32UQZ0KL24</Id>
|
||
|
|
<SchmeNm>
|
||
|
|
<Cd>LEI</Cd>
|
||
|
|
</SchmeNm>
|
||
|
|
</Othr>
|
||
|
|
</OrgId>
|
||
|
|
</Id>
|
||
|
|
</InitgPty>
|
||
|
|
</GrpHdr>
|
||
|
|
<CdtTrfTxInf>
|
||
|
|
<PmtId>
|
||
|
|
<EndToEndId>PLAN-12345678-ABCD-EFGH</EndToEndId>
|
||
|
|
<TxId>TX-2025-01-15-001</TxId>
|
||
|
|
</PmtId>
|
||
|
|
<PmtTpInf>
|
||
|
|
<SvcLvl>
|
||
|
|
<Cd>SEPA</Cd>
|
||
|
|
</SvcLvl>
|
||
|
|
<LclInstrm>
|
||
|
|
<Cd>INST</Cd>
|
||
|
|
</LclInstrm>
|
||
|
|
</PmtTpInf>
|
||
|
|
<IntrBkSttlmAmt Ccy="EUR">78000.00</IntrBkSttlmAmt>
|
||
|
|
<InstgAgt>
|
||
|
|
<FinInstnId>
|
||
|
|
<BICFI>BANKDEFFXXX</BICFI>
|
||
|
|
</FinInstnId>
|
||
|
|
</InstgAgt>
|
||
|
|
<InstdAgt>
|
||
|
|
<FinInstnId>
|
||
|
|
<BICFI>BENEFRPPXXX</BICFI>
|
||
|
|
</FinInstnId>
|
||
|
|
</InstdAgt>
|
||
|
|
<Dbtr>
|
||
|
|
<Nm>Example Corp Ltd.</Nm>
|
||
|
|
<Id>
|
||
|
|
<OrgId>
|
||
|
|
<Othr>
|
||
|
|
<Id>5493000IBP32UQZ0KL24</Id>
|
||
|
|
<SchmeNm>
|
||
|
|
<Cd>LEI</Cd>
|
||
|
|
</SchmeNm>
|
||
|
|
</Othr>
|
||
|
|
</OrgId>
|
||
|
|
</Id>
|
||
|
|
<CtctDtls>
|
||
|
|
<Email>compliance@example.com</Email>
|
||
|
|
</CtctDtls>
|
||
|
|
</Dbtr>
|
||
|
|
<DbtrAcct>
|
||
|
|
<Id>
|
||
|
|
<IBAN>DE89370400440532013000</IBAN>
|
||
|
|
</Id>
|
||
|
|
</DbtrAcct>
|
||
|
|
<Cdtr>
|
||
|
|
<Nm>Beneficiary Corp</Nm>
|
||
|
|
<PstlAdr>
|
||
|
|
<StrtNm>Main Street</StrtNm>
|
||
|
|
<BldgNb>123</BldgNb>
|
||
|
|
<PstCd>12345</PstCd>
|
||
|
|
<TwnNm>Berlin</TwnNm>
|
||
|
|
<Ctry>DE</Ctry>
|
||
|
|
</PstlAdr>
|
||
|
|
</Cdtr>
|
||
|
|
<CdtrAcct>
|
||
|
|
<Id>
|
||
|
|
<IBAN>DE89370400440532013001</IBAN>
|
||
|
|
</Id>
|
||
|
|
</CdtrAcct>
|
||
|
|
<RmtInf>
|
||
|
|
<Ustrd>Plan ID: PLAN-12345678-ABCD-EFGH</Ustrd>
|
||
|
|
<Strd>
|
||
|
|
<RfrdDocInf>
|
||
|
|
<Tp>
|
||
|
|
<CdOrPrtry>
|
||
|
|
<Cd>CINV</Cd>
|
||
|
|
</CdOrPrtry>
|
||
|
|
</Tp>
|
||
|
|
<Nb>PLAN-12345678-ABCD-EFGH</Nb>
|
||
|
|
<RltdDt>2025-01-15</RltdDt>
|
||
|
|
</RfrdDocInf>
|
||
|
|
<RfrdDocAmt>
|
||
|
|
<DuePyblAmt Ccy="EUR">78000.00</DuePyblAmt>
|
||
|
|
</RfrdDocAmt>
|
||
|
|
</Strd>
|
||
|
|
</RmtInf>
|
||
|
|
<SplmtryData>
|
||
|
|
<PlcAndNm>ComplianceData</PlcAndNm>
|
||
|
|
<Envlp>
|
||
|
|
<ComplianceData xmlns="urn:example:compliance:xsd:1.0">
|
||
|
|
<PlanId>PLAN-12345678-ABCD-EFGH</PlanId>
|
||
|
|
<LEI>5493000IBP32UQZ0KL24</LEI>
|
||
|
|
<DID>did:web:example.com:user:123</DID>
|
||
|
|
<KYC>
|
||
|
|
<Level>2</Level>
|
||
|
|
<Provider>Onfido</Provider>
|
||
|
|
<Verified>true</Verified>
|
||
|
|
<ExpiresAt>2026-12-31T23:59:59Z</ExpiresAt>
|
||
|
|
</KYC>
|
||
|
|
<AML>
|
||
|
|
<Passed>true</Passed>
|
||
|
|
<Provider>Chainalysis</Provider>
|
||
|
|
<LastCheck>2025-01-15T09:00:00Z</LastCheck>
|
||
|
|
<RiskLevel>LOW</RiskLevel>
|
||
|
|
</AML>
|
||
|
|
<DigitalSignature>
|
||
|
|
<Algorithm>ECDSA</Algorithm>
|
||
|
|
<Value>0x1234567890abcdef...</Value>
|
||
|
|
</DigitalSignature>
|
||
|
|
</ComplianceData>
|
||
|
|
</Envlp>
|
||
|
|
</SplmtryData>
|
||
|
|
</CdtTrfTxInf>
|
||
|
|
</FIToFICstmrCdtTrf>
|
||
|
|
</Document>
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Compliance Injection Points
|
||
|
|
|
||
|
|
1. **Header (`GrpHdr.InitgPty`)**: LEI in `Id.OrgId.Othr.Id` with `SchmeNm.Cd = "LEI"`
|
||
|
|
2. **Debtor (`Dbtr`)**: LEI in `Id.OrgId.Othr.Id`
|
||
|
|
3. **Supplementary Data (`SplmtryData`)**: Full compliance data (LEI, DID, KYC, AML, signature)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Real-Time Status Checks
|
||
|
|
|
||
|
|
### API Endpoint: `GET /api/compliance/status`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Request
|
||
|
|
GET /api/compliance/status?userId=user123
|
||
|
|
|
||
|
|
// Response
|
||
|
|
{
|
||
|
|
"userId": "user123",
|
||
|
|
"lei": "5493000IBP32UQZ0KL24",
|
||
|
|
"did": "did:web:example.com:user:123",
|
||
|
|
"kyc": {
|
||
|
|
"level": 2,
|
||
|
|
"provider": "Onfido",
|
||
|
|
"verified": true,
|
||
|
|
"expiresAt": "2026-12-31T23:59:59Z"
|
||
|
|
},
|
||
|
|
"aml": {
|
||
|
|
"passed": true,
|
||
|
|
"provider": "Chainalysis",
|
||
|
|
"lastCheck": "2025-01-15T09:00:00Z",
|
||
|
|
"riskLevel": "LOW"
|
||
|
|
},
|
||
|
|
"valid": true
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Workflow Compliance Check: `POST /api/compliance/check`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Request
|
||
|
|
POST /api/compliance/check
|
||
|
|
{
|
||
|
|
"steps": [
|
||
|
|
{ "type": "borrow", "asset": "CBDC_USD", "amount": 100000 },
|
||
|
|
{ "type": "swap", "from": "CBDC_USD", "to": "CBDC_EUR", "amount": 100000 },
|
||
|
|
{ "type": "pay", "asset": "EUR", "amount": 78000, "beneficiary": { "IBAN": "DE89..." } }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
// Response
|
||
|
|
{
|
||
|
|
"valid": true,
|
||
|
|
"required": ["LEI", "DID", "KYC", "AML"],
|
||
|
|
"missing": [],
|
||
|
|
"warnings": []
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Frontend Integration
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Real-time compliance badge in UI
|
||
|
|
const ComplianceBadge = () => {
|
||
|
|
const { data: compliance } = useQuery(['compliance'], () =>
|
||
|
|
api.getComplianceStatus()
|
||
|
|
);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="flex gap-2">
|
||
|
|
{compliance?.lei && <Badge>✓ LEI</Badge>}
|
||
|
|
{compliance?.did && <Badge>✓ DID</Badge>}
|
||
|
|
{compliance?.kyc?.verified && <Badge>✓ KYC</Badge>}
|
||
|
|
{compliance?.aml?.passed && <Badge>✓ AML</Badge>}
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Identity Assertion Format
|
||
|
|
|
||
|
|
### W3C Verifiable Credential Format
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"@context": [
|
||
|
|
"https://www.w3.org/2018/credentials/v1",
|
||
|
|
"https://www.w3.org/2018/credentials/examples/v1"
|
||
|
|
],
|
||
|
|
"id": "https://example.com/credentials/123",
|
||
|
|
"type": ["VerifiableCredential", "ComplianceCredential"],
|
||
|
|
"issuer": {
|
||
|
|
"id": "did:web:example.com:issuer",
|
||
|
|
"name": "Compliance Authority"
|
||
|
|
},
|
||
|
|
"issuanceDate": "2025-01-15T10:00:00Z",
|
||
|
|
"credentialSubject": {
|
||
|
|
"id": "did:web:example.com:user:123",
|
||
|
|
"lei": "5493000IBP32UQZ0KL24",
|
||
|
|
"kyc": {
|
||
|
|
"level": 2,
|
||
|
|
"provider": "Onfido",
|
||
|
|
"verified": true,
|
||
|
|
"expiresAt": "2026-12-31T23:59:59Z"
|
||
|
|
},
|
||
|
|
"aml": {
|
||
|
|
"passed": true,
|
||
|
|
"provider": "Chainalysis",
|
||
|
|
"lastCheck": "2025-01-15T09:00:00Z",
|
||
|
|
"riskLevel": "LOW"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"proof": {
|
||
|
|
"type": "Ed25519Signature2020",
|
||
|
|
"created": "2025-01-15T10:00:00Z",
|
||
|
|
"verificationMethod": "did:web:example.com:issuer#key-1",
|
||
|
|
"proofPurpose": "assertionMethod",
|
||
|
|
"proofValue": "z5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5Vz5V"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Entra Verified ID Integration
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Request verified credential from Entra
|
||
|
|
const requestCredential = async (userId: string) => {
|
||
|
|
const response = await fetch('https://verifiedid.did.msidentity.com/v1.0/verifiableCredentials/request', {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Authorization': `Bearer ${accessToken}`,
|
||
|
|
'Content-Type': 'application/json'
|
||
|
|
},
|
||
|
|
body: JSON.stringify({
|
||
|
|
credentialType: 'ComplianceCredential',
|
||
|
|
claims: {
|
||
|
|
lei: user.lei,
|
||
|
|
kycLevel: user.kyc.level
|
||
|
|
}
|
||
|
|
})
|
||
|
|
});
|
||
|
|
|
||
|
|
return response.json();
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Audit Trail Requirements
|
||
|
|
|
||
|
|
### Audit Log Structure
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface AuditLog {
|
||
|
|
planId: string;
|
||
|
|
timestamp: string;
|
||
|
|
event: 'PLAN_CREATED' | 'COMPLIANCE_CHECKED' | 'EXECUTION_STARTED' | 'EXECUTION_COMPLETED' | 'EXECUTION_FAILED';
|
||
|
|
userId: string;
|
||
|
|
compliance: {
|
||
|
|
lei: string;
|
||
|
|
did: string;
|
||
|
|
kyc: KYCStatus;
|
||
|
|
aml: AMLStatus;
|
||
|
|
};
|
||
|
|
metadata: {
|
||
|
|
steps: PlanStep[];
|
||
|
|
dltTxHash?: string;
|
||
|
|
isoMessageId?: string;
|
||
|
|
notaryProof?: string;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Audit Trail Storage
|
||
|
|
|
||
|
|
1. **On-Chain (NotaryRegistry)**: Immutable proof hashes
|
||
|
|
2. **Off-Chain (Database)**: Full audit logs with compliance data
|
||
|
|
3. **ISO Messages**: Compliance data embedded in messages
|
||
|
|
|
||
|
|
### Compliance Audit Report
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
interface ComplianceAuditReport {
|
||
|
|
planId: string;
|
||
|
|
executionDate: string;
|
||
|
|
user: {
|
||
|
|
userId: string;
|
||
|
|
lei: string;
|
||
|
|
did: string;
|
||
|
|
};
|
||
|
|
compliance: {
|
||
|
|
kyc: {
|
||
|
|
level: number;
|
||
|
|
provider: string;
|
||
|
|
verified: boolean;
|
||
|
|
expiresAt: string;
|
||
|
|
};
|
||
|
|
aml: {
|
||
|
|
passed: boolean;
|
||
|
|
provider: string;
|
||
|
|
lastCheck: string;
|
||
|
|
riskLevel: string;
|
||
|
|
};
|
||
|
|
};
|
||
|
|
workflow: {
|
||
|
|
steps: PlanStep[];
|
||
|
|
totalAmount: number;
|
||
|
|
currency: string;
|
||
|
|
};
|
||
|
|
receipts: {
|
||
|
|
dltTxHash: string;
|
||
|
|
isoMessageId: string;
|
||
|
|
notaryProof: string;
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. Compliance Workflow Integration
|
||
|
|
|
||
|
|
### Step 1: User Registration
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// User registers LEI and DID
|
||
|
|
await complianceEngine.registerLEI(userId, lei);
|
||
|
|
await complianceEngine.registerDID(userId, did);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: KYC Verification
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Run KYC verification (Level 2+ for fiat workflows)
|
||
|
|
const kycResult = await complianceEngine.verifyKYC(userId, 2);
|
||
|
|
if (!kycResult.verified) {
|
||
|
|
throw new Error('KYC verification failed');
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 3: AML Screening
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Run AML screening for payments > threshold
|
||
|
|
const amlResult = await complianceEngine.screenAML(userId, amount, 'EUR');
|
||
|
|
if (!amlResult.passed) {
|
||
|
|
throw new Error('AML screening failed');
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 4: Workflow Compliance Check
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Validate compliance before execution
|
||
|
|
const complianceCheck = await complianceEngine.validateWorkflowCompliance(workflow);
|
||
|
|
if (!complianceCheck.valid) {
|
||
|
|
throw new Error(`Missing compliance: ${complianceCheck.missing.join(', ')}`);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 5: ISO Message Generation
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Generate ISO message with compliance data
|
||
|
|
const isoMessage = generateISO20022Message(plan, {
|
||
|
|
lei: complianceStatus.lei,
|
||
|
|
did: complianceStatus.did,
|
||
|
|
kyc: complianceStatus.kyc,
|
||
|
|
aml: complianceStatus.aml,
|
||
|
|
signature: planSignature
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. Error Handling
|
||
|
|
|
||
|
|
### Compliance Validation Failures
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Compliance check fails
|
||
|
|
if (!complianceCheck.valid) {
|
||
|
|
return {
|
||
|
|
error: 'COMPLIANCE_REQUIRED',
|
||
|
|
message: `Missing compliance attributes: ${complianceCheck.missing.join(', ')}`,
|
||
|
|
missing: complianceCheck.missing,
|
||
|
|
required: complianceCheck.required
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### KYC Expiration Warning
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Check if KYC is expiring soon
|
||
|
|
if (complianceStatus.kyc.expiresAt) {
|
||
|
|
const expiresAt = new Date(complianceStatus.kyc.expiresAt);
|
||
|
|
const daysUntilExpiry = (expiresAt.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
|
||
|
|
|
||
|
|
if (daysUntilExpiry < 30) {
|
||
|
|
return {
|
||
|
|
warning: 'KYC_EXPIRING_SOON',
|
||
|
|
message: `KYC expires in ${daysUntilExpiry} days`,
|
||
|
|
expiresAt: complianceStatus.kyc.expiresAt
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. Testing Requirements
|
||
|
|
|
||
|
|
### Unit Tests
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
describe('ComplianceEngine', () => {
|
||
|
|
it('should validate LEI format', () => {
|
||
|
|
expect(validateLEI('5493000IBP32UQZ0KL24')).toBe(true);
|
||
|
|
expect(validateLEI('invalid')).toBe(false);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should check workflow compliance', async () => {
|
||
|
|
const result = await complianceEngine.validateWorkflowCompliance(workflow);
|
||
|
|
expect(result.valid).toBe(true);
|
||
|
|
expect(result.missing).toEqual([]);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Integration Tests
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
describe('ISO Message Generation', () => {
|
||
|
|
it('should inject compliance data into pacs.008', () => {
|
||
|
|
const message = generateISO20022Message(plan, complianceData);
|
||
|
|
expect(message).toContain('<LEI>5493000IBP32UQZ0KL24</LEI>');
|
||
|
|
expect(message).toContain('<DID>did:web:example.com:user:123</DID>');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Document Version**: 1.0
|
||
|
|
**Last Updated**: 2025-01-15
|
||
|
|
**Author**: Compliance Team
|
||
|
|
|