Sommaire
- Ce que couvre l’API Orange Money Web Payment
- Prérequis et accès
- Étape 1 — Créer son compte développeur Orange
- Étape 2 — Obtenir le merchant_key auprès d’Orange Money
- Étape 3 — Récupérer un access token via OAuth client_credentials
- Étape 4 — Initier un paiement via /webpayment
- Étape 5 — Rediriger l’utilisateur vers la payment_url
- Étape 6 — Recevoir et vérifier la notification webhook
- Étape 7 — Interroger /transactionstatus pour la confirmation
- Étape 8 — Passage en production
- Erreurs fréquentes
- FAQ
- Ressources officielles
Ce que couvre l’API Orange Money Web Payment
L’API Orange Money Web Payment, exposée sur le portail developer.orange.com, permet à un site marchand d’encaisser des paiements Orange Money sans passer par un intégrateur tiers. Le client est redirigé vers une page hébergée par Orange où il valide le paiement avec un OTP envoyé sur sa SIM, puis revient sur le site marchand. Le service est disponible dans plusieurs pays, dont le Sénégal et la Côte d’Ivoire qui sont les deux marchés visés par ce tutoriel.
Trois appels HTTP suffisent à boucler le flux : un pour obtenir un token OAuth, un pour initier le paiement et récupérer une payment_url, un dernier pour vérifier le statut final. Une notification webhook (POST entrant côté serveur marchand) double le mécanisme et garantit qu’une transaction validée hors ligne — l’utilisateur ferme l’onglet — est bien comptabilisée. Ce tutoriel passe les six interactions une à une, avec les URL exactes, les paramètres requis, et un test mental de chaque réponse attendue.
Prérequis et accès
- Un compte développeur sur developer.orange.com (gratuit, validation par e-mail)
- Un contrat marchand Orange Money signé localement (au Sénégal via Orange Sénégal, en Côte d’Ivoire via Orange CI) qui produit le
merchant_key - Un domaine HTTPS publiquement accessible pour les
return_url,cancel_urletnotif_url— Orange refuse les URL HTTP non chiffrées en production - Un environnement serveur capable d’envoyer des requêtes HTTPS sortantes : Node.js 22 LTS, PHP 8.2+, Python 3.12, Java 21 ou Go 1.22+ font tous l’affaire
- Niveau attendu : développeur backend intermédiaire, à l’aise avec OAuth 2.0 et les webhooks
- Temps d’intégration estimé : 4 à 6 heures hors validation marchand
Étape 1 — Créer son compte développeur Orange
L’inscription au portail Orange Developer est le point de départ obligatoire. C’est là qu’on déclare une « application », c’est-à-dire l’identité qui consommera l’API et qui produit le couple client_id / client_secret nécessaire à l’OAuth.
On se rend sur developer.orange.com, on clique sur Sign up, on confirme l’e-mail. Une fois loggé, on accède à la section My apps et on crée une application en lui donnant un nom interne descriptif. Sur cette application, on souscrit au produit Orange Money WebPay. La page affiche alors deux chaînes : client_id (visible) et client_secret (masqué, à révéler une fois et stocker dans un secret manager).
# À ce stade, vous devriez avoir :
# CLIENT_ID=mYcLiEnTiD123
# CLIENT_SECRET=mYcLiEnTsEcReT456
# Ces deux valeurs sont les fondations du flux OAuth — ne JAMAIS les commiter
Si la souscription n’apparaît pas immédiatement après le clic sur Subscribe, c’est que le portail attend une validation interne. Sur les comptes nouvellement créés, le délai courant est de quelques minutes ; sur certains profils, Orange déclenche une revue manuelle qui peut prendre 1 à 2 jours ouvrés. Il faut donc anticiper cette friction si on a un planning serré.
Étape 2 — Obtenir le merchant_key auprès d’Orange Money
Le client_id et le client_secret permettent de parler à l’API, mais ils ne suffisent pas à encaisser de l’argent. Chaque paiement doit référencer un merchant_key qui est lié à un contrat marchand actif chez Orange Money dans le pays cible. C’est cette pièce qui authentifie le marchand auprès du système de paiement et qui détermine sur quel compte arriveront les fonds.
La procédure est strictement locale et hors API : on contacte le service Orange Money entreprises du pays — au Sénégal par orangemoney.entreprise.sn@orange-sonatel.com ou via une visite à l’agence Orange Money pro de Dakar, en Côte d’Ivoire par le service B2B d’Orange CI. On fournit son registre de commerce, son NINEA ou son IDU, ses statuts et un RIB. Après validation du dossier, Orange envoie un PDF avec le merchant_key et l’identifiant marchand.
Une chose souvent ignorée : un même client_id peut être associé à plusieurs merchant_key si le groupe a des entités juridiques distinctes au Sénégal et en Côte d’Ivoire. Dans ce cas, on stocke deux secrets et on choisit le bon selon la devise et le pays de l’acheteur.
Étape 3 — Récupérer un access token via OAuth client_credentials
Tous les appels métier exigent un access token obtenu via le flux OAuth 2.0 client_credentials. La durée de validité par défaut est de 3 600 secondes (une heure) selon la documentation officielle Orange Developer ; on le met en cache dans Redis ou en mémoire pour éviter de redemander un token à chaque transaction. Le portail limite à 50 requêtes de token par minute, ce qui rend le caching obligatoire dès qu’on traite plusieurs paiements simultanés.
curl -X POST https://api.orange.com/oauth/v3/token \
-H "Authorization: Basic $(printf '%s' "$CLIENT_ID:$CLIENT_SECRET" | base64 -w0)" \
-H "Accept: application/json" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials"
La réponse, en cas de succès, est un JSON avec quatre champs. token_type vaut toujours Bearer. access_token est la chaîne à passer dans l’en-tête Authorization des appels suivants. expires_in donne la durée de validité en secondes — typiquement 3600. scope contient la liste des API auxquelles le token donne accès. On stocke le token avec son TTL et on relance la procédure quand il expire. Le drapeau -w0 sur base64 évite l’insertion de retours à la ligne, fréquente cause d’erreur 401 quand on ne le précise pas — sur macOS BSD, utiliser base64 sans option, qui ne wrappe pas par défaut. Une erreur 401 à ce stade indique presque toujours un mauvais encodage Base64 du couple client_id:client_secret : vérifier qu’il n’y a pas d’espace ou de retour à la ligne dans la chaîne avant encodage.
Étape 4 — Initier un paiement via /webpayment
L’appel central de l’intégration est le POST sur /orange-money-webpay/dev/v1/webpayment. Il prend en entrée le merchant_key, le montant, la devise, les URL de retour et la référence interne, et retourne une payment_url à laquelle on redirige l’utilisateur.
curl -X POST https://api.orange.com/orange-money-webpay/dev/v1/webpayment \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"merchant_key": "votre_merchant_key",
"currency": "OUV",
"order_id": "CMD-2026-000123",
"amount": 5000,
"return_url": "https://votre-site.tld/orange-money/return",
"cancel_url": "https://votre-site.tld/orange-money/cancel",
"notif_url": "https://votre-site.tld/orange-money/notif",
"lang": "fr",
"reference": "Commande #123"
}'
Trois paramètres méritent attention. La currency vaut OUV en sandbox (devise de test commune à tous les pays) et XOF en production pour le Sénégal et la Côte d’Ivoire — les deux pays partagent le franc CFA UEMOA. order_id est une référence libre côté marchand, doit être unique pour pouvoir réconcilier ; on conseille un format CMD-YYYY-XXXXXX qui reste lisible dans les logs. amount est en unité monétaire entière, sans décimale : 5 000 signifie 5 000 FCFA, pas 50,00.
La réponse JSON, en cas de succès, contient quatre champs utiles. status vaut 201, message vaut « OK », pay_token est l’identifiant unique du paiement côté Orange, payment_url est l’URL hébergée par Orange vers laquelle on doit rediriger l’utilisateur. notif_token est un secret partagé qu’Orange renverra dans le webhook pour qu’on puisse vérifier l’authenticité de la notification — à stocker en base avec l’order_id.
Étape 5 — Rediriger l’utilisateur vers la payment_url
Une fois la payment_url obtenue, l’utilisateur doit y être envoyé pour valider le paiement. Côté frontend, c’est un simple window.location.href = payment_url. Côté backend, on retourne une réponse 302 ou 303 avec Location: payment_url.
Sur la page Orange, le client saisit son numéro Orange Money, reçoit un OTP par USSD, le saisit, et confirme. Si le paiement réussit, Orange redirige vers return_url avec des paramètres en query string. Si le client annule ou échoue, Orange redirige vers cancel_url. Ces deux URL doivent rendre une page intelligible côté marchand — afficher un récapitulatif, pas seulement « merci » : la fenêtre de redirection est l’un des points où l’utilisateur perd confiance s’il ne sait pas ce qui se passe.
La règle absolue est de ne jamais marquer la commande comme payée à partir de la simple réception d’un appel sur return_url. Cette URL est sous le contrôle du navigateur, donc falsifiable. La validation officielle vient soit de la notification webhook, soit de l’appel à /transactionstatus.
Étape 6 — Recevoir et vérifier la notification webhook
Orange envoie une requête POST sur la notif_url à chaque changement d’état du paiement. Le payload arrive en JSON et contient au minimum txnid (identifiant Orange de la transaction), order_id (ce qu’on avait envoyé en entrée), status et pay_token. Le champ status peut prendre cinq valeurs : INITIATED, PENDING, EXPIRED, SUCCESS, FAILED.
// Exemple Node.js avec Express 5
import express from 'express'
const app = express()
app.use(express.json())
app.post('/orange-money/notif', async (req, res) => {
const { txnid, order_id, status, pay_token } = req.body
const order = await db.orders.findOne({ id: order_id })
if (!order) return res.status(404).end()
// Vérification défensive : si Orange envoie notif_token, on le compare ;
// sinon on s'appuie sur la double-vérification via /transactionstatus
const claimedNotifToken = req.body.notif_token
if (claimedNotifToken && order.notif_token !== claimedNotifToken) {
return res.status(403).end()
}
if (status === 'SUCCESS') {
// Idempotence : si déjà marquée payée, on accuse réception sans re-traiter
if (order.paid) return res.status(200).end()
// Double-check : on ré-interroge /transactionstatus pour confirmer
const verified = await checkTransactionStatus(order.id, order.amount, pay_token)
if (verified.status !== 'SUCCESS') return res.status(200).end()
await db.orders.update({ id: order_id }, { paid: true, txnid })
// déclencher la suite (envoi e-mail, livraison numérique, etc.)
} else if (status === 'FAILED' || status === 'EXPIRED') {
await db.orders.update({ id: order_id }, { paid: false, status })
}
res.status(200).end() // toujours 200 OK pour qu'Orange ne ré-essaie pas
})
Trois précautions sont essentielles. Premièrement, ne jamais faire confiance au seul corps du webhook : la documentation publique d’Orange ne formalise pas un mécanisme de signature HMAC standard, donc on double-check systématiquement par un appel à /transactionstatus avant de marquer la commande payée. Le notif_token obtenu à l’étape 4 sert d’authentification optionnelle et certaines intégrations le voient remonté dans le webhook ; quand il est présent, on le compare à la valeur stockée. Deuxièmement, l’idempotence : le même statut peut être notifié deux fois en cas de timeout réseau côté Orange, donc le code doit vérifier que la commande n’est pas déjà marquée payée avant d’agir. Troisièmement, toujours répondre HTTP 200 rapidement (sous quelques secondes) ; au-delà, Orange considère l’appel raté et retentera, ce qui aggrave la duplication.
Étape 7 — Interroger /transactionstatus pour la confirmation
En complément du webhook, l’API expose un endpoint qui donne le statut courant d’une transaction sur demande. C’est le filet de sécurité quand le webhook tarde : si l’utilisateur revient sur return_url et qu’on n’a pas encore reçu de notification, on peut interroger ce endpoint pour décider quoi afficher.
curl -X POST https://api.orange.com/orange-money-webpay/dev/v1/transactionstatus \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"order_id": "CMD-2026-000123",
"amount": 5000,
"pay_token": "87a9f2f8ebca97...e4299fd"
}'
La réponse contient status (mêmes valeurs que dans le webhook), order_id et txnid (présent uniquement si la transaction a abouti, succès ou échec). En pratique, on appelle ce endpoint avec un retry exponentiel court — par exemple 1s, 2s, 4s — depuis la page return_url pour donner un feedback immédiat à l’utilisateur sans attendre le webhook qui peut tarder de plusieurs secondes.
Étape 8 — Passage en production
Une fois les tests passés en sandbox avec la devise OUV, la bascule en production demande deux actions. Côté Orange, on signale au support partenaire que les tests sont concluants, qui à son tour active le compte production. Côté code, on remplace la devise OUV par XOF et on bascule la base URL de l’API sur le chemin pays — Orange peut router via /orange-money-webpay/dev/v1/ en sandbox et /orange-money-webpay/sn/v1/ ou /orange-money-webpay/ci/v1/ en production selon le pays cible. Confirmer la valeur exacte avec le contact Orange parce que le segment de chemin évolue parfois.
L’autre changement majeur à ce stade est le passage à un merchant_key production différent du merchant_key sandbox. Ne pas tester en production avec le merchant_key sandbox : les tentatives sont rejetées, et on encombre les logs Orange ce qui complique le support en cas de problème. La bonne hygiène est d’avoir OM_MERCHANT_KEY_SANDBOX et OM_MERCHANT_KEY_PROD dans des secrets séparés, et de basculer via une variable d’environnement OM_ENV=prod qui choisit le bon couple URL/clé.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
| HTTP 401 sur l’OAuth | client_id:client_secret mal encodé en Base64 |
Utiliser echo -n (pas echo) ou la fonction Base64 stricte de votre langage |
HTTP 400 sur /webpayment avec « invalid currency » |
Devise XOF envoyée sur l’environnement sandbox |
Utiliser OUV en sandbox, XOF en production uniquement |
HTTP 403 sur /webpayment |
merchant_key non actif ou typo |
Vérifier le contrat marchand côté Orange Money entreprises ; copier-coller depuis le PDF reçu |
| Aucune notification webhook reçue | notif_url non publique ou en HTTP non chiffré |
Exposer un endpoint HTTPS valide ; tester avec curl depuis l’extérieur que l’URL est joignable |
| Notification reçue plusieurs fois pour la même transaction | Timeout côté serveur marchand > 5 s | Optimiser l’endpoint pour répondre en moins d’une seconde, traiter en arrière-plan via une queue |
Statut bloqué à PENDING |
L’utilisateur n’a pas saisi son OTP dans le délai imparti | Attendre l’expiration automatique (15 minutes typiquement), Orange enverra un statut EXPIRED |
Discordance de montant entre order_id et webhook |
Tentative de fraude ou bug interne | Comparer toujours le montant du webhook avec le montant attendu en base avant de marquer payée |
FAQ
Faut-il un compte Orange Money personnel pour intégrer l’API ?
Non, le compte développeur est distinct. Mais pour tester un paiement complet en sandbox, il faut un numéro Orange Money de test fourni par Orange à la demande. En production, n’importe quel client final ayant un Orange Money actif peut payer.
Y a-t-il des frais marchand ?
Oui, et ils dépendent du pays et du volume négocié. Au Sénégal et en Côte d’Ivoire, la commission marchand standard se situe entre 1,5 % et 2,5 % du montant transigé en 2026, avec des paliers dégressifs négociables au-delà de certains volumes mensuels. Confirmer auprès du commercial Orange Money de votre pays pour le devis exact.
L’API supporte-t-elle les remboursements ?
Pas directement via l’API Web Payment. Les remboursements sont gérés manuellement depuis le portail marchand Orange Money ou par contact direct avec le support marchand. Pour des cas d’usage où le remboursement doit être automatisé, Orange propose une API séparée Orange Money Cash-In/Cash-Out sur le même portail développeur, sous condition d’éligibilité.
Comment gérer les paiements à cheval entre Sénégal et Côte d’Ivoire ?
Chaque transaction est tied au pays du client (déterminé par le préfixe MSISDN). Si vous opérez dans les deux pays, deux contrats marchands distincts et deux merchant_key sont nécessaires. Le routing côté code se fait sur le numéro saisi par le client ou via une UI qui demande le pays avant la redirection.
Quel délai entre paiement client et crédit du compte marchand ?
Crédit en temps réel sur le portefeuille marchand Orange Money, mais le retrait vers un compte bancaire classique (settlement) suit un cycle T+1 à T+3 selon les conditions du contrat. Anticiper cette latence dans les flux de trésorerie.
L’API est-elle compatible avec les apps mobiles natives iOS et Android ?
Oui, le flux est strictement côté serveur — le frontend mobile ouvre la payment_url dans une WebView ou un onglet Safari/Chrome, l’utilisateur paie, l’app se réveille via deep link sur le return_url ou cancel_url configurés en Universal Link côté iOS et App Link côté Android.
Comment faire en cas d’absence prolongée de notification webhook ?
Mettre en place une tâche planifiée (cron toutes les 5 minutes) qui interroge /transactionstatus sur toutes les commandes en statut INITIATED ou PENDING depuis plus de 30 minutes. Cette boucle de réconciliation rattrape les notifications perdues.