Field 04 of 07

Fintech & payments

Collect Mobile Money payments without ever losing or duplicating a transaction.

Proven on :Syitech CashKultixEbedouFlokSydica

Since June 2022

  • idempotence
  • webhooks signés
  • partie double
  • temporisation exponentielle
  • réconciliation
  • machine à états
  • USSD / OTP
  • multi-opérateurs

Demonstration — no real payment, no operator contacted.

Transaction state machine

  1. INITIATED

    idempotency key issued

  2. AWAITING_OTP

    USSD then OTP code

  3. CONFIRMED

    signed webhook verified

  • FAILED
  • EXPIRED
  • REFUNDED

Operator

Selected: Orange Money — USSD then OTP code

Failure simulator

no failure simulated

Idempotency, in one action

The network drops mid-call: the request goes again, the transaction does not double.

Requests sent
0
Transactions created
0
Replayed key

Webhook log

0 events

Double-entry postings

15 000 XOF · service fee 1,00%

No postings until the transaction is confirmed.

Technical panel

Transaction id

Idempotency key

Attempts— / 5

Webhook signature

Pending

Latency per step

Initiation
OTP
Confirmation

Amount / fee / net

15 000F CFA

1,00% fee · net 14 850 XOF

Ledger state

4
Opérateurs intégrés

Orange · MTN · Moov · Wave

5
Tentatives de reprise

temporisation exponentielle

The mechanism

How it works.

Client

clé d'idempotence générée

API

création de la transaction

PSP

Wave · CinetPay · Fineopay

Opérateur

USSD puis code OTP

Webhook signé

reprise 5 tentatives

Vérification

HMAC-SHA256

Partie double

débit = crédit · réconciliation

Côté encaissement — le webhook fait foi.

Le retour synchrone de l'API est immédiat mais ment : l'opérateur peut confirmer après coup, ou infirmer. Seul le webhook signé fait foi ; le retour synchrone ne sert qu'à afficher un état d'attente. La reprise applique une temporisation exponentielle sur cinq tentatives, parce qu'un réseau qui coupe en cours d'appel est la règle et non l'exception.

Côté comptabilité — l'équilibre d'abord.

Chaque transaction confirmée produit ses écritures en partie double, contrôlées à l'enregistrement : une écriture déséquilibrée n'est jamais émise. La clé d'idempotence est générée par le client à l'ouverture du formulaire et rejouée telle quelle à chaque tentative, ce qui protège du double appui avant la première réponse — une contrainte d'unicité en base rattrape les collisions.

The trade-offs

What I ruled out, and why.

A technical choice without its cost is not a choice, it is a preference.

01

Se fier au retour de l'API, ou attendre le webhook ?

The problem

Le retour synchrone de l'API est immédiat mais ment : l'opérateur peut confirmer après coup, ou infirmer.

The options

Se fier au retour synchrone · attendre le webhook · combiner les deux.

What I chose

Le webhook signé fait seul foi ; le retour synchrone ne sert qu'à afficher un état d'attente.

The cost

L'interface doit gérer un état intermédiaire, et une transaction peut rester en attente plusieurs minutes.

02

Clé d'idempotence : client ou serveur ?

The problem

Une clé d'idempotence générée par le serveur ne protège pas d'un double appui avant la première réponse.

The options

Clé côté serveur · clé côté client · pas de clé, déduplication a posteriori.

What I chose

Clé générée par le client à l'ouverture du formulaire, rejouée telle quelle à chaque tentative.

The cost

Il faut faire confiance au client sur l'unicité — d'où une contrainte d'unicité en base qui rattrape les collisions.

03

Un seul prestataire de paiement, ou plusieurs ?

The problem

Un seul prestataire de paiement simplifie tout, mais une panne de son côté arrête l'encaissement.

The options

Un PSP unique · plusieurs PSP derrière une interface commune.

What I chose

Plusieurs, avec les frais de service paramétrés par méthode en points de base.

The cost

Chaque PSP a son format de webhook, sa signature et ses codes d'erreur — la couche d'abstraction est une vraie charge de maintenance.

Proof in the code

Two real extracts.

app/Services/Payment/PaymentStateMachine.php

Extract to be supplied

app/Http/Controllers/Webhooks/PspWebhookController.php

Extract to be supplied

Code Mes Rêves

Alexis Kouakou