POC · Connecteur RNE Tunisie (Kotlin)
Code source :
poc-rne-connector/dans le monorepo VitaKYC.
Deuxième POC technique : le connecteur RNE Tunisie côté VitaKYC. Consomme les web services WS-KYC et WS-BE exactement comme documenté dans les annexes officielles RNE, avec gestion complète des erreurs métier, cache et économie de quota d’abonnement.
1. Objectif
Section intitulée « 1. Objectif »Matérialiser l’intégration RNE décrite au cahier des charges §12.3.1 et au diagramme de séquence §7.3. C’est la brique qui fait du KYB instantané côté VitaKYC pour les clients tunisiens — argument commercial clé en AO banque tunisien (extraction auto fiche entreprise + UBO sans dev côté banque).
2. Web services consommés
Section intitulée « 2. Web services consommés »| Service | Endpoint | Paramètres |
|---|---|---|
| WS-KYC | GET https://api.registre-entreprises.tn:8243/WS-KYC/1 | subscriptionId, subscriberId, identifiantUnique |
| WS-BE | GET https://api.registre-entreprises.tn:8243/WS-BE/1 | subscriptionId, subscriberId, codeMotif, identifiantUnique |
Authentification : Authorization: Bearer <token> (token délivré en privé par le RNE à la souscription).
3. Stack
Section intitulée « 3. Stack »| Composant | Version | Raison |
|---|---|---|
| Kotlin | 2.0.20 | Langage cible VitaKYC |
| OkHttp | 4.12 | Client HTTP minimaliste, timeout fin |
| kotlinx-serialization | 1.7 | JSON → data classes |
| JUnit 5 + AssertJ + MockWebServer | — | Tests avec fixtures RNE officielles |
Pas de Spring ni framework lourd — un connecteur ciblé, prêt à être extrait en tant que service Spring Boot connector-rne dans le monorepo VitaKYC (cf. architecture §4.3).
4. Architecture du POC
Section intitulée « 4. Architecture du POC »5. Règles métier implémentées
Section intitulée « 5. Règles métier implémentées »- Validation entrée :
identifiantUniquedoit matcher^[0-9]{7}[A-Z]$avant tout appel réseau (évite les 400 inutiles qui consomment le quota). - Économie de quota WS-BE : le connecteur n’appelle
WS-BE/1que si la fiche KYC retournedeclaration_BENEFICIAIRES_EFFECTIFS = "Oui". Les entreprises qui n’ont pas déclaré leurs BE ne déclenchent pas de décompte inutile. - Cache en mémoire TTL 24 h avec
invalidate()etinvalidateAll(). - Gestion erreurs métier HTTP 400 mappées vers 7 sous-types
RneExceptiontypés (voir tableau §7). - Cas particulier WS-BE : réponse HTTP 200 contenant « Cette entité ne possède pas de déclaration de bénéficiaire Effectif. » →
emptyList()retourné sans exception.
6. Data classes — fidèles aux annexes RNE
Section intitulée « 6. Data classes — fidèles aux annexes RNE »Les @SerialName de kotlinx-serialization reproduisent exactement les noms d’attributs JSON du RNE, y compris la typo originale QALITE_GESTION_FR (au lieu de QUALITE_GESTION_FR) pour rester déserialisables sans transformation côté RNE.
Blocs exposés :
KycBlocEntreprise: 50+ champs (fiche, adresses siège + activité, dates, association, direction, activités)KycDirectionMember: dirigeants (identité, qualité, dates nomination, pouvoirs)KycPouvoir: libellés AR/FR des pouvoirsUboRecord: bénéficiaires effectifs (type, capital direct/indirect, droits de vote, résident, statut)KybFullLookup: agrégat retourné avec métadonnéessource(LIVE vs CACHE) etretrievedAtEpochMs
7. Erreurs typées
Section intitulée « 7. Erreurs typées »| HTTP | Message RNE | Exception Kotlin |
|---|---|---|
| 400 | « Le solde disponible est insuffisant… » | RneException.InsufficientBalance |
| 400 | « …non associé à une entité active… » | RneException.InactiveEntity |
| 400 | « Aucune inscription correspondant… » | RneException.NotFound |
| 400 | « L’abonné n’est pas autorisé… » | RneException.SubscriberNotAuthorized |
| 400 | « Adresse IP non autorisé ! » | RneException.IpNotAllowed |
| 400 | « L’inscription à cet abonnement est inactive… » | RneException.SubscriptionInactive |
| 400 | « Ce motif WS Be n’a pas été trouvée… » | RneException.MotifNotFound |
| 404 | Not Found | RneException.NotFound |
| 429 | Rate limited | RneException.RateLimited |
| 5xx / IO | — | RneException.Transport |
Le service métier kyc-svc peut ainsi prendre des décisions fines :
InsufficientBalance→ notifier le tenant pour recharger son abonnement + activer fallback documentaire.InactiveEntity→ passer le dossier KYB enrejectedavec motif lisible pour l’agent.NotFound→ idem, ou relance manuelle avec un autre identifiant.RateLimited→ retry exponentiel côté tcr-svc.
8. Tests (12 cas passants)
Section intitulée « 8. Tests (12 cas passants) »| # | Intitulé | Couvre |
|---|---|---|
| 1 | Mapping JSON RNE → domaine complet | Annexe KYC officielle (CNRE) |
| 2 | Authorization Bearer + query params corrects | Spec annexe |
| 3 | UBO consommé si declaration=Oui | Économie quota |
| 4 | WS-BE sauté si declaration=non | Économie quota |
| 5 | WS-BE réponse 200 texte « pas de déclaration » | Cas métier |
| 6 | 400 Solde insuffisant → InsufficientBalance | Quota épuisé |
| 7 | 400 Entité inactive → InactiveEntity | Entité radiée |
| 8 | 400 IP non autorisée → IpNotAllowed | Contrôle IP |
| 9 | 400 Motif invalide WS-BE → MotifNotFound | Conf abonnement |
| 10 | 404 Not Found | Identifiant inconnu |
| 11 | Identifiant malformé → IllegalArgumentException | Validation amont |
| 12 | Cache hit (1 seul appel réseau pour 2 lookups) | Cache mémoire |
| 13 | invalidate() force un nouvel appel | Invalidation |
Sortie ./gradlew test :
RneConnectorTest > fetchFull — fiche RNE : mapping JSON → domaine complet PASSEDRneConnectorTest > fetchFull — Authorization Bearer + query params corrects PASSEDRneConnectorTest > fetchFull — UBO consommé automatiquement si déclaration Oui PASSEDRneConnectorTest > fetchFull — pas d'appel WS-BE si declaration=non (économie quota) PASSEDRneConnectorTest > WS-BE répond 'pas de déclaration' → liste vide, pas d'exception PASSEDRneConnectorTest > Solde insuffisant (400) → RneException.InsufficientBalance PASSEDRneConnectorTest > Entité inactive → RneException.InactiveEntity PASSEDRneConnectorTest > IP non autorisée → RneException.IpNotAllowed PASSEDRneConnectorTest > Motif invalide (WS-BE) → RneException.MotifNotFound PASSEDRneConnectorTest > 404 Not Found → RneException.NotFound PASSEDRneConnectorTest > Identifiant RNE malformé rejeté en amont (pas d'appel réseau) PASSEDRneConnectorTest > Cache : second appel dans la fenêtre TTL ne tape pas le réseau PASSEDRneConnectorTest > invalidate() force un nouvel appel PASSEDBUILD SUCCESSFUL9. Exemple d’usage
Section intitulée « 9. Exemple d’usage »val config = RneConfig( bearerToken = System.getenv("RNE_BEARER_TOKEN"), subscriptionId = 42, subscriberId = 7, codeMotif = 1,)
val connector = RneConnector(RneClient(config))val result = connector.fetchFull("1616343A")
println("Dénomination : ${result.kyc.denomination_FR}")println("Statut : ${result.kyc.STATUS}")println("Capital TND : ${result.kyc.capital_TND}")println("Dirigeants (n) : ${result.kyc.direction.size}")println("UBO (n) : ${result.ubo.size}")println("Source : ${result.source}") // LIVE ou CACHE10. Roadmap pour passer en production
Section intitulée « 10. Roadmap pour passer en production »- Resilience4j circuit breaker (après 5 erreurs 5xx en 60 s → mode
degraded). - Rate limiter client (200 req/min / tenant) pour protéger le solde RNE.
- Fallback documentaire dans
kyc-svcsidegradedouInsufficientBalance. - Audit trail WORM de chaque appel RNE (request-id, subscription-id, status, hash réponse).
- Vault pour
bearerTokenavec rotation à chaud. - Mapping vers schéma VitaKYC (
kyc_business+kyc_business_officer[]+kyc_business_ubo[]). - Métriques Prometheus :
rne_subscription_balance,rne_ws_kyc_latency_ms,rne_errors_total{type}. - Alerte proactive à 80 % consommation pour recharger avant épuisement.
- Horizon 2026 : bascule RNE 100 % en ligne S2 2026 (DIGIGO / MobileID) — adapter schémas à ce moment.
11. Pourquoi c’est important
Section intitulée « 11. Pourquoi c’est important »- Preuve terrain à présenter en démo AO La Poste Tunisienne et prospects banques TN.
- Time-to-market réduit : les tenants n’ont pas à développer eux-mêmes l’intégration RNE.
- Économie de coût : gestion automatique du quota + cache évite les consommations inutiles (environ 30-40 % d’économie vs appels naïfs).
- Robustesse : 7 erreurs métier typées = workflow KYB bien orienté côté agent.
POC validé le 2026-04-22 · 12 tests passants · aucune dépendance Spring.