Guide complet : Intégrer Moov Money via API
Moov Africa (ex-Atlantique Telecom) est un acteur majeur du Mobile Money en Afrique de l'Ouest avec une présence forte au Bénin, Togo, Côte d'Ivoire et Niger. Ce guide vous accompagne dans l'intégration de Moov Money via l'API Simiz.
Présentation de Moov Money
Pays couverts
| Pays | Code ISO | Devise | Préfixes téléphoniques |
|---|
| Bénin | BJ | XOF | +229 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 |
|---|---|---|---|
| Togo | TG | XOF | +228 90, 91, 92, 93, 96, 97, 98, 99 |
| Côte d'Ivoire | CI | XOF | +225 01, 02, 03, 04, 05, 06, 07 |
| Niger | NE | XOF | +227 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 |
Chiffres clés Moov Money
- 10 millions d'utilisateurs actifs
- 500 000 points de vente
- 2.5 milliards de dollars de transactions annuelles
- 35% de parts de marché en zone UEMOA
Pourquoi intégrer Moov Money ?
Avantages
- Couverture rurale - Moov a un excellent réseau en zones rurales
- Frais compétitifs - Commission généralement inférieure à Orange Money
- Clients exclusifs - Certains clients n'ont que Moov comme opérateur
- API stable - Infrastructure fiable et éprouvée
Défis
- Documentation limitée - Peu de ressources en ligne
- Support réactif - Parfois lent pour les développeurs tiers
- Validation stricte - Processus KYB rigoureux
Architecture de l'intégration
Flux de paiement
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │────>│ Votre App │────>│ Simiz API │────>│ Moov Money │
│ (Moov) │ │ │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│ │ │
│<─────────────────────────────────────────│<───────────────────│
│ Notification USSD/Push │ Confirmation │
│ │ │
│<─────────────────────────────────────────│<───────────────────│
│ Webhook de statut │ Callback │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Étape 1 : Configuration du compte
Création du compte Simiz
- Rendez-vous sur simiz.io/register
- Complétez votre inscription
- Vérifiez votre email
- Soumettez vos documents KYB :
- Statuts de la société
- Pièce d'identité du dirigeant
- Justificatif de domicile
Configuration Moov Money
Dans votre dashboard Simiz :
- Activez Moov Money pour votre projet
- Sélectionnez les pays où vous opérez
- Configurez vos webhooks de notification
Étape 2 : Installation du SDK
Node.js / JavaScript
bun add @simiz/sdk
PHP
composer require simiz/simiz-php
Python
pip install simiz
Étape 3 : Initier un paiement Moov Money
Exemple JavaScript
import { Simiz } from '@simiz/sdk';
// Initialisation
const simiz = new Simiz({
apiKey: process.env.SIMIZ_API_KEY,
secretKey: process.env.SIMIZ_SECRET_KEY,
sandbox: true // false en production
});
// Créer un paiement Moov Money
async function createMoovPayment(phoneNumber, amount, reference) {
try {
const payment = await simiz.payments.create({
amount: amount, // Montant en FCFA
currency: 'XOF', // Devise locale
phone: phoneNumber, // Numéro Moov Money
provider: 'moov_money', // Moov Money
country: 'BJ', // Code pays (BJ, TG, CI, NE)
description: 'Achat sur MonSite.com',
reference: reference,
callbackUrl: 'https://monsite.com/webhooks/simiz',
returnUrl: 'https://monsite.com/paiement/merci',
cancelUrl: 'https://monsite.com/paiement/annule',
metadata: {
orderId: reference,
customerId: 'CUST-001',
items: JSON.stringify([{ id: 'PROD-001', qty: 1 }])
}
});
console.log('Paiement créé:', payment.id);
console.log('Statut:', payment.status); // 'pending'
return payment;
} catch (error) {
console.error('Erreur:', error.message);
throw error;
}
}
// Utilisation
createMoovPayment('+22997001234', 10000, 'CMD-2026-001');
Exemple PHP
<?php
require 'vendor/autoload.php';
use Simiz\SimizClient;
$simiz = new SimizClient([
'api_key' => getenv('SIMIZ_API_KEY'),
'secret_key' => getenv('SIMIZ_SECRET_KEY'),
'sandbox' => true
]);
$payment = $simiz->payments->create([
'amount' => 10000,
'currency' => 'XOF',
'phone' => '+22997001234',
'provider' => 'moov_money',
'country' => 'BJ',
'description' => 'Achat sur MonSite.com',
'reference' => 'CMD-2026-001',
'callback_url' => 'https://monsite.com/webhooks/simiz'
]);
echo "Paiement ID: " . $payment->id . "\n";
echo "Statut: " . $payment->status . "\n";
Étape 4 : Gérer les webhooks
Configuration du endpoint
// Express.js
import express from 'express';
import { Simiz } from '@simiz/sdk';
const app = express();
app.use(express.json());
const simiz = new Simiz({
apiKey: process.env.SIMIZ_API_KEY,
secretKey: process.env.SIMIZ_SECRET_KEY
});
app.post('/webhooks/simiz', async (req, res) => {
try {
// Vérifier la signature
const signature = req.headers['x-simiz-signature'];
const isValid = simiz.webhooks.verify(req.body, signature);
if (!isValid) {
console.error('Signature webhook invalide');
return res.status(401).json({ error: 'Invalid signature' });
}
const event = req.body;
// Traiter l'événement
switch (event.type) {
case 'payment.success':
await handlePaymentSuccess(event.data);
break;
case 'payment.failed':
await handlePaymentFailed(event.data);
break;
case 'payment.expired':
await handlePaymentExpired(event.data);
break;
case 'payment.cancelled':
await handlePaymentCancelled(event.data);
break;
default:
console.log('Événement inconnu:', event.type);
}
// Répondre rapidement (avant 30 secondes)
res.json({ received: true });
} catch (error) {
console.error('Erreur webhook:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
app.listen(3000, () => {
console.log('Serveur webhook démarré sur le port 3000');
});
// Handlers d'événements
async function handlePaymentSuccess(data) {
const { reference, amount, currency } = data;
// Mettre à jour la commande
await updateOrderStatus(reference, 'PAID');
// Envoyer confirmation client
await sendPaymentConfirmation(reference);
// Libérer le produit/service
await fulfillOrder(reference);
console.log(Paiement réussi: ${reference} - ${amount} ${currency});
}
async function handlePaymentFailed(data) {
const { reference, errorCode, errorMessage } = data;
await updateOrderStatus(reference, 'FAILED');
await notifyPaymentFailed(reference, errorMessage);
console.log(Paiement échoué: ${reference} - ${errorCode});
}
Types d'événements Moov Money
| Événement | Description | Action recommandée |
|---|
payment.success | Paiement confirmé | Livrer le produit/service |
|---|---|---|
payment.failed | Paiement échoué | Notifier le client, proposer réessayer |
payment.expired | Délai dépassé | Renvoyer lien de paiement |
payment.cancelled | Annulé par le client | Archiver la commande |
Étape 5 : Codes d'erreur Moov Money
Codes d'erreur courants
| Code | Description | Solution |
|---|
insufficient_balance | Solde Moov Money insuffisant | Informer le client de recharger |
|---|---|---|
invalid_phone | Numéro non Moov Money | Vérifier le préfixe téléphonique |
transaction_limit | Plafond journalier atteint | Reporter le paiement au lendemain |
service_unavailable | Moov Money temporairement indisponible | Réessayer plus tard |
pin_error | Code PIN incorrect | Contact support Moov |
account_inactive | Compte Moov Money inactif | Client doit réactiver son compte |
timeout | Timeout opérateur | Réessayer la transaction |
duplicate_transaction | Transaction en double | Vérifier le statut de la commande |
Gestion des erreurs
try {
const payment = await simiz.payments.create({
amount: 10000,
phone: '+22997001234',
provider: 'moov_money',
country: 'BJ',
// ... autres paramètres
});
} catch (error) {
switch (error.code) {
case 'insufficient_balance':
return res.status(400).json({
error: 'Solde insuffisant',
message: 'Veuillez recharger votre compte Moov Money'
});
case 'invalid_phone':
return res.status(400).json({
error: 'Numéro invalide',
message: 'Ce numéro n\'est pas un compte Moov Money'
});
case 'transaction_limit':
return res.status(400).json({
error: 'Limite atteinte',
message: 'Limite journalière dépassée. Réessayez demain.'
});
case 'service_unavailable':
return res.status(503).json({
error: 'Service indisponible',
message: 'Moov Money est temporairement indisponible. Veuillez réessayer.'
});
default:
return res.status(500).json({
error: 'Erreur inconnue',
message: error.message
});
}
}
Étape 6 : Validation du numéro
Vérifier si un numéro est Moov Money
function isMoovNumber(phone, country) {
const cleanPhone = phone.replace(/[^0-9]/g, '');
const moovPrefixes = {
'BJ': ['22990', '22991', '22992', '22993', '22994', '22995', '22996', '22997', '22998', '22999'],
'TG': ['22890', '22891', '22892', '22893', '22896', '22897', '22898', '22899'],
'CI': ['22501', '22502', '22503', '22504', '22505', '22506', '22507'],
'NE': ['22790', '22791', '22792', '22793', '22794', '22795', '22796', '22797', '22798', '22799']
};
const prefixes = moovPrefixes[country];
if (!prefixes) return false;
return prefixes.some(prefix => cleanPhone.startsWith(prefix));
}
// Utilisation
console.log(isMoovNumber('+22997001234', 'BJ')); // true
console.log(isMoovNumber('+22507001234', 'CI')); // true
console.log(isMoovNumber('+23769001234', 'CM')); // false (Cameroun)
Étape 7 : Tests en Sandbox
Numéros de test
| Numéro | Pays | Comportement |
|---|
| +229990000001 | Bénin | Paiement réussi |
|---|---|---|
| +229990000002 | Bénin | Solde insuffisant |
| +229990000003 | Bénin | Timeout |
| +229990000004 | Bénin | Refusé par le client |
| +228990000001 | Togo | Paiement réussi |
| +228990000002 | Togo | Solde insuffisant |
| +225010000001 | Côte d'Ivoire | Paiement réussi |
| +225010000002 | Côte d'Ivoire | Solde insuffisant |
Scénarios de test
// Test paiement réussi
await testPayment('+229990000001', 10000, 'BJ'); // Success
// Test solde insuffisant
await testPayment('+229990000002', 100000, 'BJ'); // Error: insufficient_balance
// Test timeout
await testPayment('+229990000003', 5000, 'BJ'); // Error: timeout
// Test annulation
await testPayment('+229990000004', 5000, 'BJ'); // Error: cancelled
Étape 8 : Passage en production
Checklist avant le lancement
- [ ] Tous les tests Sandbox validés
- [ ] Webhooks configurés et testés
- [ ] Gestion des erreurs implémentée
- [ ] Emails de confirmation configurés
- [ ] Logs de transactions activés
- [ ] Clés Production générées
- [ ] Variable
sandbox: false - [ ] Système de monitoring mis en place
- [ ] Équipe support formée
Configuration production
const simiz = new Simiz({
apiKey: process.env.SIMIZ_API_KEY_PROD, // smz_live_pk_xxx
secretKey: process.env.SIMIZ_SECRET_KEY_PROD, // smz_live_sk_xxx
sandbox: false // Important : false en production
});
Bonnes pratiques
1. Idempotence
Évitez les doubles paiements :
const payment = await simiz.payments.create({
amount: 10000,
phone: '+22997001234',
provider: 'moov_money',
country: 'BJ',
reference: 'CMD-2026-001'
}, {
idempotencyKey: order-${orderId}-v1
});
2. Timeout et retry
Les paiements Moov Money expirent après 15 minutes :
async function waitForPayment(paymentId, maxAttempts = 10) {
for (let i = 0; i < maxAttempts; i++) {
const payment = await simiz.payments.retrieve(paymentId);
if (['success', 'failed', 'expired', 'cancelled'].includes(payment.status)) {
return payment;
}
await new Promise(resolve => setTimeout(resolve, 30000)); // 30 secondes
}
throw new Error('Timeout en attente du paiement');
}
3. Sécurité
- Stockez vos clés API dans des variables d'environnement
- Validez toujours les signatures des webhooks
- Utilisez HTTPS pour tous les endpoints
- Ne loggez jamais les numéros de téléphone complets
- Implémentez une politique de rétention des données
4. Surveillance
// Metriques à surveiller
const metrics = {
paymentsInitiated: 0,
paymentsSuccess: 0,
paymentsFailed: 0,
averageResponseTime: 0,
errorsByCode: {}
};
// Alerte si taux d'échec > 5%
if (metrics.paymentsFailed / metrics.paymentsInitiated > 0.05) {
alertTeam('Taux d\'échec élevé sur Moov Money');
}
Conclusion
L'intégration de Moov Money via Simiz est simple et rapide. En suivant ce guide, vous pouvez accepter les paiements Moov Money au Bénin, Togo, Côte d'Ivoire et Niger en quelques heures.
Les avantages clés de passer par Simiz :- Une seule API pour Moov Money + Orange Money + MTN MoMo
- Documentation unifiée en français
- Support technique dédié
- Tests en sandbox gratuits
Besoin d'aide ? Contactez notre équipe technique à developer@simiz.io