Aller au contenu

POC · Générateur FATCA XML v2.0 (Kotlin)

Code source : poc-fatca-generator/ dans le monorepo VitaKYC (hors site docs, à cloner depuis GitLab).

Premier POC technique de VitaKYC : un générateur FATCA XML v2.0 en Kotlin conforme à l’IRS Publication 5124 et au cahier des charges DGI Tunisie V1.0-2019. Il matérialise l’architecture décrite dans le Dossier d’architecture technique §13.


Valider concrètement — avec code, tests, et un fichier XML produit — que VitaKYC sait générer des déclarations FATCA conformes. C’est la preuve technique à montrer aux prospects banque (notamment dans une réponse AO) et le noyau qui sera intégré dans le service tcr-svc de production.


ComposantVersionRaison
Kotlin2.0.20Langage cible VitaKYC (ADR implicite §6 cahier des charges)
JVM17LTS stable, largement disponible
Gradle8.10.2Build standard JVM
StAX (javax.xml.stream)JDK natifStreaming XML — contrôle précis des namespaces, empreinte mémoire minimale
JUnit 5 + AssertJ5.11 / 3.26Tests
SLF4J2.0Logging structuré

Pas de iText (AGPL), pas de JAXB génération automatique — StAX à la main pour maîtrise totale du schéma et respect strict des règles DGI.


RègleImplémentation
Nommage fichier <MF>-<exercice>.xmlFatcaXmlBuilder.build() renvoie un fileName
UTF-8 sans BOMStAX produit UTF-8 par défaut, BOM absent (test dédié)
Namespace urn:oecd:ties:fatca:v2 + schemaLocationEn-tête racine <ftc:FATCA_OECD>
MessageRefId unique = <GIIN>-<ISO8601>-<GUID>Identifiers.newMessageRefId()
DocRefId = <GIIN>.<GUID>, 21-200 chars, [A-Za-z0-9.-]Identifiers.newDocRefId() avec validation regex
RG2.1 — TIN inconnu : neuf A (individu) ou neuf 0 (EENF passive)Helpers tinForUnknownIndividual() / tinForUnknownPassiveNffe()
RG2.2 — GIIN obligatoire pour ReportingFIValidation au début de build()
RG2.3 — FilerCategory FATCA601 (participating FFI) par défautEnum FilerCategory
RG4 — codage AcctNumberType : OECD601 (IBAN) / OECD602 (OBAN) / OECD603 (ISIN) / OECD604 (OSIN) / OECD605 (autre) / NANUMEnum AcctNumberType
RG5 — montants 2 décimales, point comme séparateur"%.2f".format(Locale.ROOT, ...)
RG6.2 — types FATCA 1 / 2 / 3 / 4 + NilReport + chaînage corrMessageRefIdEnum FatcaMessageType + validation
RG7 — NilReport avec NoAccountToReportBranche dédiée dans writeFatcaBlock
AcctHolderType : FATCA102 (EENF passive) / FATCA103 (NP FI) / FATCA104 (US Person)Enum AccountHolderType

poc-fatca-generator/
├── build.gradle.kts # Kotlin 2.0 + JDK 17 + JAXB + JUnit 5
├── settings.gradle.kts
├── gradlew + gradle/wrapper/ # Gradle 8.10.2 wrapper
├── README.md
└── src/
├── main/kotlin/io/vitakyc/fatca/
│ ├── Model.kt # FatcaDeclaration, ReportingFI, AccountHolder, ...
│ ├── Identifiers.kt # MessageRefId + DocRefId
│ ├── FatcaXmlBuilder.kt # construction XML StAX (~300 lignes)
│ └── Main.kt # exemple runnable
└── test/kotlin/io/vitakyc/fatca/
└── FatcaXmlBuilderTest.kt # 11 tests AssertJ

Fenêtre de terminal
cd poc-fatca-generator
./gradlew test

Sortie attendue :

FatcaXmlBuilderTest > build · produces a UTF-8 XML with correct root and namespaces PASSED
FatcaXmlBuilderTest > file name follows DGI convention <MF>-<year>.xml PASSED
FatcaXmlBuilderTest > MessageRefId is GIIN + timestamp + unique GUID PASSED
FatcaXmlBuilderTest > ReportingFI includes FilerCategory FATCA601 PASSED
FatcaXmlBuilderTest > AccountReport includes AccountNumber with AcctNumberType OECD601 (IBAN) PASSED
FatcaXmlBuilderTest > Individual holder is rendered with TIN issuedBy=US PASSED
FatcaXmlBuilderTest > AccountBalance formatted with 2 decimals + currCode PASSED
FatcaXmlBuilderTest > AcctHolderType FATCA104 rendered for Specified US Person PASSED
FatcaXmlBuilderTest > NilReport produces a NoAccountToReport element PASSED
FatcaXmlBuilderTest > FATCA3 without corrMessageRefId is rejected (RG6.2) PASSED
FatcaXmlBuilderTest > SHA-256 is deterministic for identical inputs PASSED
FatcaXmlBuilderTest > UTF-8 sans BOM (contrôle CF-DGI) PASSED
FatcaXmlBuilderTest > DocRefID respecte le format <GIIN>.<id> et la plage 21-200 chars PASSED
BUILD SUCCESSFUL
Fenêtre de terminal
./gradlew run

