Sites Web11 min de lecture

Mobile Money disbursement & bulk payouts Afrique : Wave + OM + MTN MoMo (2026)

Mohamed Bah·Fondateur, Kolonell
19 mai 2026
Partager :
Mobile Money disbursement & bulk payouts Afrique : Wave + OM + MTN MoMo (2026)

Mobile Money disbursement & bulk payouts Afrique : Wave + OM + MTN MoMo (2026)

Sites Web

Au-delà de la paie, beaucoup de plateformes ont besoin de disburser des paiements en masse vers des wallets : commissions affiliés, remboursements, payouts marketplace, bourses étudiantes, aides sociales. En 2026, l'écosystème B2B mobile money africain est encore fragmenté — voici comment l'unifier.

TL;DR

- 4 providers majeurs UEMOA + Anglophone : Wave, OM, MTN MoMo, Airtel Money.

- Architecture unifiée : payment-router avec connector par provider.

- Idempotence + retry + reconciliation comptable critiques.

Architecture unifiée

`

[Plateforme]

[Bulk payout request]

[PayoutRouter — choisit provider par receiver]

┌───┴───┬──────┬──────┐

↓ ↓ ↓ ↓

[Wave] [OM] [MTN] [Airtel]

↓ ↓ ↓ ↓

└───┬───┴──────┴──────┘

[Webhook callbacks]

[Reconciliation + audit log]

`

Étape 1 — abstraction provider commune

`ts

// lib/payouts/types.ts

export interface PayoutProvider {

name: string;

countries: string[];

initiateBulk(payouts: PayoutRequest[]): Promise;

getStatus(externalId: string): Promise;

verifyWebhook(body: string, signature: string): boolean;

}

interface PayoutRequest {

externalId: string; // votre ID

amount: number; // XOF entier

currency: 'XOF' | 'XAF' | 'GHS' | 'NGN' | 'KES';

receiverMsisdn: string;

narration: string;

metadata?: Record;

}

`

Étape 2 — connector par provider

Wave Connector

`ts

class WaveConnector implements PayoutProvider {

name = 'wave';

countries = ['SN', 'CI', 'ML', 'BF'];

async initiateBulk(payouts: PayoutRequest[]) {

// ... voir article Wave Business B2B

}

async getStatus(externalId: string) {

const res = await fetch(https://api.wave.com/v1/business/disbursements/${externalId}, {

headers: { 'Authorization': Bearer ${process.env.WAVE_KEY} },

});

return res.json();

}

}

`

MTN MoMo Connector

`ts

class MtnMomoConnector implements PayoutProvider {

name = 'mtn-momo';

countries = ['CI', 'GH', 'NG', 'CM', 'UG', 'RW', 'ZM'];

async initiateBulk(payouts: PayoutRequest[]) {

const token = await this.getOAuthToken();

// MTN MoMo nécessite 1 call par receiver (pas de bulk natif)

const results = await Promise.all(

payouts.map(p => this.singleTransfer(p, token))

);

return { batchId: crypto.randomUUID(), results };

}

private async singleTransfer(payout: PayoutRequest, token: string) {

const referenceId = crypto.randomUUID();

await fetch('https://sandbox.momodeveloper.mtn.com/disbursement/v1_0/transfer', {

method: 'POST',

headers: {

'Authorization': Bearer ${token},

'X-Reference-Id': referenceId,

'X-Target-Environment': process.env.NODE_ENV === 'production' ? 'production' : 'sandbox',

'Ocp-Apim-Subscription-Key': process.env.MTN_SUB_KEY!,

'Content-Type': 'application/json',

},

body: JSON.stringify({

amount: String(payout.amount),

currency: payout.currency,

externalId: payout.externalId,

payee: { partyIdType: 'MSISDN', partyId: payout.receiverMsisdn },

payerMessage: payout.narration,

payeeNote: payout.narration,

}),

});

return { externalId: payout.externalId, referenceId, status: 'PENDING' };

}

}

`

Routing intelligent

`ts

// lib/payouts/router.ts

const PROVIDERS: Record = {

wave: new WaveConnector(),

mtn: new MtnMomoConnector(),

om: new OrangeMoneyConnector(),

airtel: new AirtelConnector(),

};

export async function routeBulkPayouts(payouts: PayoutRequest[]) {

// Grouper par provider selon receiver country + détection wallet

const byProvider: Record = {};

for (const p of payouts) {

const provider = await detectProvider(p.receiverMsisdn);

if (!byProvider[provider]) byProvider[provider] = [];

byProvider[provider].push(p);

}

// Disbursement parallèle

const results = await Promise.all(

Object.entries(byProvider).map(async ([providerName, providerPayouts]) => {

const provider = PROVIDERS[providerName];

const result = await provider.initiateBulk(providerPayouts);

return { provider: providerName, result };

Besoin d'un site web professionnel ?

Kolonell crée des sites web qui attirent des clients, optimisés pour le marché sénégalais. Devis gratuit en 2 minutes.

})

);

return results.flat();

}

async function detectProvider(msisdn: string): Promise {

// Logique : préfixe + heuristiques + base data

if (msisdn.startsWith('+221')) {

// SN : Wave > OM > Free

const stored = await prisma.user.findFirst({

where: { phone: msisdn },

select: { preferredWalletProvider: true },

});

return stored?.preferredWalletProvider ?? 'wave';

}

// etc. pour autres pays

}

`

