POC · Générateur STR goAML (Kotlin)
Code source :
poc-goaml-generator/dans le monorepo VitaKYC.
Troisième POC technique : le générateur de STR (Suspicious Transaction Report) au format goAML UNODC. Complémentaire au POC FATCA, c’est le format de déclaration utilisé par plus de 60 Cellules de Renseignement Financier dans le monde.
1. Pourquoi goAML
Section intitulée « 1. Pourquoi goAML »goAML est une suite logicielle développée par l’UNODC (Office des Nations Unies contre la drogue et le crime) et mise gratuitement à disposition des FIU (Financial Intelligence Units). C’est le standard de fait mondial pour la réception des STR / SAR / CTR.
Juridictions cibles VitaKYC utilisant goAML :
| Zone | Pays utilisateurs goAML |
|---|---|
| MENA | UAE (goAML), Égypte, Maroc, Libye, Jordanie, Bahreïn, Oman, Liban |
| Europe continentale | Allemagne (FIU), Belgique (CTIF), Pays-Bas (FIU-NL), Autriche, Hongrie, Pologne, Roumanie, Bulgarie, etc. |
| Afrique | Afrique du Sud, Nigeria, Kenya, Ghana, Ouganda, … |
| Amérique latine | Mexique, Argentine, Colombie, Pérou, Uruguay, … |
| Asie | Bangladesh, Sri Lanka, Philippines, … |
Non-goAML mais proches : Tunisie (CTAF, format spécifique à adapter), France (TRACFIN ERMES XML), US (FinCEN SAR PDF/XML BSA), UK (NCA SAR XML).
Conclusion commerciale : un seul connecteur goAML → couverture majoritaire du marché cible VitaKYC (MENA + Europe continentale).
2. Stack
Section intitulée « 2. Stack »| Composant | Version | Notes |
|---|---|---|
| Kotlin | 2.0.20 | Identique FATCA POC |
| StAX (JDK) | 17+ | Streaming XML, contrôle fin |
| JUnit 5 + AssertJ | 5.11 / 3.26 | Tests |
| SLF4J | 2.0 | Logging |
Zéro dépendance propriétaire, Apache 2.0 partout.
3. Structure du projet
Section intitulée « 3. Structure du projet »poc-goaml-generator/├── build.gradle.kts├── settings.gradle.kts└── src/ ├── main/kotlin/io/vitakyc/goaml/ │ ├── Model.kt # Person, Entity, Party, Account, Transaction, GoAmlReport │ ├── GoAmlXmlBuilder.kt # Construction XML via StAX │ └── Main.kt # Scénario démo : STR pass-through suspect └── test/kotlin/io/vitakyc/goaml/ └── GoAmlXmlBuilderTest.kt # 15 tests AssertJ4. Éléments couverts dans le schéma goAML
Section intitulée « 4. Éléments couverts dans le schéma goAML »Le POC produit un XML conforme aux balises goAML v5.x :
- Header :
rentity_id,rentity_branch,submission_code,report_code(STR / SAR / CTR / TFR),entity_reference,action(NEW / REVISION / DELETE),parent_reference,submission_date,currency_code_local,reporting_person,location. - Transactions (n-ary) :
transactionnumber,date_transaction,transmode_code(WIRETRANSFER / CASH / CHEQUE / CARD),amount_local+amount_transacted+amount_transacted_currency,t_from_my_client,t_to_my_client,from_personoufrom_entity,from_account,from_country, idemto_*,comments. - Party personne :
gender,first_name,middle_name,last_name,birthdate,birth_place,nationality1,nationality2,residence,occupation,email,ssn,tax_number,tax_reg_country,phones,addresses,identifications,source_of_wealth(PEP). - Party entité :
name,commercial_name,incorporation_legal_form,incorporation_number,incorporation_country_code,tax_number,business,addresses,phones,email,url,director_id[]. - Account :
institution_name,branch,account,currency_code,iban,account_name,personal_account_type,opened,closed,balance,date_balance,signatory.t_personousignatory.t_entity. - Indicators + reason :
report_indicators > indicator,reason,action.
5. Règles de validation implémentées
Section intitulée « 5. Règles de validation implémentées »reportReferenceobligatoire.reasonForReportingentre 50 et 10 000 caractères (contrainte conformité UNODC + bonne pratique 5W).- Au moins une transaction obligatoire dans un STR.
parentReportReferenceobligatoire siactionCode = REVISIONouDELETE.- Montants toujours sans notation scientifique (
BigDecimal.toPlainString()). - UTF-8 sans BOM (contrôle couramment appliqué par les FIU).
6. Tests (15 cas passants)
Section intitulée « 6. Tests (15 cas passants) »| # | Intitulé | Couvre |
|---|---|---|
| 1 | XML UTF-8 avec <report> racine | Structure racine |
| 2 | Header : rentity_id, report_code, submission_code, action, currency_code_local | Métadonnées obligatoires |
| 3 | reporting_person inclus | Agent déclarant |
| 4 | Transaction : numéro, date, mode, amount_local en plain | Mapping transaction |
| 5 | BigDecimal n’utilise pas de notation scientifique | Format monétaire FIU |
| 6 | to_account · institution + IBAN + currency | Mapping account |
| 7 | identification avec type + number + issue_country | Pièce d’identité |
| 8 | report_indicators + reason_for_reporting présents | Narrative 5W |
| 9 | Reason < 50 chars rejeté | Validation narrative |
| 10 | STR sans transaction rejeté | Contrainte structure |
| 11 | Action REVISION sans parent_reference rejetée | Chaînage corrections |
| 12 | Action REVISION avec parent_reference inclut parent_reference | Chaînage OK |
| 13 | SHA-256 retourné en hex 64 chars | Intégrité |
| 14 | Nom de fichier = STR-<ref>.xml | Convention |
| 15 | Entité compagnie : incorporation_country_code + tax_number rendus | Mapping entité |
Sortie ./gradlew test :
GoAmlXmlBuilderTest > XML UTF-8 avec <report> racine PASSEDGoAmlXmlBuilderTest > Header report : rentity_id, report_code STR, submission_code, ref PASSEDGoAmlXmlBuilderTest > Reporting person inclus dans reporting_person PASSED...BUILD SUCCESSFUL in 14s7. Exemple : STR pass-through réaliste
Section intitulée « 7. Exemple : STR pass-through réaliste »Le Main.kt génère un STR complet pour un scénario classique :
- Scenario : ACME Trading SARL (TN) reçoit 923 320 USD de sociétés offshore (BVI, Panama) en 5 jours, puis transfère 840 000 USD en < 24 h vers une entité Dubaï dont le BE est listé OFAC SDN.
- Pattern détecté : pass-through + counterparty non-coopérative + hit sanctions indirect.
- 3 transactions incluses dans le rapport.
- Narrative : ~450 caractères factuels + action prise (gel préventif).
Exécution :
./gradlew run# → ./build/out/STR-BANK-0042-STR-20260422-0001.xml (5 KB environ)8. Extrait du XML produit
Section intitulée « 8. Extrait du XML produit »<?xml version="1.0" encoding="UTF-8"?><report> <rentity_id>TN-BANK-0042</rentity_id> <rentity_branch>HEAD</rentity_branch> <submission_code>E</submission_code> <report_code>STR</report_code> <entity_reference>BANK-0042-STR-20260422-0001</entity_reference> <action>NEW</action> <submission_date>2026-04-22T12:00:00</submission_date> <currency_code_local>TND</currency_code_local> <reporting_person> <gender>M</gender> <first_name>Mohamed</first_name> <last_name>Ben Ali</last_name> <occupation>Compliance Officer</occupation> <email>compliance@banque-exemple.tn</email> </reporting_person> <location>TN</location> <transaction> <transactionnumber>TX-2026-00126</transactionnumber> <date_transaction>2026-04-17T14:08:00</date_transaction> <transmode_code>WIRETRANSFER</transmode_code> <amount_local>2623820.00</amount_local> <amount_transacted>847320.00</amount_transacted> <amount_transacted_currency>USD</amount_transacted_currency> <t_from_my_client> <from_entity> <name>STARFISH CAPITAL SA</name> <incorporation_country_code>PA</incorporation_country_code> </from_entity> <from_country>PA</from_country> </t_from_my_client> <t_to_my_client> <to_account> <institution_name>BANQUE EXEMPLE DE TUNISIE</institution_name> <account>TN59-1000-1234-5678-9012-3456</account> <currency_code>USD</currency_code> <iban>TN59 1000 1234 5678 9012 3456</iban> ... </to_account> <to_country>TN</to_country> </t_to_my_client> <comments>2e versement, moins de 72 h après le 1er.</comments> </transaction> ... <report_indicators> <indicator>SUSP_BEHAVIOR</indicator> </report_indicators> <reason>Le compte TN59 1000 1234 5678 9012 3456 au nom d'ACME TRADING SARL...</reason> <action>Dossier gelé préventivement...</action></report>9. Comparaison FATCA XML vs goAML XML
Section intitulée « 9. Comparaison FATCA XML vs goAML XML »| Dimension | FATCA XML | goAML STR |
|---|---|---|
| Source | IRS (US) + schéma OECD | UNODC |
| Cible | Autorité fiscale US (via IGA national) | FIU locale |
| Forme | Déclaration fiscale périodique (annuelle) | Signalement ponctuel suspect |
| Volume typique | Milliers par IF par exercice | Dizaines à centaines par IF par an |
| Format racine | <ftc:FATCA_OECD> avec namespaces | <report> sans namespace (selon version) |
| Chaînage | FATCA 1/2/3/4 via CorrMessageRefId | action=NEW/REVISION/DELETE via parent_reference |
| Juridictions cibles VitaKYC | ~100 pays via IGA | 60+ via goAML + 3-4 variantes nationales |
Mutualisation : les deux builders partagent ~40 % de code (wrappers StAX, hashing, validation commune, formatage). Industrialisable en librairie commune vitakyc-xml-builder en V1.
10. Roadmap pour passer en production
Section intitulée « 10. Roadmap pour passer en production »- Validation XSD contre schéma officiel UNODC (récupérer xsd depuis
goaml.unodc.org). - Profils nationaux : Belgique (CTIF), UAE (FIU), Maroc (UTRF), Allemagne (FIU) — chacun peut imposer des contraintes additionnelles.
- Variante Tunisie CTAF : format similaire mais non-goAML, à développer en V1 après contact CTAF.
- Variante TRACFIN ERMES : format XML spécifique, à développer en V1.
- Transmission : API REST ou upload sFTP selon FIU, runbook dédié par juridiction.
- Orchestration Temporal : cycle complet enquête → rédaction narrative → validation → transmission → suivi réponse FIU.
- Intégration
aml-svc: dès qu’une alerte est confirméetrue_positivepar la revue compliance. - Archivage WORM 10 ans (cf. obligations LCB-FT) avec hash et signature Ed25519.
- Reporting : dashboard annuel du volume STR par typologie (pour comité conformité).
11. Impact commercial
Section intitulée « 11. Impact commercial »- Un format = 60+ juridictions → argument ROI fort pour les banques internationales.
- Complète FATCA pour un module TCR + AML complet coté reporting.
- Compliance-critical : nouvelle obligation pour les banques MENA régulées (UAE Central Bank, etc. depuis 2020) — pas de STR goAML = sanction.
- Démonstration : le XML produit par
./gradlew runest montrable à un compliance officer banque qui connaît son FIU — validation terrain immédiate.
POC validé le 2026-04-22 · 15 tests passants · aucune dépendance lourde.