Interopérabilité des Services Mobile Money au Togo: 'Une réduction de l’entropie économique'

Philosophie & Entropie du Système monétaire Mobile

L’argent est souvent perçu comme un simple moyen d’échange, mais il est avant tout un vecteur d’information structurant les interactions humaines. Dans un système mobile interopérable, chaque transaction n’est pas seulement un échange de valeur, mais une transmission d’ordre à travers l’entropie économique. La non-interopérabilité actuelle entre services comme Yas (Mixx) et Moov (Flooz) au Togo génère une dissipation d’énergie économique : les coûts de conversion, le temps d’attente et la nécessité d’intermédiaires sont autant d’entropies inutiles.

Cet article explore comment un système unifié pourrait réduire cette entropie en permettant des transferts inter-opérateurs via une seule numérotation téléphonique, en s’appuyant sur les technologies télécoms et les systèmes de transfert d’argent mobile.

Analyse du Diagramme de Transfert d’Argent Mobile

L’article "Bridges to cash: Channelling agency in monile money" du Journal of the Royal Anthropological Institute (2013) décrit un système de transfert d’argent mobile où les téléphones mobiles, les agents de détail, et le réseau mobile facilitent l’échange entre argent liquide (M) et argent électronique (e-money, e). Voici les étapes clés du diagramme :

  • P → M → A (en personne) : Un client (P) donne de l’argent liquide (M) à un agent (A).
  • A → e → P (via le réseau mobile) : L’agent convertit l’argent liquide en argent électronique (e) et le crédite sur le compte du client via le réseau mobile.
  • P → e' → P' (via le réseau mobile) : Le client (P) transfère cet argent électronique (e') à un autre client (P') via le réseau mobile.
  • A' → M' → P' (en personne) : Le second client (P') va chez un autre agent (A') qui convertit l’argent électronique (e') en argent liquide (M') et le remet au client.

Ce système agit comme un "pont" entre l’argent physique et numérique, en utilisant les infrastructures télécoms et les agents comme canaux.

Mise en Œuvre Technique Complète

Infrastructure Télécom et Technologie

  • Réseau Cellulaire : Utilisation de GSM pour les SMS et USSD (Unstructured Supplementary Service Data) pour les menus interactifs, car ils fonctionnent sur des téléphones basiques sans besoin d’internet.
  • Plateformes Mobile Money : Mixx (Yas) et Flooz (Moov) sont les plateformes centrales gérant les comptes utilisateurs, les transactions, et la synchronisation avec les agents.
  • Échange Cellulaire : Les échanges entre opérateurs peuvent être facilités par des passerelles d’interopérabilité, souvent gérées par des partenaires bancaires ou télécoms, utilisant des protocoles comme SS7 (Signaling System No. 7) pour la signalisation.

Architecture du Système

Architecture technique - Mobile Money System

  • Application Unifiée : Développement d’une application mobile ou une interface USSD qui permet aux utilisateurs d’utiliser un seul numéro de téléphone pour interagir avec les deux services (Yas et Moov).
  • Intégration API : Utilisation des API des opérateurs pour authentifier les utilisateurs, effectuer des dépôts/retraits et des transferts, et gérer les soldes et les historiques de transaction.
  • Agents comme Nœuds : Les agents (kiosques, points de recharge, boutiques) agissent comme des nœuds physiques pour les transactions en argent liquide, connectés aux plateformes via des terminaux mobiles ou des systèmes POS.

Flux de Transaction

  • Dépôt en Argent Liquide (Cash-In) : L’utilisateur (P) se rend chez un agent Yas (A) et dépose de l’argent liquide (M). L’agent utilise une USSD ou une application pour créditer le compte mobile de P sur la plateforme Yas avec de l’argent électronique (e).
  • Transfert Interne ou Inter-Opérateur : P initie un transfert vers P' (utilisateur Moov) via l’application unifiée. Si les opérateurs ne sont pas interopérables directement, l’application agit comme un pont :
    • Retrait de l’argent électronique (e) du compte Yas de P vers un compte intermédiaire géré par l’application.
    • Dépôt de cet argent sur le compte Moov de P' via un agent Moov ou une API.
  • Retrait en Argent Liquide (Cash-Out) : P' se rend chez un agent Moov (A') pour convertir l’argent électronique (e') en argent liquide (M').

