Ce que vous saurez faire
Ce tutoriel est destiné aux développeurs et intégrateurs techniques qui souhaitent connecter une boutique e-commerce à une API Mobile Money en zone UEMOA. À la fin, vous serez capable d'intégrer PayDunya ou CinetPay sur un site WooCommerce ou une application sur-mesure (Laravel, Node.js), de gérer le cycle complet d'une transaction (initiation, redirection, webhook, vérification, réconciliation), de sécuriser les échanges et de tracer les erreurs en production. Le code est donné à titre indicatif et adaptable.
Étapes détaillées
Étape 1 : Préparer l'environnement de développement
Installez localement :
- PHP 8.1+ ou Node.js 18+
- Composer ou npm
- ngrok ou expose pour tunneliser les webhooks vers localhost
- Postman ou Insomnia pour tester les requêtes API
Créez un compte chez PayDunya (paydunya.com) et activez l'environnement Sandbox. Récupérez les 4 clés : master_key, private_key, public_key, token.
Étape 2 : Comprendre l'architecture d'un paiement Mobile Money
Le flux standard comprend 6 étapes :
- 1. Le client clique "Payer" sur votre site
- 2. Votre serveur appelle l'API agrégateur pour créer une facture
- 3. L'agrégateur retourne une URL de paiement
- 4. Vous redirigez le client vers cette URL
- 5. Le client choisit son opérateur (Wave, Orange Money) et confirme sur son téléphone
- 6. L'agrégateur notifie votre serveur via webhook IPN du résultat
Important : ne marquez jamais une commande payée sur la base de la redirection navigateur, uniquement sur le webhook signé.
Étape 3 : Stocker les clés API en sécurité
Créez un fichier .env à la racine du projet (et ajoutez-le à .gitignore) :
PAYDUNYA_MODE=test
PAYDUNYA_MASTER_KEY=4fpMv7T8-Vatz-bynh-...
PAYDUNYA_PRIVATE_KEY=test_private_...
PAYDUNYA_PUBLIC_KEY=test_public_...
PAYDUNYA_TOKEN=tNyaYOpb...
APP_BASE_URL=https://maboutique.sn
Ne committez jamais ce fichier. En production, utilisez les variables d'environnement du serveur (Apache SetEnv, Nginx fastcgi_param, ou .env chiffré).
Étape 4 : Installer le SDK officiel
Pour PHP :
composer require paydunya/paydunya-php
Pour Node.js, utilisez axios et signez vous-même les requêtes :
npm install axios dotenv
Pour WooCommerce, le plugin officiel PayDunya est disponible sur le marketplace, ce qui évite tout code mais limite la personnalisation.
Étape 5 : Initialiser le SDK dans votre application
En PHP :
require 'vendor/autoload.php';
\Paydunya\Setup::setMasterKey(getenv('PAYDUNYA_MASTER_KEY'));
\Paydunya\Setup::setPublicKey(getenv('PAYDUNYA_PUBLIC_KEY'));
\Paydunya\Setup::setPrivateKey(getenv('PAYDUNYA_PRIVATE_KEY'));
\Paydunya\Setup::setToken(getenv('PAYDUNYA_TOKEN'));
\Paydunya\Setup::setMode(getenv('PAYDUNYA_MODE'));
\Paydunya\Checkout\Store::setName('Ma Boutique SN');
\Paydunya\Checkout\Store::setTagline('Vente en ligne au Sénégal');
\Paydunya\Checkout\Store::setPhoneNumber('+221770000000');
\Paydunya\Checkout\Store::setPostalAddress('Dakar, Plateau');
\Paydunya\Checkout\Store::setWebsiteUrl('https://maboutique.sn');
\Paydunya\Checkout\Store::setLogoUrl('https://maboutique.sn/logo.png');
Étape 6 : Créer une facture (invoice)
Lorsqu'un client valide son panier, votre back-end crée la facture :
$invoice = new \Paydunya\Checkout\CheckoutInvoice();
$invoice->addItem('Pagne wax 6 yards', 1, 12000, 12000, 'Motif fleuri');
$invoice->addItem('Frais de livraison Dakar', 1, 2000, 2000);
$invoice->setTotalAmount(14000);
$invoice->setDescription('Commande #1042');
$invoice->addCustomData('order_id', '1042');
$invoice->addCustomData('customer_phone', '+221770000000');
$invoice->setCallbackUrl('https://maboutique.sn/api/paydunya/ipn');
$invoice->setReturnUrl('https://maboutique.sn/merci');
$invoice->setCancelUrl('https://maboutique.sn/annulation');
if ($invoice->create()) {
$url = $invoice->getInvoiceUrl();
$token = $invoice->getToken();
// Stocker $token en base lié à la commande
header('Location: ' . $url);
exit;
} else {
error_log('PayDunya error: ' . $invoice->response_text);
http_response_code(500);
echo 'Erreur de paiement, veuillez réessayer.';
}
Le montant doit être un entier en FCFA (pas de centimes).
Étape 7 : Implémenter le webhook IPN
Créez l'endpoint qui recevra la notification serveur-à-serveur. En PHP :
// fichier ipn.php
$rawBody = file_get_contents('php://input');
$payload = json_decode($rawBody, true);
if (!isset($payload['data'])) {
http_response_code(400);
exit;
}
$data = $payload['data'];
$status = $data['status'] ?? null;
$token = $data['invoice']['token'] ?? null;
$totalAmount = $data['invoice']['total_amount'] ?? 0;
$customData = $data['custom_data'] ?? [];
$orderId = $customData['order_id'] ?? null;
if (!$token || !$orderId) {
http_response_code(400);
exit;
}
// Vérification serveur : on rappelle PayDunya pour confirmer
$confirm = new \Paydunya\Checkout\CheckoutInvoice();
if (!$confirm->confirm($token)) {
http_response_code(400);
exit;
}
if ($confirm->getStatus() === 'completed') {
// Marquer la commande comme payée en base
$db->update('orders', ['status' => 'paid', 'paid_at' => date('c')], ['id' => $orderId]);
// Envoyer email/SMS de confirmation au client
sendOrderConfirmation($orderId);
}
http_response_code(200);
echo 'OK';
La double vérification (webhook + appel API) protège contre les webhooks falsifiés.
Étape 8 : Tester avec ngrok en local
Lancez votre serveur local sur le port 8000 puis :
ngrok http 8000
Vous obtenez une URL publique HTTPS du type https://abcd-1234.ngrok-free.app. Renseignez-la comme URL de callback dans PayDunya pour vos tests. Effectuez plusieurs paiements avec les numéros de simulation et observez les requêtes IPN dans la console ngrok pour valider la réception et le contenu.
Étape 9 : Gérer les statuts de transaction
Les statuts possibles retournés par l'API :
- pending : facture créée, paiement non encore initié
- completed : paiement réussi et confirmé par l'opérateur
- cancelled : client a annulé sur la page de paiement
- failed : opérateur a refusé (solde insuffisant, OTP incorrect)
Stockez chaque changement de statut avec horodatage dans une table d'audit. Cela facilite les réconciliations comptables.
Étape 10 : Implémenter l'idempotence
Un webhook peut être appelé plusieurs fois (rejeu réseau, retry agrégateur). Votre handler doit être idempotent :
// Vérifier si déjà traité
$existing = $db->fetchOne('SELECT id FROM payment_events WHERE token = ? AND status = ?', [$token, $status]);
if ($existing) {
http_response_code(200);
echo 'Already processed';
exit;
}
// Insérer l'événement avant traitement
$db->insert('payment_events', [
'token' => $token,
'status' => $status,
'received_at' => date('c'),
]);
Sans cette protection, vous risquez de doubler la livraison ou la facturation.
Étape 11 : Logger et tracer les transactions
Créez une table payment_logs avec : id, order_id, provider, request_payload, response_payload, http_status, created_at. Loguer chaque appel sortant et chaque webhook entrant. En production, utilisez Monolog ou Winston pour pousser vers un agrégateur (Loki, Datadog, ou simplement fichier rotaté). Conservez 12 mois minimum pour la conformité BCEAO.
Étape 12 : Mettre en place les tentatives et timeouts
Les appels API peuvent échouer (réseau, timeout opérateur). Configurez :
- Timeout HTTP : 30 secondes maximum
- Retry automatique : 3 tentatives avec backoff exponentiel (1 s, 3 s, 9 s)
- Cercle de coupure (circuit breaker) : si l'API est indisponible 5 fois en 1 minute, désactivez temporairement et basculez sur un agrégateur de secours
Affichez un message clair au client en cas d'indisponibilité avec une alternative (paiement à la livraison).
Étape 13 : Implémenter les remboursements
Certains agrégateurs offrent un endpoint de remboursement. Pour PayDunya, le remboursement se fait via le dashboard ou l'API REST :
POST https://app.paydunya.com/api/v1/refund
Headers:
PAYDUNYA-MASTER-KEY: ...
PAYDUNYA-PRIVATE-KEY: ...
PAYDUNYA-TOKEN: ...
Body JSON:
{
"invoice_token": "abcd1234",
"amount": 14000,
"reason": "Produit défectueux"
}
Conservez la trace du remboursement dans la table d'audit avec lien vers la commande originale.
Étape 14 : Réconcilier avec votre comptabilité
Mettez en place une tâche cron quotidienne qui :
- Récupère la liste des transactions du jour précédent depuis l'API agrégateur
- Compare avec les commandes marquées payées dans votre base
- Liste les écarts (commandes payées non versées, virements sans commande associée)
- Envoie un rapport email au gestionnaire chaque matin
Cette discipline évite les pertes financières et facilite l'audit annuel.
Erreurs fréquentes à éviter
- Confondre montant client et montant net reçu : intégrez la commission agrégateur dans votre comptabilité.
- Ne pas vérifier l'origine du webhook : un attaquant peut forger une fausse notification de paiement. Toujours rappeler l'API pour confirmer.
- Webhook non idempotent : double facturation et clients mécontents.
- Hardcoder les URL de callback : utilisez des variables d'environnement pour pouvoir basculer dev/staging/prod.
- Pas de logs en production : impossible de diagnostiquer les transactions perdues.
- Timeouts trop courts : les opérateurs Mobile Money peuvent prendre 20 à 30 secondes pour confirmer.
- Oublier les retries côté agrégateur : un seul webhook peut être livré 3 à 10 fois en cas d'échec HTTP >= 500.
Checklist d'intégration technique
- [ ] Compte sandbox PayDunya/CinetPay créé avec clés récupérées
- [ ] Variables d'environnement configurées en dev, staging, prod
- [ ] SDK installé via Composer ou npm
- [ ] Création de facture testée avec retour URL valide
- [ ] Endpoint IPN implémenté en HTTPS uniquement
- [ ] Double vérification webhook + appel API confirm
- [ ] Idempotence du webhook garantie par table de tracking
- [ ] Tous les statuts (pending, completed, cancelled, failed) gérés
- [ ] Logs structurés avec rotation et conservation 12 mois
- [ ] Timeouts HTTP fixés à 30 s avec 3 retries en backoff
- [ ] Circuit breaker prévu en cas de panne agrégateur
- [ ] Remboursement implémenté ou procédure manuelle documentée
- [ ] Job cron quotidien de réconciliation comptable
- [ ] Tests sandbox automatisés avec PHPUnit ou Jest
- [ ] Bascule production validée par 1 transaction réelle de 100 FCFA
- [ ] Documentation interne pour les développeurs (README technique)
Cette intégration prend entre 2 et 5 jours-homme pour un développeur expérimenté, et 5 à 10 jours pour un junior accompagné. Une fois en production, prévoyez 0,5 jour/mois de maintenance pour suivre les évolutions API, mettre à jour le SDK et analyser les logs d'erreurs. La fiabilité du paiement étant critique pour la confiance client, n'ouvrez la production qu'après validation complète de la checklist.