Aller au contenu

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.


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 :

ZonePays utilisateurs goAML
MENAUAE (goAML), Égypte, Maroc, Libye, Jordanie, Bahreïn, Oman, Liban
Europe continentaleAllemagne (FIU), Belgique (CTIF), Pays-Bas (FIU-NL), Autriche, Hongrie, Pologne, Roumanie, Bulgarie, etc.
AfriqueAfrique du Sud, Nigeria, Kenya, Ghana, Ouganda, …
Amérique latineMexique, Argentine, Colombie, Pérou, Uruguay, …
AsieBangladesh, 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).


ComposantVersionNotes
Kotlin2.0.20Identique FATCA POC
StAX (JDK)17+Streaming XML, contrôle fin
JUnit 5 + AssertJ5.11 / 3.26Tests
SLF4J2.0Logging

Zéro dépendance propriétaire, Apache 2.0 partout.


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 AssertJ

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_person ou from_entity, from_account, from_country, idem to_*, 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_person ou signatory.t_entity.
  • Indicators + reason : report_indicators > indicator, reason, action.

  • reportReference obligatoire.
  • reasonForReporting entre 50 et 10 000 caractères (contrainte conformité UNODC + bonne pratique 5W).
  • Au moins une transaction obligatoire dans un STR.
  • parentReportReference obligatoire si actionCode = REVISION ou DELETE.
  • Montants toujours sans notation scientifique (BigDecimal.toPlainString()).
  • UTF-8 sans BOM (contrôle couramment appliqué par les FIU).

#IntituléCouvre
1XML UTF-8 avec <report> racineStructure racine
2Header : rentity_id, report_code, submission_code, action, currency_code_localMétadonnées obligatoires
3reporting_person inclusAgent déclarant
4Transaction : numéro, date, mode, amount_local en plainMapping transaction
5BigDecimal n’utilise pas de notation scientifiqueFormat monétaire FIU
6to_account · institution + IBAN + currencyMapping account
7identification avec type + number + issue_countryPièce d’identité
8report_indicators + reason_for_reporting présentsNarrative 5W
9Reason < 50 chars rejetéValidation narrative
10STR sans transaction rejetéContrainte structure
11Action REVISION sans parent_reference rejetéeChaînage corrections
12Action REVISION avec parent_reference inclut parent_referenceChaînage OK
13SHA-256 retourné en hex 64 charsIntégrité
14Nom de fichier = STR-<ref>.xmlConvention
15Entité compagnie : incorporation_country_code + tax_number rendusMapping entité

Sortie ./gradlew test :

GoAmlXmlBuilderTest > XML UTF-8 avec <report> racine PASSED
GoAmlXmlBuilderTest > Header report : rentity_id, report_code STR, submission_code, ref PASSED
GoAmlXmlBuilderTest > Reporting person inclus dans reporting_person PASSED
...
BUILD SUCCESSFUL in 14s

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 :

Fenêtre de terminal
./gradlew run
# → ./build/out/STR-BANK-0042-STR-20260422-0001.xml (5 KB environ)

<?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>

DimensionFATCA XMLgoAML STR
SourceIRS (US) + schéma OECDUNODC
CibleAutorité fiscale US (via IGA national)FIU locale
FormeDéclaration fiscale périodique (annuelle)Signalement ponctuel suspect
Volume typiqueMilliers par IF par exerciceDizaines à centaines par IF par an
Format racine<ftc:FATCA_OECD> avec namespaces<report> sans namespace (selon version)
ChaînageFATCA 1/2/3/4 via CorrMessageRefIdaction=NEW/REVISION/DELETE via parent_reference
Juridictions cibles VitaKYC~100 pays via IGA60+ 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.


  1. Validation XSD contre schéma officiel UNODC (récupérer xsd depuis goaml.unodc.org).
  2. Profils nationaux : Belgique (CTIF), UAE (FIU), Maroc (UTRF), Allemagne (FIU) — chacun peut imposer des contraintes additionnelles.
  3. Variante Tunisie CTAF : format similaire mais non-goAML, à développer en V1 après contact CTAF.
  4. Variante TRACFIN ERMES : format XML spécifique, à développer en V1.
  5. Transmission : API REST ou upload sFTP selon FIU, runbook dédié par juridiction.
  6. Orchestration Temporal : cycle complet enquête → rédaction narrative → validation → transmission → suivi réponse FIU.
  7. Intégration aml-svc : dès qu’une alerte est confirmée true_positive par la revue compliance.
  8. Archivage WORM 10 ans (cf. obligations LCB-FT) avec hash et signature Ed25519.
  9. Reporting : dashboard annuel du volume STR par typologie (pour comité conformité).

  • 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 run est 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.