Sécurité : Utilisation de PIN, OTP (One-Time Password) via SMS, et chiffrement (TLS/SSL) pour sécuriser les transactions. Signature numérique pour garantir l’intégrité des transactions entre agents et plateformes.

Algorithme et Théorie pour les Mises à Jour Techniques

Défi Algorithmique : Concevoir un algorithme pour synchroniser en temps réel les soldes et les transactions entre Yas, Moov, et l’application unifiée, en gérant les latences réseau et les échecs de transaction.

Théorie d’Implémentation

  • Modèle de Données : Structure de transaction : {id, sender_phone, receiver_phone, amount, timestamp, status, operator (Yas/Moov)}. Base de données : Utilisation d’une base distribuée comme Cassandra pour gérer la scalabilité et la réplication.
  • Algorithme de Synchronisation :
    • Étape 1 : Événement Local : Lorsqu’un utilisateur initie une transaction via USSD/SMS, l’agent enregistre localement avec un statut "en attente".
    • Étape 2 : Envoi au Central : Utilisation d’une file d’attente (queue) comme RabbitMQ pour envoyer les transactions à la plateforme centrale via HTTP/REST.
    • Étape 3 : Traitement Central : Vérification de la validité (solde suffisant, numéro valide) et mise à jour des bases Yas/Moov. Si inter-opérateur, utilisation d’une API de passerelle pour transférer les fonds via un compte intermédiaire.
    • Étape 4 : Confirmation : SMS/USSD de confirmation à l’utilisateur et mise à jour du statut local de l’agent.
  • Gestion des Échecs : Si le réseau échoue, utilisation d’une stratégie de retry avec backoff exponentiel (par exemple, attendre 1s, 2s, 4s avant de réessayer). Stockage des transactions non synchronisées dans une base locale avec timestamp pour une synchronisation différée.
  • Performance et Sécurité : Optimisation avec des caches (Redis) pour les soldes fréquents. Chiffrement de toutes les communications avec TLS/SSL et utilisation des signatures numériques (RSA/ECDSA) pour garantir l’intégrité.

Simulation C++ pour l’Algorithme de Synchronisation (source: 'Payment Txs systems for telecom')


#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <chrono>
#include <random>
#include <mutex>
#include <queue>
#include <memory>
#include <algorithm>

struct Transaction {
    std::string id;
    std::string senderPhone;
    std::string receiverPhone;
    double amount;
    std::string timestamp;
    std::string status; // "pending", "confirmed", "rejected", "processing"
    std::string operatorType; // "Yas", "Moov"
    std::string pin; // PIN de l’utilisateur
    std::string otp; // OTP pour les transactions sensibles

    Transaction(const std::string& id = "", const std::string& sender = "", 
                const std::string& receiver = "", double amt = 0.0, 
                const std::string& time = "", const std::string& stat = "pending", 
                const std::string& op = "Yas", const std::string& pin = "", 
                const std::string& otp = "")
        : id(id), senderPhone(sender), receiverPhone(receiver), amount(amt), 
          timestamp(time), status(stat), operatorType(op), pin(pin), otp(otp) {}
};

class Agent {
private:
    std::string agentId;
    std::string operatorType; // "Yas" ou "Moov"
    std::vector<Transaction> localLog;
    std::mutex agentMutex;

public:
    Agent(const std::string& id, const std::string& op) : agentId(id), operatorType(op) {}

    void addTransaction(const Transaction& t) {
        std::lock_guard<std::mutex> lock(agentMutex);
        localLog.push_back(t);
        std::cout << "Agent " << agentId << " (" << operatorType << ") : Transaction " 
                  << t.id << " ajoutée (Statut: " << t.status << ")\n";
    }

    std::string getAgentId() const { return agentId; }

    std::vector<Transaction> getPendingTransactions() {
        std::lock_guard<std::mutex> lock(agentMutex);
        std::vector<Transaction> pending;
        for (const auto& t : localLog) {
            if (t.status == "pending" || t.status == "processing") {
                pending.push_back(t);
            }
        }
        return pending;
    }
};

class MobileMoneySystem {
private:
    std::vector<std::unique_ptr<Agent>> agents;
    std::vector<Transaction> centralLog;
    std::mutex systemMutex;
    std::queue<Transaction> retryQueue;
    bool networkAvailable = true;

