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 :
| Tentative | Délai avant envoi | Timeout par tentative |
|---|---|---|
| 1ère | Immédiat | 10 secondes |
| 2ème | + 5 secondes | 10 secondes |
| 3ème | + 15 secondes | 10 secondes |
| 4ème | + 30 secondes | 10 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).
{
"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.
{
"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é.
| Champ | Type | Description |
|---|---|---|
| reference | string | Paiement : référence OpenPay de la transaction. Retrait programmé : UUID reference fourni à la création API. |
| amount | string | Montant de la transaction |
| currency | string | Devise (ex : XAF) |
| paymentPhoneNumber | string | Numéro mobile money (paiement ou bénéficiaire du retrait) |
| provider | string | Opérateur mobile (MTN, AIRTEL) |
| type | string | payment (paiement) ou scheduled_cashout (retrait programmé approuvé) |
| status | string | Paiement : success, failed, cancelled, etc. Retrait programmé : approved |
| message | string | Message lisible décrivant l’événement |
| customer | object | null | Informations du client si fournies lors de la création |
| metadata | object | 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.
{ "success": true }5. Exemples d'implémentation
Node.js Express
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
<?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
@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}), 2006. 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
metadatapour 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.