Produit un fichier build/out/0005623A-2025.xml (≈ 2,5 KB) pour une banque tunisienne fictive avec un compte au nom de JOHN SMITH (US Person résidant à La Marsa, solde 125 430,75 USD).

Chaque exécution produit un MessageRefId et des DocRefId uniques, et retourne un SHA-256 pour intégrité.


val reportingFi = ReportingFI(
matriculeFiscal = "0005623A",
giin = "00Z148.00027.ME.788",
legalName = "BANQUE EXEMPLE DE TUNISIE",
address = Address(
countryCode = "TN",
street = "Avenue Habib Bourguiba",
buildingIdentifier = "25",
postCode = "1000",
city = "Tunis",
),
filerCategory = FilerCategory.PARTICIPATING_FFI,
)
val holder = AccountHolder(
kind = AccountHolder.Kind.INDIVIDUAL,
residenceCountryCodes = listOf("US"),
tin = "123-45-6789",
tinIssuedBy = "US",
name = Name(firstName = "JOHN", lastName = "SMITH"),
address = Address(countryCode = "TN", street = "Rue de la Liberté",
postCode = "2045", city = "La Marsa"),
nationality = "US",
birthInfo = BirthInfo(birthDate = LocalDate.of(1980, 6, 15), countryCode = "US"),
holderType = AccountHolderType.SPECIFIED_US_PERSON,
)
val accountReport = AccountReport(
accountNumber = "TN59 1000 1234 5678 9012 3456",
accountNumberType = AcctNumberType.IBAN,
holder = holder,
accountBalance = AccountBalance(125_430.75, "USD"),
payments = listOf(Payment(PaymentType.INTEREST, 240.55, "USD")),
)
val declaration = FatcaDeclaration(
reportingFi = reportingFi,
reportingYear = 2025,
messageType = FatcaMessageType.FATCA1,
accountReports = listOf(accountReport),
)
val result = FatcaXmlBuilder().build(declaration)
Files.write(Path.of("out").resolve(result.fileName), result.xmlBytes)