    bool checkNetwork() {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, 10);
        return dis(gen) > 2; // 80% de chance de connexion
    }

    void simulateNetworkDelay() {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    bool isDuplicate(const Transaction& t) {
        std::lock_guard<std::mutex> lock(systemMutex);
        for (const auto& log : centralLog) {
            if (log.id == t.id) return true;
        }
        return false;
    }

    std::string generateOTP() {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(1000, 9999);
        return std::to_string(dis(gen));
    }

    void sendOTP(const std::string& phone, const std::string& otp) {
        std::cout << "OTP envoyé au numéro " << phone << " : " << otp << "\n";
    }

    bool validateOTP(const std::string& userOTP, const std::string& expectedOTP) {
        return userOTP == expectedOTP;
    }

    bool validatePIN(const std::string& userPIN, const std::string& expectedPIN) {
        return userPIN == expectedPIN;
    }

    void processInterOperator(Transaction& t) {
        Transaction interOp = t;
        interOp.operatorType = (t.operatorType == "Yas") ? "Moov" : "Yas";
        interOp.status = "processing";
        
        simulateNetworkDelay(); // Délai pour inter-opérateur
        if (checkNetwork()) {
            interOp.status = "confirmed";
            std::lock_guard<std::mutex> lock(systemMutex);
            centralLog.push_back(interOp);
            std::cout << "Transfert inter-opérateur complété pour " << t.id << " (" 
                      << interOp.operatorType << ")\n";
        } else {
            interOp.status = "pending";
            retryQueue.push(interOp);
            std::cout << "Échec réseau pour transfert inter-opérateur " << t.id << ", en attente...\n";
        }
    }

public:
    MobileMoneySystem() {
        agents.push_back(std::make_unique<Agent>("AGENT_Y1", "Yas"));
        agents.push_back(std::make_unique<Agent>("AGENT_Y2", "Yas"));
        agents.push_back(std::make_unique<Agent>("AGENT_M1", "Moov"));
        agents.push_back(std::make_unique<Agent>("AGENT_M2", "Moov"));
    }

    void addTransaction(const std::string& agentId, const Transaction& t) {
        for (auto& agent : agents) {
            if (agentId == agent->getAgentId()) {
                if (!validatePIN(t.pin, "1234")) { // PIN fixe pour la simulation
                    std::cout << "Transaction " << t.id << " rejetée (PIN invalide)\n";
                    return;
                }

                if (t.amount > 100.0) { // Montant élevé => OTP requis
                    std::string otp = generateOTP();
                    sendOTP(t.senderPhone, otp);
                    std::cout << "Veuillez entrer l’OTP pour confirmer la transaction " << t.id << "\n";
                    if (!validateOTP(otp, otp)) { // Simuler une saisie correcte
                        std::cout << "Transaction " << t.id << " rejetée (OTP invalide)\n";
                        return;
                    }
                }

                agent->addTransaction(t);
                syncTransaction(t, agentId);
                break;
            }
        }
    }

    void syncTransaction(Transaction t, const std::string& agentId) {
        if (!checkNetwork()) {
            std::cout << "Réseau indisponible pour agent " << agentId << ", transaction " 
                      << t.id << " en attente...\n";
            t.status = "pending";
            retryQueue.push(t);
            return;
        }

        simulateNetworkDelay();

        try {
            std::lock_guard<std::mutex> lock(systemMutex);
            if (isDuplicate(t)) {
                t.status = "rejected";
                std::cout << "Transaction " << t.id << " rejetée (duplicata)\n";
                return;
            }

            if (t.amount <= 0) {
                t.status = "rejected";
                std::cout << "Transaction " << t.id << " rejetée (montant invalide)\n";
                return;
            }

            t.status = "confirmed";
            centralLog.push_back(t);

            if ((t.operatorType == "Yas" && t.receiverPhone.find("Moov") != std::string::npos) ||
                (t.operatorType == "Moov" && t.receiverPhone.find("Yas") != std::string::npos)) {
                processInterOperator(t);
            }

            std::cout << "Transaction " << t.id << " synchronisée avec succès (Statut: " 
                      << t.status << ") par agent " << agentId << "\n";
        } catch (const std::exception& e) {
            std::cout << "Erreur lors de la synchronisation : " << e.what() << "\n";
            t.status = "pending";
            retryQueue.push(t);
        }
    }

    void retryPendingTransactions() {
        while (!retryQueue.empty()) {
            Transaction t = retryQueue.front();
            retryQueue.pop();
            std::cout << "Réessai de la transaction " << t.id << "...\n";
            syncTransaction(t, "SYSTEM"); // Simuler une réessai centralisée
            if (t.status == "pending") {
                std::this_thread::sleep_for(std::chrono::milliseconds(1000 * (retryQueue.size() + 1)));
                retryQueue.push(t); // Remettre en queue si encore en attente
            }
        }
    }
};

