Cybersécurité

Orange Money Web Payment en production : OAuth2 et certification pas-à-pas

11 دقائق للقراءة

Passer Orange Money en production via l’API Web Payment d’Orange Developer reste le canal payment-redirect le plus utilisé par les sites e-commerce. Elle ouvre une session de paiement hébergée, redirige l’utilisateur vers une page Orange où celui-ci valide la transaction via OTP USSD, puis renvoie le contrôle à votre application avec une notification asynchrone. La mise en production de ce flux exige davantage de patience administrative que technique — quatre à huit semaines en moyenne — mais la suite technique est précise et mérite une checklist exhaustive.

Ce guide reprend la migration de bout en bout : préparation du dossier merchant, demande d’accès production, génération des credentials, adaptation du code OAuth 2.0, premier paiement live et observabilité. Les exemples sont en Python avec requests mais sont transposables à n’importe quelle stack server-side classique.

Prérequis pour passer Orange Money en production

  • Un compte Orange Developer actif avec accès au portail developer.orange.com
  • Une intégration sandbox fonctionnelle sur l’API OM Web Payment
  • Un dossier merchant complet (immatriculation, RIB, identification du dirigeant)
  • Un site marchand HTTPS valide avec mentions légales, CGV et politique de confidentialité
  • Un endpoint webhook HTTPS stable et un endpoint de retour utilisateur

Étape 1 — Comprendre l’écosystème Orange Developer

Orange Developer fédère trois produits « payment » complémentaires. Orange Money Web Payment est le flux redirect web pour les sites e-commerce. M Payment cible l’intégration dans une application mobile native. eMoney Wallet Manager permet aux marchands de consulter et de manipuler leur portefeuille. Pour la grande majorité des projets web, OM Web Payment suffit.

L’authentification est uniformément OAuth 2.0 en mode client credentials. Vous identifiez votre application avec un client_id et un client_secret émis par Orange. Vous échangez ces identifiants contre un access token de courte durée. Vous présentez ce token comme Bearer dans chaque appel ultérieur.

Le sandbox et la production partagent la même mécanique mais des endpoints distincts dans certains pays. La doc docs.developer.orange.com fournit la matrice exacte pour chaque pays partenaire. Vérifiez l’URL exacte avant de coder en dur.

Étape 2 — Préparer le dossier merchant

L’accès production d’Orange Money est sous contrôle de l’entité Orange locale et, dans plusieurs pays, sous régulation de la banque centrale. Le dossier merchant typique exige : registre de commerce ou équivalent, NINEA ou identifiant fiscal, statuts à jour, pièce d’identité du dirigeant, RIB du compte bancaire de réception, attestation d’hébergement du site marchand, modèle d’opération (description du produit ou service vendu).

