| name | open-banking-iso20022-apis |
| description | Architect enterprise Open Banking platforms, PSD2/PSD3 compliant APIs, financial message modeling in ISO 20022 (pacs.008, camt.053, pain.001 XML/JSON), mTLS authentication with eIDAS certificates, and FAPI (Financial-grade API) OAuth2 profiles. |
Open Banking & ISO 20022 APIs Architecture
This skill package defines architectural guidelines, security controls, and production implementations for building Open Banking platforms, PSD2/PSD3 compliant Payment Initiation (PISP) and Account Information (AISP) services, and ISO 20022 financial messaging engines.
1. Security Architecture & Standards
FAPI (Financial-grade API 1.0/2.0) & OAuth 2.0 Security Profile
- Client Authentication: Enforce Mutual TLS (
tls_client_auth) or private_key_jwt with asymmetric RSA-SHA256 (PS256) or ECDSA (ES256) key pairs.
- Pushed Authorization Requests (PAR - RFC 9126): PISPs/AISPs must push authorization request parameters directly to the ASPSP authorization server via back-channel POST requests, obtaining an opaque
request_uri to prevent front-channel tampering.
- JWS Signature & Non-Repudiation: Payment initiation payloads must include detached HTTP Message Signatures (RFC 9421) signed with eIDAS QSealC (Qualified Electronic Seal Certificates).
mTLS & eIDAS Certificate Validation
- Transport Security: Enforce mutual TLS 1.3 using eIDAS QWAC (Qualified Website Authentication Certificates) to establish identity between Third-Party Providers (TPPs) and Account Servicing Payment Service Providers (ASPSPs).
- Certificate Revocation Checking: Validate TPP certificate status in real-time via OCSP stapling and CRL (Certificate Revocation List) checks against Qualified Trust Service Providers (QTSPs).
2. ISO 20022 Financial Messaging Matrix
| Message Identifier | Name | Domain | Primary Purpose |
|---|
pain.001.001.11 | Customer Credit Transfer Initiation | Payments | TPP/Corporate initiates credit transfer to bank |
pacs.008.001.10 | FI to FI Customer Credit Transfer | Clearing & Settlement | Interbank real-time settlement (SEPA Instant, FedNow) |
camt.053.001.10 | Bank to Customer Statement | Account Management | Daily end-of-day account balance & statement |
camt.054.001.10 | Bank to Customer Debit/Credit Notification | Notifications | Real-time transaction notification advisory |
pacs.002.001.12 | Payment Status Report | Status Tracking | Real-time ACK/NACK status of payment processing |
3. Production Code Implementations
Python Implementation: ISO 20022 pain.001 XML Builder & Schema Validator
import datetime
import hashlib
import xml.etree.ElementTree as ET
from xml.dom import minidom
from typing import Dict, Any
class ISO20022Pain001Builder:
"""
Constructs ISO 20022 pain.001.001.11 Credit Transfer Initiation XML documents
"""
NS_PAIN = "urn:iso:std:iso:20022:tech:xsd:pain.001.001.11"
def __init__(self, msg_id: str, initiating_party_name: str):
self.msg_id = msg_id
self.initiating_party_name = initiating_party_name
def build_credit_transfer_xml(self, pmt_inf_id: str, debtor: Dict[str, str], creditor: Dict[str, str], amount: float, currency: str) -> str:
ET.register_namespace('', self.NS_PAIN)
doc = ET.Element("Document", xmlns=self.NS_PAIN)
cstmr_pmt_init = ET.SubElement(doc, "CstmrPmtSttmtReq")
grp_hdr = ET.SubElement(cstmr_pmt_init, "GrpHdr")
ET.SubElement(grp_hdr, "MsgId").text = self.msg_id
ET.SubElement(grp_hdr, "CreDtTm").text = datetime.datetime.utcnow().isoformat() +
ET.SubElement(grp_hdr, ).text =
ET.SubElement(grp_hdr, ).text =
initg_pty = ET.SubElement(grp_hdr, )
ET.SubElement(initg_pty, ).text = .initiating_party_name
pmt_inf = ET.SubElement(cstmr_pmt_init, )
ET.SubElement(pmt_inf, ).text = pmt_inf_id
ET.SubElement(pmt_inf, ).text =
ET.SubElement(pmt_inf, ).text =
ET.SubElement(pmt_inf, ).text =
dbtr = ET.SubElement(pmt_inf, )
ET.SubElement(dbtr, ).text = debtor[]
dbtr_acct = ET.SubElement(pmt_inf, )
dbtr_id = ET.SubElement(dbtr_acct, )
ET.SubElement(dbtr_id, ).text = debtor[]
dbtr_agt = ET.SubElement(pmt_inf, )
fin_inst_dbtr = ET.SubElement(dbtr_agt, )
ET.SubElement(fin_inst_dbtr, ).text = debtor[]
cdt_trf_tx_inf = ET.SubElement(pmt_inf, )
pmt_id = ET.SubElement(cdt_trf_tx_inf, )
ET.SubElement(pmt_id, ).text =
amt = ET.SubElement(cdt_trf_tx_inf, )
instd_amt = ET.SubElement(amt, , Ccy=currency)
instd_amt.text =
cdtr_agt = ET.SubElement(cdt_trf_tx_inf, )
fin_inst_cdtr = ET.SubElement(cdtr_agt, )
ET.SubElement(fin_inst_cdtr, ).text = creditor[]
cdtr = ET.SubElement(cdt_trf_tx_inf, )
ET.SubElement(cdtr, ).text = creditor[]
cdtr_acct = ET.SubElement(cdt_trf_tx_inf, )
cdtr_id = ET.SubElement(cdtr_acct, )
ET.SubElement(cdtr_id, ).text = creditor[]
raw_xml = ET.tostring(doc, encoding=)
parsed = minidom.parseString(raw_xml)
parsed.toprettyxml(indent=)
() -> :
digest = hashlib.sha256(xml_content.encode()).digest()
base64
TypeScript Implementation: FAPI 1.0 Advanced Client with Private Key JWT & Signature Interceptor
import crypto from 'crypto';
import fs from 'fs';
export interface FapiClientConfig {
clientId: string;
tokenEndpoint: string;
privateKeyPath: string;
keyId: string;
}
export class FapiAdvancedAuthClient {
private config: FapiClientConfig;
private privateKeyPem: string;
constructor(config: FapiClientConfig) {
this.config = config;
this.privateKeyPem = fs.readFileSync(config.privateKeyPath, 'utf8');
}
public generateClientAssertion(): string {
const now = Math.floor(Date.now() / 1000);
const header = {
alg: 'PS256',
typ: ,
: ..,
};
payload = {
: ..,
: ..,
: ..,
: crypto.(),
: now + ,
: now,
};
encodedHeader = .(.(header));
encodedPayload = .(.(payload));
dataToSign = ;
signer = crypto.();
signer.(dataToSign);
signer.();
signature = signer.({
: .,
: crypto..,
: crypto..,
});
;
}
(: , : , : ): <, > {
digest = + crypto.().(bodyPayload).();
xFapiInteractionId = crypto.();
dateHeader = ().();
signatureInputStr = ;
signer = crypto.();
signer.(signatureInputStr);
signature = signer.(., );
{
: digest,
: dateHeader,
: xFapiInteractionId,
: ,
};
}
(: | ): {
buf = input === ? .(input) : input;
buf.().(, ).(, ).(, );
}
}
4. Anti-Patterns & Critical Mistakes
-
Failure to Perform XML Schema (XSD) Validation Prior to Parsing
- Impact: Vulnerability to XML External Entity (XXE) injection attacks and unexpected XML expansion attacks (Billion Laughs).
- Remediation: Disable external DTD resolution (
resolve_entities=False) and validate all incoming ISO 20022 XML against official ISO XSD schemas before business processing.
-
Passing Dynamic Auth Parameters via Front-Channel Authorize Endpoint
- Impact: Vulnerable to Authorization Code injection and parameter tampering.
- Remediation: Require FAPI Pushed Authorization Requests (PAR) to exchange parameters back-channel for an opaque
request_uri.
-
Treating IBAN Validation as a Simple Regex Match
- Impact: False positives and failed SEPA payments due to missing MOD-97 check digit calculations.
- Remediation: Always execute MOD-97 checksum algorithm verification on IBAN strings prior to generating
pain.001 or pacs.008 messages.
-
Omitting x-fapi-interaction-id Tracing Headers
- Impact: Non-compliance with Open Banking auditing mandates, leading to untraceable dispute handling between TPP and ASPSP.
- Remediation: Inject and log unique UUID v4
x-fapi-interaction-id headers across all HTTP client requests and server logs.
5. Verification & Testing Playbook