Étape 3 — reconciliation + audit

`prisma

model BulkPayoutBatch {

id String @id @default(cuid())

organizationId String

initiatedBy String // user.id

reason String // PAYROLL / COMMISSIONS / REFUNDS / SUBSIDY / OTHER

totalAmount Int

totalReceivers Int

status String // DRAFT / IN_PROGRESS / COMPLETED / PARTIALLY_FAILED

createdAt DateTime @default(now())

completedAt DateTime?

payouts Payout[]

}

model Payout {

id String @id @default(cuid())

batchId String

batch BulkPayoutBatch @relation(fields: [batchId], references: [id])

externalId String

receiverId String

receiverMsisdn String

amount Int

currency String

provider String // WAVE / OM / MTN / AIRTEL

providerTxId String?

status String // PENDING / SUCCESS / FAILED / REVERSED

errorMessage String?

initiatedAt DateTime @default(now())

completedAt DateTime?

@@unique([externalId, provider])

@@index([status, createdAt])

}

model PayoutAuditLog {

id String @id @default(cuid())

payoutId String

action String // INITIATED / PROCESSED / WEBHOOK_RECEIVED / RECONCILED

payload Json

timestamp DateTime @default(now())

}

`

Étape 4 — retry et idempotence

`ts

// jobs/retry-failed-payouts.ts

export async function retryFailedPayouts() {

const failed = await prisma.payout.findMany({

where: {

status: 'FAILED',

errorMessage: { in: ['NETWORK_ERROR', 'TEMPORARY_FAILURE'] },

initiatedAt: { gt: new Date(Date.now() - 24 * 60 * 60 * 1000) },

},

});

for (const payout of failed) {

const provider = PROVIDERS[payout.provider];

try {

// Idempotence via externalId : si déjà traité côté provider, retournera SUCCESS

const result = await provider.initiateBulk([{

externalId: payout.externalId,

amount: payout.amount,

currency: payout.currency as any,

receiverMsisdn: payout.receiverMsisdn,

narration: 'retry',

}]);

await prisma.payout.update({

where: { id: payout.id },

data: {

status: result.results[0].status === 'success' ? 'SUCCESS' : 'FAILED',

providerTxId: result.results[0].transaction_id,

},

});

} catch (e) {

// Logger

}

}

}

`

Cas d'usage typiques

Marketplace commissions

Plateforme avec 2 000 vendeurs payés mensuellement = bulk disbursement Wave/OM/MTN selon leur pays.

Affiliés / apporteurs d'affaires

Programme apporteurs Kolonell : commissions mensuelles vers wallets Wave/OM des apporteurs.

Remboursements customers

E-commerce : refund vers wallet d'origine. Voir refund Wave/OM →.

Bourses / aides sociales

ONG distribuant aides : 5 000 bénéficiaires/mois × 25K XOF = 125M FCFA disbursés en 1h vs 3 mois manuel.

Subsides agricoles

Programme gouvernemental SN distribution intrants : 8 000 paysans, paiement direct wallet OM = transparence + traçabilité.

Coûts comparés

ProviderFrais bulkDélaiSandbox
Wave SN/CI0.5-1.0 %< 5 min
Orange Money1.5-2.0 %5-15 min
MTN MoMo0.5-1.5 %5-30 min
Airtel Money1.0-2.0 %5-15 min
Banque virement bulk200 XOF/transactionT+1 jours

Pour > 100 transactions : mobile money domine en coût et délai.

FAQ

Q : Plafond bulk Wave ?

R : 50M XOF par batch. Au-delà, splitter en plusieurs batches.

Q : Reverser FCFA d'une plateforme USD ?

R : Stripe Connect peut payer en USD vers compte EUR/USD, puis conversion XOF via Wise. Pas direct vers Wave wallets.

Q : Conformité ?

R : Loi 2008-12 SN sur traitement données wallet numbers. AML/KYC strict si volume > 50M FCFA/mois (déclaration BCEAO).

Conclusion

Bulk payouts mobile money Afrique en 2026 = brique critique pour plateformes scaling. Architecture unifiée 4-12 semaines selon ambition. Économies massives vs paie manuelle / virements bancaires / agences cash.

Tags :#Mobile Money#Bulk Payouts#Wave#MTN MoMo#Disbursement#Afrique
Partager :

Mohamed Bah

Fondateur, Kolonell

Passionné par le digital et l'entrepreneuriat en Afrique, Mohamed accompagne les entreprises sénégalaises dans leur transformation digitale depuis 2020. Fondateur de Kolonell, il croit que chaque PME mérite une présence en ligne professionnelle et accessible.