Le formulaire merchant demande aussi votre URL de notification webhook et votre URL de retour utilisateur. Préparez-les en amont avec des chemins explicites (par exemple https://api.exemple.com/payments/orange/webhook et https://app.exemple.com/checkout/orange/return). Le changement de ces URLs après validation impose souvent un re-passage en validation, donc bétonnez-les avant soumission.

Comptez deux semaines minimum d’instruction côté Orange. Dans certains pays, une étape supplémentaire de validation par la banque centrale ou par un correspondant compliance externe ajoute encore deux à quatre semaines. Anticipez ce délai dans votre planning produit.

Étape 3 — Récupérer les credentials production

Une fois le dossier validé, le portail Orange Developer affiche une nouvelle application « Live » dans la section My Applications. Cette application contient deux paires de credentials : sandbox et production. La paire production n’apparaît qu’après activation explicite par l’équipe Orange.

Les credentials production se composent d’un client_id et d’un client_secret à concaténer en Basic Auth pour l’appel d’obtention du token. Stockez-les immédiatement dans votre coffre à secrets — le client_secret n’est plus affiché en clair après la première vue. Si vous le perdez, demandez une régénération via le support Orange Developer.

vault kv put secret/orange/prod \
  client_id="..." \
  client_secret="..." \
  merchant_key="..."

Le merchant_key est un identifiant spécifique à votre compte marchand Orange Money, distinct du couple OAuth. Il intervient dans la création de session de paiement et identifie le compte sur lequel les fonds seront crédités.

Étape 4 — Implémenter l’obtention du token avec cache

Le token OAuth retourné par POST /oauth/v3/token a une durée de vie limitée — typiquement 3600 secondes selon la doc Orange Developer. Refaire l’appel à chaque transaction est inutile et augmente la latence perçue. Un cache en mémoire avec refresh anticipé est la bonne pratique.

import os
import base64
import time
import threading
import requests

OAUTH_URL = os.environ["ORANGE_OAUTH_URL"]
CLIENT_ID = os.environ["ORANGE_CLIENT_ID"]
CLIENT_SECRET = os.environ["ORANGE_CLIENT_SECRET"]

_token_cache = {"value": None, "expires_at": 0}
_lock = threading.Lock()

def get_access_token():
    now = time.time()
    if _token_cache["value"] and _token_cache["expires_at"] - 60 > now:
        return _token_cache["value"]
    with _lock:
        if _token_cache["value"] and _token_cache["expires_at"] - 60 > now:
            return _token_cache["value"]
        basic = base64.b64encode(
            f"{CLIENT_ID}:{CLIENT_SECRET}".encode()
        ).decode()
        r = requests.post(
            OAUTH_URL,
            headers={
                "Authorization": f"Basic {basic}",
                "Content-Type": "application/x-www-form-urlencoded",
                "Accept": "application/json",
            },
            data={"grant_type": "client_credentials"},
            timeout=10,
        )
        r.raise_for_status()
        data = r.json()
        _token_cache["value"] = data["access_token"]
        _token_cache["expires_at"] = now + int(data.get("expires_in", 3600))
        return _token_cache["value"]

Le refresh anticipé d’une minute (expires_at - 60) évite qu’une transaction tombe pile au moment où le token expire. Le verrou (threading.Lock) protège contre la course concurrente où plusieurs threads détecteraient simultanément l’expiration. Dans un environnement multi-process (Gunicorn, uWSGI), remplacez le cache local par un cache partagé Redis avec un verrou distribué.

Étape 5 — Initier une session de paiement

L’appel d’initiation Web Payment construit une session côté Orange et retourne une URL de redirection. Le payload typique inclut amount en unité monétaire entière, currency, order_id (votre référence interne), return_url, cancel_url, notif_url et le merchant_key.

def init_webpayment(order_id, amount, return_url, cancel_url, notif_url):
    token = get_access_token()
    r = requests.post(
        os.environ["ORANGE_WEBPAY_URL"],
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
            "Accept": "application/json",
        },
        json={
            "merchant_key": os.environ["ORANGE_MERCHANT_KEY"],
            "currency": os.environ["ORANGE_CURRENCY"],
            "order_id": order_id,
            "amount": amount,
            "return_url": return_url,
            "cancel_url": cancel_url,
            "notif_url": notif_url,
            "lang": "fr",
            "reference": order_id,
        },
        timeout=15,
    )
    r.raise_for_status()
    data = r.json()
    return data["payment_url"], data["pay_token"], data["notif_token"]

Trois valeurs sont à conserver côté serveur : le pay_token identifie la session côté Orange, le notif_token sera renvoyé dans la notification webhook (votre point de jonction), et le payment_url est l’URL vers laquelle vous redirigez le navigateur du client. Le currency dépend du pays et de l’environnement : OUV est la valeur sandbox commune à tous les pays Orange Developer ; en production, vous utilisez la devise réelle du pays (XOF pour la zone UEMOA — Sénégal, Côte d’Ivoire, Mali, Burkina Faso ; XAF pour le Cameroun ; etc.). Vérifiez la table devises dans la doc du pays cible avant le go-live.

Étape 6 — Traiter la notification asynchrone

Après que le client a validé son paiement via OTP côté Orange, votre notif_url reçoit une requête POST contenant le statut de la transaction et le notif_token. Les valeurs de status documentées dans les implémentations communautaires sont typiquement SUCCESS et FAILED ; la matrice exacte peut varier selon le pays (consultez votre interlocuteur Orange Developer du pays cible avant de bétonner votre machine à états). Votre serveur vérifie la cohérence avec votre base interne, applique l’effet métier, puis répond 200.

from fastapi import FastAPI, Request, HTTPException
import json

app = FastAPI()

@app.post("/webhooks/orange")
async def orange_webhook(request: Request):
    body = await request.body()
    payload = json.loads(body)
    notif_token = payload.get("notif_token")
    status = payload.get("status")
    order_id = payload.get("reference") or payload.get("order_id")
    # Vérification croisée avec la transaction stockée
    tx = await get_transaction_by_order_id(order_id)
    if not tx or tx["orange_notif_token"] != notif_token:
        raise HTTPException(400, "Token mismatch")
    if tx["status"] != "PENDING":
        # Idempotence : déjà traité
        return {"ok": True}
    if status == "SUCCESS":
        await apply_payment_success(tx)
    elif status == "FAILED":
        await apply_payment_failure(tx)
    return {"ok": True}

