Sites Web11 min de lecture

Wave Business B2B : paie salaires + API en 2026 (Sénégal, CI)

Mohamed Bah·Fondateur, Kolonell
19 mai 2026
Partager :
Wave Business B2B : paie salaires + API en 2026 (Sénégal, CI)

Wave Business B2B : paie salaires + API en 2026 (Sénégal, CI)

Sites Web

La paie manuelle au Sénégal ou Côte d'Ivoire = nightmare comptabilité : virements bancaires un par un, espèces pour les non-bancarisés, retards récurrents, conflits employés. Wave Business propose depuis 2024 une API B2B pour disburser des salaires en masse via mobile money.

TL;DR

- Wave Business B2B API : disburse paie en bulk vers wallets employés.

- Coût : 0.5-1.0 % par transaction (négocié à volume).

- Compatible multi-pays UEMOA (SN, CI, ML, BF).

- Conformité : déclaration CSS / IPRES + IR salaires séparée.

Workflow paie automatisée

`

[Mois M : préparer fichier paie]

[Validation DRH + comptable]

[POST API Wave Business : bulk_disbursement]

[Wave traite + envoie SMS confirmation à chaque employé]

[Webhook réussite/échec par transaction]

[Reconciliation comptable auto]

[Bulletins paie générés + envoyés WhatsApp/email]

`

Étape 1 — modèle de données

`prisma

model Employee {

id String @id @default(cuid())

organizationId String

firstName String

lastName String

email String

phone String // E.164

whatsapp String?

ninea String?

cniNumber String @unique

iban String? // pour virement bancaire si applicable

walletProvider String // WAVE / ORANGE_MONEY / FREE / BANK

walletNumber String // n° wallet ou IBAN selon provider

baseSalary Int // brut XOF

contractType String // CDI / CDD / INTERIM

startedAt DateTime

endedAt DateTime?

isActive Boolean

}

model PayrollRun {

id String @id @default(cuid())

organizationId String

month String // "2026-05"

status String // DRAFT / VALIDATED / DISBURSING / COMPLETED / FAILED

totalGross Int

totalNet Int

totalTaxes Int // CSS + IPRES + IR

validatedBy String?

validatedAt DateTime?

disbursedAt DateTime?

items PayrollItem[]

}

model PayrollItem {

id String @id @default(cuid())

payrollRunId String

payrollRun PayrollRun @relation(fields: [payrollRunId], references: [id])

employeeId String

employee Employee @relation(fields: [employeeId], references: [id])

baseSalary Int

bonuses Int

overtime Int

grossSalary Int

ipresContribution Int // 5.6 % salarié + 8.4 % employeur SN

cssContribution Int // 7 %

incomeTax Int // tranches IR

netSalary Int

paymentStatus String // PENDING / SENT / RECEIVED / FAILED

walletTransactionId String?

payslipPdfUrl String?

}

`

Étape 2 — calcul net salaires Sénégal

Au Sénégal, retenues légales 2026 :

  • IPRES : 5.6 % salarié (employeur 8.4 %)
  • CSS : 7 % salarié sur plafond
  • Impôt sur le revenu (IR) : tranches 0-40 %

`ts

// lib/payroll/senegal.ts

export function computeNetSenegal(grossXof: number) {

const ipresEmployee = grossXof * 0.056;

const cssEmployee = Math.min(grossXof, 432000) * 0.07; // plafond CSS

const taxableIncome = grossXof - ipresEmployee - cssEmployee;

// Barème IR Sénégal 2026 (annuel converti mensuel)

let incomeTax = 0;

if (taxableIncome > 50000) {

if (taxableIncome <= 75000) incomeTax = (taxableIncome - 50000) * 0.20;

else if (taxableIncome <= 100000) incomeTax = 5000 + (taxableIncome - 75000) * 0.25;

else if (taxableIncome <= 200000) incomeTax = 11250 + (taxableIncome - 100000) * 0.30;

else if (taxableIncome <= 500000) incomeTax = 41250 + (taxableIncome - 200000) * 0.35;

else incomeTax = 146250 + (taxableIncome - 500000) * 0.40;

}

const netSalary = grossXof - ipresEmployee - cssEmployee - incomeTax;

return {

gross: grossXof,

ipres: Math.round(ipresEmployee),

css: Math.round(cssEmployee),

incomeTax: Math.round(incomeTax),

net: Math.round(netSalary),

};

}

`

Étape 3 — bulk disbursement via API Wave

`ts

// jobs/disburse-payroll.ts

async function disbursePayroll(payrollRunId: string) {

const run = await prisma.payrollRun.findUnique({

where: { id: payrollRunId },

include: { items: { include: { employee: true } } },

});

// Filtrer employés Wave uniquement

const waveItems = run.items.filter(i => i.employee.walletProvider === 'WAVE');

const bulkRequest = {

receivers: waveItems.map(item => ({

external_id: item.id,

amount: String(item.netSalary),

currency: 'XOF',

receiver_msisdn: item.employee.walletNumber,

narration: Salaire ${run.month},

})),

};

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.

const res = await fetch('https://api.wave.com/v1/business/bulk-disbursements', {

method: 'POST',

headers: {

'Authorization': Bearer ${process.env.WAVE_BUSINESS_API_KEY},

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

'Idempotency-Key': payroll_${payrollRunId},

},

body: JSON.stringify(bulkRequest),

});

const result = await res.json();

// Wave retourne batch_id + statuts individuels par receiver

// Mettre à jour PayrollItems

for (const r of result.results) {

await prisma.payrollItem.update({

where: { id: r.external_id },

data: {

paymentStatus: r.status === 'success' ? 'SENT' : 'FAILED',

walletTransactionId: r.transaction_id,

},

});

}

}

`

