One integration point. All 27 Licensed Financial Institutions across 8 ECCU territories. Built on the ECCB's Instant Payment System (IPS) data rail, aligned with the Greening the Financial System initiative and the Payment System and Services Act 2025/2026.
| Purpose Code | Label | GCF Category | Rio Marker | Status |
|---|---|---|---|---|
| ENRG | Energy / Solar PV | Mitigation — Renewable Energy | Marker 2 | ✅ Tracked |
| AGRI | Agriculture / CSA | Adaptation — Food Security | Marker 1 | ✅ Tracked |
| HLTH | Water & Health | Adaptation — Water Resources | Marker 2 | ✅ Tracked |
| TRAD | EV Fleet / Transport | Mitigation — Transport | Marker 2 | ✅ Tracked |
| OTHR | Resilient Housing | Adaptation — Infrastructure | Marker 2 | ✅ Tracked |
| CORT | General Commercial | Non-Climate (Flagged) | Marker 0 | ⏭ Skipped |
"""
GCFT Platform — ECCB IPS ISO 20022 Green Finance Listener
Connects to ECCB Payment System Hub via secure WebSocket (mTLS)
Filters transactions by ISO 20022 Purpose Code for climate-tagged loans
Pushes anonymized metadata to GCFT Regional MRV Dashboard
Legal basis: Payment System and Services Act 2025/2026 (ECCU)
Security: mTLS X.509 + HMAC-SHA256 payload signing
Data: PII masked per ECCB Data Protection Guidelines
"""
import asyncio, json, hashlib, hmac, os
from datetime import datetime, timezone
import websockets
ECCB_IPS_WS_URL = "wss://ips.eccb-centralbank.org/v1/transactions/stream"
GCFT_MRV_ENDPOINT = "https://api.gcftportal.org/api/v1/mrv/ingest"
MTLS_CERT = "/etc/gcft/certs/client.crt"
MTLS_KEY = "/etc/gcft/certs/client.key"
ECCB_CA_BUNDLE = "/etc/gcft/certs/eccb-ca.crt"
# ISO 20022 Purpose Codes mapped to GCF categories
GREEN_PURPOSE_CODES = {
"ENRG": {"gcf": "Mitigation — Renewable Energy", "rio": "2"},
"AGRI": {"gcf": "Adaptation — Food Security", "rio": "1"},
"HLTH": {"gcf": "Adaptation — Water Resources", "rio": "2"},
"TRAD": {"gcf": "Mitigation — Transport", "rio": "2"},
"OTHR": {"gcf": "Adaptation — Infrastructure", "rio": "2"},
}
def mask_pii(account_id: str) -> str:
"""SHA-256 hash of account ID — irreversible, ECCB-compliant"""
return hashlib.sha256(account_id.encode()).hexdigest()[:16]
def sign_payload(payload: dict, secret: str) -> str:
"""HMAC-SHA256 signature for GCF audit trail"""
msg = json.dumps(payload, sort_keys=True).encode()
return hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
async def process_transaction(tx: dict):
"""Parse ISO 20022 pacs.008 message and push to GCFT MRV"""
purp_code = tx.get("PurpCd", "")
if purp_code not in GREEN_PURPOSE_CODES:
return # Non-climate transaction — skip
mapping = GREEN_PURPOSE_CODES[purp_code]
payload = {
"msgId": tx["MsgId"],
"txId": tx["TxId"],
"bankBIC": tx["DbtrAgt"]["FinInstnId"]["BICFI"], # Bank BIC code
"territory": tx["DbtrAgt"]["FinInstnId"]["BICFI"][:2], # 2-char territory
"amtXCD": float(tx["IntrBkSttlmAmt"]["#text"]),
"amtUSD": float(tx["IntrBkSttlmAmt"]["#text"]) / 2.7, # ECCB fixed peg
"purposeCode": purp_code,
"gcfCategory": mapping["gcf"],
"rioMarker": mapping["rio"],
"borrowerRef": mask_pii(tx["Cdtr"]["Acct"]["Id"]["Othr"]["Id"]),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
sig = sign_payload(payload, os.environ["GCFT_HMAC_SECRET"])
import aiohttp
async with aiohttp.ClientSession() as session:
await session.post(
GCFT_MRV_ENDPOINT,
json=payload,
headers={
"Authorization": f"Bearer {os.environ['GCFT_API_KEY']}",
"X-GCFT-Signature": sig,
"X-ISO20022-MsgType": "pacs.008.001.08",
"X-ECCB-Territory": payload["territory"],
},
ssl=True
)
print(f"[GCFT] ✅ Green tx ingested: {purp_code} | {payload['territory']} | XCD {payload['amtXCD']:,.0f}")
async def listen_eccb_ips():
"""Main listener — connects to ECCB IPS WebSocket stream"""
import ssl
ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_ctx.load_cert_chain(MTLS_CERT, MTLS_KEY)
ssl_ctx.load_verify_locations(ECCB_CA_BUNDLE)
print("[GCFT] Connecting to ECCB IPS stream...")
async with websockets.connect(ECCB_IPS_WS_URL, ssl=ssl_ctx) as ws:
print("[GCFT] ✅ Connected to ECCB IPS. Listening for Green Finance codes...")
async for message in ws:
tx = json.loads(message)
await process_transaction(tx)
if __name__ == "__main__":
asyncio.run(listen_eccb_ips())