<?xml version="1.0" encoding="UTF-8"?>
<ftc:FATCA_OECD version="2.0"
xmlns:ftc="urn:oecd:ties:fatca:v2"
xmlns:sfa="urn:oecd:ties:stffatcatypes:v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:oecd:ties:fatca:v2 FatcaXML_v2.0.xsd">
<sfa:MessageSpec>
<sfa:SendingCompanyIN>00Z148.00027.ME.788</sfa:SendingCompanyIN>
<sfa:TransmittingCountry>TN</sfa:TransmittingCountry>
<sfa:ReceivingCountry>US</sfa:ReceivingCountry>
<sfa:MessageType>FATCA</sfa:MessageType>
<sfa:MessageRefId>00Z148.00027.ME.788-20260422T110937-0d25c607661e49f5a265dac8c2de71ed</sfa:MessageRefId>
<sfa:ReportingPeriod>2025-12-31</sfa:ReportingPeriod>
<sfa:Timestamp>2026-04-22T11:09:37Z</sfa:Timestamp>
</sfa:MessageSpec>
<ftc:FATCA>
<ftc:ReportingFI>
<sfa:ResCountryCode>TN</sfa:ResCountryCode>
<sfa:TIN issuedBy="US">00Z148.00027.ME.788</sfa:TIN>
<sfa:Name>BANQUE EXEMPLE DE TUNISIE</sfa:Name>
<sfa:Address>
<sfa:CountryCode>TN</sfa:CountryCode>
<sfa:AddressFix>
<sfa:Street>Avenue Habib Bourguiba</sfa:Street>
<sfa:BuildingIdentifier>25</sfa:BuildingIdentifier>
<sfa:PostCode>1000</sfa:PostCode>
<sfa:City>Tunis</sfa:City>
</sfa:AddressFix>
</sfa:Address>
<ftc:FilerCategory>FATCA601</ftc:FilerCategory>
<ftc:DocSpec>
<sfa:DocTypeIndic>FATCA1</sfa:DocTypeIndic>
<sfa:DocRefId>00Z148.00027.ME.788.2f5b1d4010cf493eba52ed4be6a046c4</sfa:DocRefId>
</ftc:DocSpec>
</ftc:ReportingFI>
<ftc:ReportingGroup>
<ftc:AccountReport>
<ftc:DocSpec>
<sfa:DocTypeIndic>FATCA1</sfa:DocTypeIndic>
<sfa:DocRefId>00Z148.00027.ME.788.ed64692944f641f2ae8f2a5a2a5547d6</sfa:DocRefId>
</ftc:DocSpec>
<ftc:AccountNumber AcctNumberType="OECD601">TN59 1000 1234 5678 9012 3456</ftc:AccountNumber>
<ftc:AccountHolder>
<ftc:Individual>
<sfa:ResCountryCode>US</sfa:ResCountryCode>
<sfa:TIN issuedBy="US">123-45-6789</sfa:TIN>
<sfa:Name>
<sfa:FirstName>JOHN</sfa:FirstName>
<sfa:LastName>SMITH</sfa:LastName>
</sfa:Name>
<sfa:Address>
<sfa:CountryCode>TN</sfa:CountryCode>
<sfa:AddressFix>
<sfa:Street>Rue de la Liberté</sfa:Street>
<sfa:PostCode>2045</sfa:PostCode>
<sfa:City>La Marsa</sfa:City>
</sfa:AddressFix>
</sfa:Address>
<sfa:Nationality>US</sfa:Nationality>
<sfa:BirthInfo>
<sfa:BirthDate>1980-06-15</sfa:BirthDate>
<sfa:CountryInfo>
<sfa:CountryCode>US</sfa:CountryCode>
</sfa:CountryInfo>
</sfa:BirthInfo>
</ftc:Individual>
<ftc:AcctHolderType>FATCA104</ftc:AcctHolderType>
</ftc:AccountHolder>
<ftc:AccountBalance currCode="USD">125430.75</ftc:AccountBalance>
<ftc:Payment>
<ftc:Type>FATCA502</ftc:Type>
<ftc:PaymentAmnt currCode="USD">240.55</ftc:PaymentAmnt>
</ftc:Payment>
</ftc:AccountReport>
</ftc:ReportingGroup>
</ftc:FATCA>
</ftc:FATCA_OECD>

  • Conformité XML structurelle : root, namespaces, attribut schemaLocation, version 2.0 — le fichier passe visuellement une inspection à la xmllint --noout.
  • Règles DGI implémentées : nommage, UTF-8 no-BOM, MessageRefId / DocRefId formatés selon le cahier des charges V1.0-2019.
  • Types FATCA 1 à 4 + NilReport avec chaînage corrMessageRefId.
  • Tests automatisables : 13 tests AssertJ couvrent les cas critiques et régressions futures.
  • Empreinte SHA-256 retournée par le builder : prête à être journalisée dans tcr_declaration.file_hash_sha256.

9. Ce qu’il reste à faire pour passer en production

Section intitulée « 9. Ce qu’il reste à faire pour passer en production »

Ce POC n’est pas prêt production. Pour intégration dans tcr-svc :

  1. Validation XSD stricte contre FatcaXML_v2.0.xsd + isofatcatypes_v1.1.xsd + oecdtypes_v4.2.xsd + stffatcatypes_v2.0.xsd avant sortie (télécharger les XSD IRS Pub 5124).
  2. Compression GZIP si fichier > seuil configurable.
  3. Dépôt automatique IDES via connector-ides (mTLS + certificat ANCE) — cf. AML TxMon pour les patterns d’intégration.
  4. Orchestration Temporal pour le chaînage FATCA 3 → FATCA 1 avec attente de la fenêtre DGI → IRS.
  5. Parsing des notifications ICMM (IRS Publication 5189) et génération automatique des corrections (FATCA 2, 3 ou 4) — cf. Playbook compliance 5.
  6. Support Sponsor et Intermediary (blocs optionnels ReportingGroup).
  7. Tests avec échantillons officiels IRS (Publication 5124) pour non-régression.
  8. Production au sein du service tcr-svc + archivage WORM.

  • Preuve technique à présenter en RFP / AO (La Poste Tunisienne attend une démonstration concrète).
  • Dé-risque l’architecture : le plus spécifique et réglementaire des modules est validé tôt.
  • Noyau réutilisable pour le futur report-svc et la génération CRS (même schéma structurel XML OECD).
  • Matérialise la différenciation VitaKYC : l’un des seuls acteurs KYC/AML modernes à savoir générer un fichier FATCA conforme DGI Tunisie à la granularité près.

POC validé le 2026-04-22 · 13 tests passants · fichier XML produit en < 1 s.