Documentation /Callback URL

Callbacks

OPENPAY envoie automatiquement une notification HTTP POST vers votre callback URL lorsque le statut d’un paiement évolue, ou lorsqu’un retrait programmé créé via l’API est approuvé par OpenPay, sans polling de votre côté.

1. Configuration

Renseignez votre Callback URL depuis votre tableau de bord partenaire (onglet Callback URL). Il doit s'agir d'une URL publique accessible depuis internet (HTTPS recommandé).

Votre endpoint doit obligatoirement retourner un statut HTTP 200 pour indiquer que la notification a bien été reçue. Tout autre code est considéré comme un échec et déclenche une nouvelle tentative.

2. Politique de relance

Si votre endpoint ne répond pas avec un HTTP 200, OPENPAY relance automatiquement la notification selon le calendrier suivant :

TentativeDélai avant envoiTimeout par tentative
1èreImmédiat10 secondes
2ème+ 5 secondes10 secondes
3ème+ 15 secondes10 secondes
4ème+ 30 secondes10 secondes

Après 4 tentatives infructueuses, la livraison est abandonnée. Vous pouvez toujours vérifier manuellement le statut d'une transaction via l'endpoint Statut de paiement.

3. Format du payload

OPENPAY envoie une requête POST avec le header Content-Type: application/json. Le même callback URL reçoit les deux familles d’événements ; distinguez-les avec type et status.

3.1 Paiement (collection)

Envoyé lorsque le statut d’une transaction de type payment change (ex. succès ou échec).

Exemple - paiement réussiPOST
{
  "reference": "PTXN26042237B99A5D9",
  "amount": "5000",
  "currency": "XAF",
  "paymentPhoneNumber": "242066203420",
  "provider": "MTN",
  "type": "payment",
  "status": "success",
  "message": "Paiement effectué avec succès.",
  "customer": {
    "name": "Jean Dupont",
    "phone": "242066203420"
  },
  "metadata": {
    "order_id": "123456",
    "id_cv": "dc769729-78f6-4ae9-a882-ee5830f156d9"
  }
}

3.2 Retrait programmé approuvé

Envoyé lorsque le retrait a été approuvé. Le champ reference correspond à l’UUID reference que vous avez fourni à la création. Ensuite, déclenchez le virement avec POST /scheduled-cashout/:reference/execute.

Exemple - retrait approuvé (metadata.event = approved)POST
{
  "reference": "550e8400-e29b-41d4-a716-446655440000",
  "amount": "50000.00",
  "currency": "XAF",
  "paymentPhoneNumber": "242066203420",
  "provider": "MTN",
  "type": "scheduled_cashout",
  "status": "approved",
  "message": "Retrait programmé approuvé. Vous pouvez déclencher l'exécution via l'API.",
  "metadata": {
    "event": "approved",
    "execution_date": "2026-10-20T08:00:00.000Z",
    "origin_app": "MonApp Mobile v2"
  }
}

À noter : ce callback signale l’approbation, pas l’exécution du virement mobile money. Le résultat du cash-out apparaît dans vos transactions partenaire une fois execute appelé.

ChampTypeDescription
referencestring Paiement : référence OpenPay de la transaction. Retrait programmé : UUID reference fourni à la création API.
amountstringMontant de la transaction
currencystringDevise (ex : XAF)
paymentPhoneNumberstringNuméro mobile money (paiement ou bénéficiaire du retrait)
providerstringOpérateur mobile (MTN, AIRTEL)
typestringpayment (paiement) ou scheduled_cashout (retrait programmé approuvé)
statusstring Paiement : success, failed, cancelled, etc. Retrait programmé : approved
messagestringMessage lisible décrivant l’événement
customerobject | nullInformations du client si fournies lors de la création
metadataobject | null Paiement : métadonnées envoyées à la création. Retrait programmé : event (approved), execution_date, origin_app (application client final)

4. Réponse attendue

Votre endpoint doit retourner un HTTP 200 dans les 10 secondes. Le corps de la réponse n'est pas obligatoire, mais un JSON est conseillé pour faciliter le débogage.

Réponse minimale attendue200 OK
{ "success": true }

5. Exemples d'implémentation

Node.js Express

webhook.js
app.post('/webhook/openpay', (req, res) => {
  const data = req.body;

  if (data.type === 'payment' && data.status === 'success') {
    const { order_id } = data.metadata || {};
    // Marquer la commande comme payée…
  }

  if (data.type === 'scheduled_cashout' && data.status === 'approved') {
    const ref = data.reference; // UUID fourni à la création
    const app = data.metadata?.origin_app;
    // Notifier l'utilisateur, puis appeler POST /scheduled-cashout/:ref/execute quand prêt
  }

  res.status(200).json({ success: true });
});

PHP Laravel / Vanilla

webhook.php
<?php
$payload = json_decode(file_get_contents('php://input'), true);

if (($payload['type'] ?? '') === 'payment' && ($payload['status'] ?? '') === 'success') {
    $orderId = $payload['metadata']['order_id'] ?? null;
    // Marquer la commande comme payée
}

if (($payload['type'] ?? '') === 'scheduled_cashout' && ($payload['status'] ?? '') === 'approved') {
    $reference = $payload['reference'];
    // Retrait approuvé - exécuter via l’API quand approprié
}

http_response_code(200);
echo json_encode(['success' => true]);

Python Flask / FastAPI

webhook.py
@app.route('/webhook/openpay', methods=['POST'])
def openpay_webhook():
    data = request.get_json()

    if data.get('type') == 'payment' and data.get('status') == 'success':
        order_id = data.get('metadata', {}).get('order_id')
        # Traiter la commande...

    if data.get('type') == 'scheduled_cashout' and data.get('status') == 'approved':
        reference = data.get('reference')
        # Retrait approuvé - appeler execute côté OpenPay

    return jsonify({"success": True}), 200

6. Bonnes pratiques

  • Répondre toujours avec HTTP 200 dès réception, avant tout traitement métier long.
  • Brancher votre logique sur type : paiement (payment + success), retrait approuvé (scheduled_cashout + approved).
  • Utiliser le champ metadata pour passer vos identifiants internes (ex : order_id, user_id) lors de la création du paiement.
  • Utiliser la page de paramètres partenaire pour tester votre callback URL avant la mise en production.
  • En cas de doute, vous pouvez toujours interroger l'endpoint Statut de paiement pour confirmer l'état d'une transaction.