Configuration — Géographie, transactions, frais et limites
Pays et devises supportés, types de transactions et canaux autorisés, calcul des frais (paliers et tranches), application des limites par entité et période. Pour la vue d'ensemble, voir Configuration — overview.
Country
Referentiel des pays avec evaluation de risque pour la conformite.
| Champ | Type | Description |
|---|---|---|
code | varchar(2) | Code ISO 3166-1 alpha-2, unique (ex: CG, CM, FR) |
code3 | varchar(3) | Code ISO 3166-1 alpha-3, unique (ex: COG, CMR, FRA) |
name / name_en | varchar(100) | Nom en francais / anglais |
region | varchar(50) | Region geographique |
phone_code | varchar(10) | Indicatif telephonique (+242, +237) |
numeric_code | varchar(3) | Code ISO numerique |
time_zone | varchar(50) | Fuseau horaire |
risk_level | enum | low, medium, high, blocked |
sanction_status | enum | none, partial, full |
is_active | boolean | Pays actif/inactif |
Endpoint cle : GET /countries/:id/configuration retourne la configuration complete d'un pays en une seule requete : devises, moyens de paiement, types de transactions, limites, regles, seuils, frais.
Routes specifiques :
GET /countries/:id/active-payment-methods Moyens de paiement actifs
GET /countries/:id/transaction-types Types de transactions disponibles
GET /countries/:id/limits Limites de transactions
GET /countries/:id/rules Regles inter-pays
GET /countries/:id/thresholds Seuils KYC
GET /countries/:id/fees Structure des frais (avec paliers)
GET /countries/:id/cities Villes du pays
GET /countries/:id/zones Zones geographiques
PATCH /countries/:id/risk-level Modifier le niveau de risqueCurrency
Referentiel des devises ISO 4217.
| Champ | Type | Description |
|---|---|---|
code | varchar(3) | Code ISO (EUR, USD, XAF), normalise en majuscules |
numeric_code | varchar(3) | Code numerique ISO (978, 840, 950), padde a 3 chiffres |
symbol | varchar(10) | Symbole (FCFA, $, EUR) |
minor_unit | integer | Nombre de decimales (2 pour EUR, 0 pour JPY) |
reporting_threshold | decimal(15,2) | Seuil de reporting reglementaire |
CountryCurrency
Association N-N entre pays et devises.
| Champ | Type | Description |
|---|---|---|
country_id | uuid | FK → Country |
currency_id | uuid | FK → Currency |
is_primary | boolean | Devise principale du pays |
effective_from | date | Date d'effet |
Un pays peut avoir plusieurs devises. Une seule est marquee is_primary.
City
Villes rattachees a un pays.
| Champ | Type | Description |
|---|---|---|
country_id | uuid | Reference vers Country (indexe, sans FK cross-schema) |
name | varchar(100) | Nom de la ville |
code | varchar(20) | Code ville |
latitude / longitude | decimal | Coordonnees GPS |
3. Types de transactions et canaux
TransactionType
Definit les types de transactions disponibles sur la plateforme.
| Champ | Type | Description |
|---|---|---|
code | varchar(20) | Identifiant unique (TRANSFER, PAYMENT, CASH_IN, CASH_OUT, BILL_PAYMENT...) |
name | varchar(100) | Libelle |
category | varchar(50) | Categorie metier |
requires_kyc | boolean | Necessite une verification KYC |
min_amount / max_amount | decimal(15,4) | Montants limites globaux |
default_currency_id | uuid | Devise par defaut (FK → Currency) |
vat_rate | decimal(5,4) | Taux de TVA en fraction (0.1925 = 19,25 %). 0 = non taxe. Voir §6 TVA |
vat_base | varchar(12) | Assiette TVA : none, commission, principal |
C'est l'entite pivot du service : les frais, la TVA, les limites, commissions, regles et autorisations y sont rattaches.
Configuration cote CMMS : sur la page detail d'un type (
Configs → Transactions → Types), la TVA se regle dans une section dediee « TVA » (assiette + taux saisi en %, converti en fraction).
TransactionChannel
Canaux de paiement disponibles (MOBILE_MONEY, CARD, BANK_TRANSFER, CASH, NXPAY_WALLET...).
| Champ | Type | Description |
|---|---|---|
code | varchar(20) | Identifiant unique |
name | varchar(100) | Libelle |
AuthorizedChannel
Definit quels canaux sont autorises pour chaque type de transaction.
| Champ | Type | Description |
|---|---|---|
transaction_type_id | uuid | FK → TransactionType |
transaction_channel_id | uuid | FK → TransactionChannel |
is_active | boolean | Autorisation active |
Exemple : le type CASH_IN peut etre autorise sur MOBILE_MONEY et CASH, mais pas sur CARD.
TransactionBetween
Regles d'autorisation entre roles utilisateur.
| Champ | Type | Description |
|---|---|---|
transaction_type_id | uuid | FK → TransactionType |
from_role | varchar(50) | Role source (user, merchant, agent...) |
to_role | varchar(50) | Role destination |
is_authorized | boolean | Transaction autorisee entre ces roles |
Exemple : TRANSFER est autorise de user vers user, mais pas de user vers agent.
4. Systeme de frais
Le systeme de frais est hierarchique a 3 niveaux :
FeeType Quel type de frais ? (COMMISSION, TAX, PLATFORM_FEE, PROCESSING_FEE...)
└── TransactionTypeFee Comment le calculer pour ce type de transaction ?
└── FeeTier Quels paliers pour le mode "tiered" ?FeeType
Types de frais globaux de la plateforme.
| Champ | Type | Description |
|---|---|---|
code | varchar(20) | Identifiant unique (COMMISSION, TAX, SERVICE_FEE...) |
name | varchar(100) | Libelle |
is_mandatory | boolean | Frais obligatoire |
calculation_order | integer | Ordre de calcul (les frais sont calcules sequentiellement) |
TransactionTypeFee
Configuration de calcul d'un frais pour un type de transaction donne.
| Champ | Type | Description |
|---|---|---|
transaction_type_id | uuid | FK → TransactionType |
fee_type_id | uuid | FK → FeeType |
calculation_method | enum | fixed, percentage, ou tiered |
amount | decimal(15,4) | Montant fixe (si fixed) |
percentage | decimal(5,4) | Pourcentage (si percentage) |
min_amount / max_amount | decimal(15,4) | Plafonnement min/max du frais calcule |
paid_by | enum | sender, recipient, platform, merchant |
effective_from / effective_to | date | Periode de validite |
scheduled_status | enum | active, scheduled, cancelled |
Regle metier : il ne peut y avoir qu'une seule configuration active par couple (transaction_type, fee_type) a une date donnee. A la creation d'une nouvelle configuration, les precedentes sont automatiquement desactivees.
Versioning : chaque modification cree une nouvelle version. L'historique est consultable via GET /transaction-type-fees/:id/versions.
Planification : on peut creer une configuration avec effective_from dans le futur et scheduled_status=scheduled. Elle deviendra active a la date prevue.
FeeTier
Paliers de frais pour le mode tiered. Permet de definir des frais degressifs ou progressifs selon le montant.
| Champ | Type | Description |
|---|---|---|
transaction_type_fee_id | uuid | FK → TransactionTypeFee |
tier_order | integer | Ordre du palier (1, 2, 3...) |
min_amount / max_amount | decimal(15,4) | Tranche de montant |
fixed_amount | decimal(15,4) | Frais fixe pour cette tranche |
percentage | decimal(5,4) | Pourcentage pour cette tranche |
Regles de validation :
- Les paliers doivent etre contigus (pas de trous)
- Le premier palier doit commencer a
min_amount=0 - Le dernier palier peut avoir
max_amount=null(ouvert) - Pas de chevauchement entre tranches
tier_orderunique et sequentiel
Exemple (frais degressifs sur un TRANSFER) :
| Palier | Tranche | Frais |
|---|---|---|
| 1 | 0 - 10 000 FCFA | 200 FCFA fixe |
| 2 | 10 000 - 100 000 FCFA | 1.5% |
| 3 | 100 000+ FCFA | 1% (plafonne a 5 000 FCFA) |
Simulation de frais
POST /transaction-type-fees/simulate
Calcule le montant exact des frais pour un montant donne, quel que soit le mode de calcul.
// Requete
{
"transaction_type_fee_id": "uuid",
"amount": 50000
}
// Reponse
{
"amount": 50000,
"calculation_method": "tiered",
"applicable_tier": { "tier_order": 2, "min_amount": 10000, "max_amount": 100000 },
"calculated_fee": 750,
"capped_fee": 750,
"breakdown": {
"percentage": 1.5,
"min_cap": null,
"max_cap": 5000,
"was_capped": false
}
}Le simulateur :
- Identifie la methode de calcul (fixed, percentage, tiered)
- Pour
tiered: trouve le palier applicable selon le montant - Calcule le frais brut
- Applique le plafonnement min/max si configure
- Retourne le detail complet du calcul
5. Limites de transactions
TransactionLimit
Definit des plafonds de montant par type de transaction, type de limite et type d'entite.
| Champ | Type | Description |
|---|---|---|
transaction_type_id | uuid | FK → TransactionType |
limit_type | enum | per_transaction, daily, monthly |
entity_type | enum | user, merchant, agent, organization, role, global, customer |
entity_id | uuid | ID specifique (null pour les limites globales/role) |
amount | decimal(15,4) | Montant plafond |
currency_id | uuid | FK → Currency (optionnel) |
effective_from / effective_to | date | Periode de validite |
scheduled_status | enum | active, scheduled, cancelled |
Hierarchie des limites
Les limites sont evaluees par ordre de specificite. La plus restrictive s'applique :
user/customer (specifique) ← la plus prioritaire
└── merchant/agent
└── organization
└── role
└── global ← la moins prioritaireGET /transaction-limits/applicable/:transactionTypeId
Retourne les limites applicables consolidees (per_transaction, daily, monthly) pour un type de transaction, en appliquant la hierarchie ci-dessus.
// Reponse
{
"perTransaction": { "id": "...", "amount": 500000, "entity_type": "global" },
"daily": { "id": "...", "amount": 2000000, "entity_type": "role" },
"monthly": { "id": "...", "amount": 10000000, "entity_type": "global" },
"limits": [ /* toutes les limites brutes */ ]
}Simulation de limites
POST /transaction-limits/simulate
Verifie si un montant respecte les limites configurees.
// Requete
{
"limit_type": "per_transaction",
"entity_type": "user",
"entity_id": "uuid-optionnel",
"transaction_type_id": "uuid",
"test_amount": 750000,
"test_date": "2025-01-15"
}
// Reponse
{
"allowed": false,
"message": "Le montant 750000 depasse la limite de 500000",
"limit_info": { "amount": 500000, "limit_type": "per_transaction" },
"suggestions": [
"Reduire le montant a 500000 maximum",
"Diviser en plusieurs transactions"
]
}Validation croisee
A la creation, le systeme verifie la coherence entre les types de limites :
per_transaction<daily<monthly(si les 3 existent pour la meme entite)- Pas de chevauchement de periodes pour le meme tuple (limit_type, entity_type, transaction_type, entity_id)
6. TVA
La TVA est portee par type de transaction (colonnes vat_rate / vat_base de TransactionType). Par defaut un type n'est pas taxe (vat_base=none, vat_rate=0).
Assiette (vat_base)
| Valeur | TVA calculee sur | Exemple |
|---|---|---|
none | — (non taxe) | P2P, cash-in/out |
commission | la commission (frais de service) | paiement de masse taxe |
principal | le montant de l'operation | achat d'UV / emission |
Le taux (vat_rate) est stocke en fraction (0.1925 = 19,25 %), borne a 9.9999. L'arrondi est au plus proche (la TVA est une charge fiscale reversee).
Reversement — compte VAT_COLLECTION
La TVA calculee est isolee sur le compte systeme VAT_COLLECTION (…0205, cote ledger-wallets) via une double-entree dediee (operation_type = tax), distincte des frais (FEE_COLLECTION). Cela permet le reversement fiscal ulterieur sans melange avec les fonds principaux.
Exemple — achat d'UV de 1 000 000 par une entreprise, TVA 19,25 % sur le principal : TVA = 192 500 -> VAT_COLLECTION +192 500. L'entreprise recoit le plein montant d'UV ; la TVA est portee « en sus » (cf. flux Tresorerie).
7. Remises negociees par entite
Une entreprise (ou un marchand) peut negocier un taux de commission custom, defini par type de transaction. Il surclasse le percentage du bareme standard (TransactionTypeFee).
EntityFeeOverride
| Champ | Type | Description |
|---|---|---|
entity_type | varchar(20) | organization ou merchant |
entity_id | uuid | Identifiant de l'entite |
transaction_type_id | uuid | Type concerne (null = tous les types) |
commission_rate | decimal(5,4) | Taux negocie en fraction (0.0050 = 0,5 %) |
effective_from / effective_to | date | Periode de validite |
is_active | boolean | Remise active |
approved_by | uuid | Auteur (tracabilite) |
Precedence : une remise ciblant ce type gagne sur une remise « tous types » (transaction_type_id=null). Append-only audite (on desactive plutot que de modifier).
Endpoints (/entity-fee-overrides) : POST (creer), GET /entity/:entityType/:entityId (lister pour une entite), GET /resolve (resoudre la remise applicable), PATCH /:id/deactivate, DELETE /:id.
Configuration cote CMMS : onglet Tarification de la fiche entreprise — selection du type + taux negocie (en %), dates d'effet.
8. Moteur de tarification unifie
configuration est l'unique source du calcul tarifaire : commission (bareme + remise negociee) et TVA. Tous les flux (P2P, paiement de masse, achat d'UV, …) lisent ce calcul via la TransactionPolicy — aucun recalcul ailleurs.
Previsualisation
| Endpoint | Usage |
|---|---|
POST /transaction-policy/preview | tarifie un montant pour un type + entite -> { commission, vat, net, totalToDebit, … } |
POST /transaction-policy/preview-batch | tarifie N montants en un seul appel (policy resolue une fois) — pense pour le paiement de masse |
// POST /transaction-policy/preview
{ "operationCode": "BULK_TRANSFER", "amount": "25000", "currency": "XAF",
"entityType": "organization", "entityId": "<uuid>" }
// Reponse (pricing) — entreprise a 2 % negocie
{ "commission": "500", "vat": "0", "net": "25000", "totalToDebit": "25500",
"commissionPercentage": 2, "vatBase": "none", "vatRate": 0 }La commission applique la remise negociee si l'entite en a une (ici 2 %), sinon le bareme standard du type. La TVA suit l'assiette/taux du type.