La vérification du notif_token contre la valeur stockée est la garde principale : aucun appel webhook usurpé ne peut deviner le bon token. L’idempotence repose sur l’état initial PENDING et un branchement explicite si la transaction est déjà finalisée.

Pour un niveau de sécurité supplémentaire, certains setups Orange exigent une signature des notifications. Si votre application est configurée pour le signing, vérifiez la signature avant tout traitement, en suivant la spec exacte du pays (l’algorithme et la chaîne signée varient).

Étape 7 — Tester le premier paiement live

Avant d’ouvrir au grand public, exécutez un cycle complet sur un téléphone interne avec un solde Orange Money minimal. Côté backend, déclenchez une initiation, vérifiez la redirection, payez via OTP, et observez la réception du callback. Vérifiez les quatre points suivants : le callback arrive en moins d’une minute, la signature ou le notif_token matche, la transaction passe de PENDING à SUCCESS en base, et le rapport Orange Money affiche la transaction sur votre compte marchand.

Répétez le scénario en simulant trois cas d’échec : refus du PIN, annulation par l’utilisateur, expiration. Chacun doit produire un état final distinct dans votre base et chacun doit déclencher un effet métier explicite (typiquement, libération du stock pour les flux e-commerce).

Étape 8 — Mettre en place le polling de secours

Un job horaire interroge l’API status d’Orange pour toute transaction restée en PENDING depuis plus de cinq minutes. Si Orange retourne SUCCESS alors que votre base est encore en attente, le webhook a été perdu et vous appliquez l’effet métier manuellement. Si Orange retourne FAILED, idem dans l’autre sens. Au-delà de 24 heures en PENDING, considérez la transaction comme expirée.

Ce polling est un filet de sécurité, pas le mécanisme principal. Une trop grande fréquence (toutes les minutes par exemple) consomme inutilement votre quota d’appel API et masque les vrais incidents webhook. Une fréquence horaire est un bon compromis pour la majorité des cas.

Erreurs fréquentes

Erreur Cause probable Solution
401 sur /oauth/v3/token client_secret invalide ou environnement mauvais Vérifier prefix sandbox/prod et Basic Auth exacte
403 sur initiation Application non encore activée en production Vérifier l’état dans le portail Orange Developer
400 currency invalid Code devise non supporté pour le pays Consulter la matrice devises de la doc Orange du pays
Notification jamais reçue URL notif_url HTTPS invalide ou bloquée Test depuis Internet via webhook.site puis vérifier TLS
Token mismatch sur notif notif_token mal stocké côté initiation Stocker les tokens immédiatement après init avant tout commit
Latence anormale Pas de cache OAuth, appel /token à chaque transaction Implémenter le cache avec refresh anticipé

FAQ

Combien de temps prend la mise en production ? Quatre à huit semaines selon le pays. Le délai vient quasi exclusivement de l’instruction du dossier merchant et, dans certains pays, de la validation par la banque centrale.

Doit-on signer toutes les notifications ? La signature des notifications est exigée dans certains pays et certains flux. Vérifiez la configuration de votre application dans le portail. Quand elle est active, refuser tout webhook non signé.

Comment gérer plusieurs pays avec un seul backend ? Une configuration par pays (OAuth URL, Web Payment URL, merchant_key, devise) externalisée en base de données. Le router applicatif sélectionne la config selon le pays détecté à l’initiation.

Le token doit-il être révoqué après usage ? Non. Le token OAuth est conçu pour être réutilisé jusqu’à expiration. La révocation explicite n’est utile qu’en cas de fuite suspectée.

Ressources officielles

Article de référence : Intégrer les APIs Mobile Money en production : Wave, Orange Money, MTN MoMo, Moov. Tutoriels frères : MTN MoMo sandbox vers production, Wave Business API en production.

À combiner avec : Orange Money + WebPay + plugin + API : comparatif pour offrir un parcours d’achat sans friction.

Service ITSkillsCenter

Site ou application web sur mesure

Conception Pro + Nom de domaine 1 an + Hébergement 1 an + Formation + Support 6 mois. Accès et code livrés. À partir de 350 000 FCFA.

Demander un devis
Publicité