Idempotency-Key empêche double disbursement si bug.

Étape 4 — webhooks confirmation

`ts

// app/api/webhooks/wave-business/route.ts

export async function POST(req: NextRequest) {

const body = await req.text();

const signature = req.headers.get('wave-signature') ?? '';

// Vérifier signature HMAC

const expected = crypto

.createHmac('sha256', process.env.WAVE_BUSINESS_WEBHOOK_SECRET!)

.update(body)

.digest('hex');

if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {

return NextResponse.json({ error: 'invalid_signature' }, { status: 401 });

}

const event = JSON.parse(body);

if (event.type === 'disbursement.completed') {

await prisma.payrollItem.update({

where: { id: event.external_id },

data: { paymentStatus: 'RECEIVED' },

});

// Notifier employé WhatsApp

const item = await prisma.payrollItem.findUnique({

where: { id: event.external_id },

include: { employee: true, payrollRun: true },

});

await sendWhatsApp(item.employee.whatsapp ?? item.employee.phone, {

template: 'payroll_received',

params: [

item.employee.firstName,

item.netSalary.toLocaleString(),

item.payrollRun.month,

],

});

}

return NextResponse.json({ ok: true });

}

`

Étape 5 — bulletins paie automatiques

`ts

// jobs/generate-payslips.ts

import PDFKit from 'pdfkit';

async function generatePayslip(payrollItem) {

const doc = new PDFKit();

const buffers = [];

doc.on('data', buffers.push.bind(buffers));

// En-tête

doc.fontSize(16).text(BULLETIN DE PAIE - ${payrollItem.payrollRun.month});

doc.fontSize(10).text(Employeur : ${org.name} (NINEA ${org.ninea}));

doc.text(Employé : ${employee.firstName} ${employee.lastName});

doc.text(Matricule : ${employee.id});

doc.text(CNI : ${employee.cniNumber});

// Détail

doc.moveDown();

doc.text('GAINS', { underline: true });

doc.text(Salaire de base : ${item.baseSalary.toLocaleString()} XOF);

doc.text(Primes : ${item.bonuses.toLocaleString()} XOF);

doc.text(Heures supp. : ${item.overtime.toLocaleString()} XOF);

doc.text(SALAIRE BRUT : ${item.grossSalary.toLocaleString()} XOF);

doc.text('RETENUES', { underline: true });

doc.text(IPRES (5.6%) : -${item.ipresContribution.toLocaleString()} XOF);

doc.text(CSS (7%) : -${item.cssContribution.toLocaleString()} XOF);

doc.text(IR : -${item.incomeTax.toLocaleString()} XOF);

doc.fontSize(14).text(NET À PAYER : ${item.netSalary.toLocaleString()} XOF, { align: 'right' });

doc.fontSize(9).text(Versé sur Wave ${employee.walletNumber} le ${new Date().toLocaleDateString('fr-SN')});

doc.end();

const pdfBuffer = Buffer.concat(buffers);

// Upload to S3

const url = await uploadToSpaces(payslips/${item.id}.pdf, pdfBuffer);

// Update DB

await prisma.payrollItem.update({

where: { id: item.id },

data: { payslipPdfUrl: url },

});

// Send WhatsApp

await sendWhatsApp(employee.whatsapp, {

template: 'payslip_available',

params: [employee.firstName, payrollItem.payrollRun.month, url],

});

}

`

Cas réel — PME Dakar 47 employés

MétriqueAvantAprès Wave Business B2B
Temps préparation paie/mois12h1h
Erreurs montants8 %< 1 %
Délai salaires reçus3-5j (virements)< 5 min (Wave)
Coût processing/mois65K XOF (frais bancaires)22K XOF (frais Wave)
Plaintes employés/mois111

ROI : économie 11h DRH/mois + 43K FCFA/mois = ~600K FCFA/an équivalent.

Pièges fréquents

  • Employés sans Wave — solution : reverser sur OM via PayDunya OU virement bancaire pour bancarisés.
  • Données employés exposées — wallet numbers = sensibles. Encryption + audit logs obligatoires.
  • Pas de validation 4-yeux — un PayrollRun > 5M FCFA doit être validé par 2 personnes (RBAC).
  • Reconciliation comptable — besoin export comptable mensuel SYSCOHADA.
  • Conformité fiscale — déclarations IPRES/CSS/IR mensuelles obligatoires (DGI eDGI).

FAQ

Q : Wave Business disponible ailleurs qu'au SN/CI ?

R : Oui, en cours d'expansion vers ML, BF, OG. Chaque pays a un compte séparé.

Q : Concurrents ?

R : Orange Money Business (similaire), MTN MoMo Bulk Payments (Anglophone Africa). Wave reste le moins cher (1 % vs 2-3 %).

Q : Paie bancaire vs Wave ?

R : Bancaire = sûre, Wave = rapide. Pour < 50 employés, mix Wave (60 %) + bancaire (40 % cadres premium) idéal.

Conclusion

Wave Business B2B API est l'outil B2B le plus sous-exploité d'Afrique francophone en 2026. Économies opérationnelles considérables pour PME. Investissement intégration 1.5-4M FCFA. ROI 4-8 mois. Standard 2027 pour toute PME > 30 employés.

Tags :#Wave Business#B2B#Paie#Salaires#API#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.