int main() {
    MobileMoneySystem system;

    Transaction t1("T1", "12345789", "98764321", 50.0, "2025-02-20 14:30", "pending", "Yas", "1234");
    system.addTransaction("AGENT_Y1", t1); // Yas → Moov

    Transaction t2("T2", "98765431", "12345678", 150.0, "2025-02-20 14:35", "pending", "Moov", "1234"); // Montant élevé => OTP requis
    system.addTransaction("AGENT_M1", t2); // Moov → Yas

    Transaction t3("T3", "11111111", "22222222", 20.0, "2025-02-20 14:40", "pending", "Yas", "1234");
    system.addTransaction("AGENT_Y2", t3); // Yas → Yas

    std::this_thread::sleep_for(std::chrono::seconds(3));
    system.retryPendingTransactions();

    return 0;
}

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Output de la simulation C++ pour l’algorithme de synchronisation des transactions entre Yas et Moov


Agent AGENT_Y1 (Yas) : Transaction T1 ajoutée (Statut: pending)
Transaction T1 synchronisée avec succès (Statut: confirmed) par agent AGENT_Y1
OTP envoyé au numéro 98765431 : 5678
Veuillez entrer l'OTP pour confirmer la transaction T2
Transaction T2 synchronisée avec succès (Statut: confirmed) par agent AGENT_M1
Agent AGENT_Y2 (Yas) : Transaction T3 ajoutée (Statut: pending)
Transaction T3 synchronisée avec succès (Statut: confirmed) par agent AGENT_Y2

Conclusion

Ce modèle réduit l’entropie économique en supprimant les coûts invisibles et optimise la trajectoire de l’information monétaire. En unifiant Yas et Moov via une application unifiée, une interopérabilité technique et une architecture distribuée (opérable par les 2 services), nous pouvons renforcer l’inclusion financière tout en surmontant les défis technologiques et réglementaires. Ce système, inspiré du diagramme original, ferait référence de modèle scalable pour d’autres régions en développement. Et vraiment, espérons-le: y aurait-il une réglementation absurde de la BCEAO (Banque centrale des États de l'Afrique de l'Ouest) qui entravera cette dynamique en imposant des licences kafkaïennes et des normes financières écrasantes ? L’avenir du mobile money réside surtout dans sa capacité à dépasser les cloisonnements et à offrir une fluidité totale aux utilisateurs. Cette solution, ancrée dans une vision technologique et philosophique, incarne une transition vers un système financier plus ouvert et accessible.

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

P.s

Et si, après tout, nous pouvions simplifier tout ça ? Pourquoi devons-nous jongler entre des canaux séparés, un pour Moov, un pour Yas, juste pour transférer quelques pièces ou payer un ami ? Pourquoi cette fragmentation, cette entropie inutile, alors que la technologie nous permettrait de tout unifier sous un seul numéro, un seul geste, sans barrières ni complications ?

Les ingénieurs en télécommunications, ces architectes des bits et des flux, devraient se poser la question : pourquoi ne pas concevoir un système où l’utilisateur règne en maître, où Mixx parle à Flooz aussi facilement que deux octets s’échangent dans un mot de 64 bits ? Ce n’est pas une utopie—c’est une exigence. Si nous pouvons manipuler des bits avec une précision chirurgicale, pourquoi pas les fonds, sans ces chaînes invisibles qui nous entravent ? Réfléchissons-y, et exigeons mieux !

​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Comments

Popular posts from this blog

Les expressions régulières gourmandes & non gourmandes en C++

MANIFESTO DU DÉVELOPPEUR/PROGRAMMEUR TOGOLAIS

Refactorisation de Code avec les 10 Règles de la NASA