Encaisser par mobile money avec NTPay
Une seule API pour Orange Money et Vodacom M-Pesa (et les opérateurs ajoutés ensuite) : vous demandez un paiement, le client le confirme sur son téléphone, NTPay vous notifie le résultat. Les montants encaissés, frais déduits, vont sur votre solde NTPay et vous sont reversés.
Démarrer
Chaque intégration (votre site, votre application) a deux clés, visibles dans l'espace marchand, rubrique Intégrations : une clé de production et une clé de test. Envoyez-la dans l'en-tête de chaque appel, depuis votre serveur uniquement :
Authorization: Bearer VOTRE_CLE Content-Type: application/json Accept: application/json
Adresse de l'API : https://gateway.ntoprog.org/api. En production, les appels doivent venir du serveur de votre intégration (son adresse est enregistrée au premier appel).
Réessayer sans risque : Idempotency-Key
Sur POST /api/c2b, /api/payout, /api/b2c et /api/new-transaction, ajoutez un en-tête Idempotency-Key unique par opération (par exemple un UUID). Si la connexion coupe, renvoyez la même requête avec la même clé : dans les 24 heures, vous recevez la première réponse (en-tête Idempotent-Replayed: true) au lieu d'un second paiement. La même clé avec une autre requête répond 422 ; si la première est encore en cours, 409. Une requête refusée avant tout traitement (clé d'API ou champ invalide) n'est pas retenue : corrigez-la et renvoyez-la avec la même clé.
Idempotency-Key: 7f3c2a10-5b8e-4d1f-9a6e-2c4b8d0e1f37
Mode test
Avec la clé de test, tous les appels sont simulés par NTPay : aucun opérateur, aucun argent réel, depuis n'importe quel poste. Les réponses et les notifications ont exactement le format réel. Activez la clé de test depuis la page de votre intégration ; vos opérations apparaissent dans Mode test.
Le résultat dépend des 4 derniers chiffres du numéro (préfixe d'un opérateur actif, ex. 0850000002) :
| Fin du numéro | Résultat |
|---|---|
…0002 | Refusé : solde insuffisant |
…0003 | Refusé : code secret incorrect |
…0004 | Jamais confirmé : annulé après 5 min, sans notification |
…0005 | Refusé tout de suite : opérateur indisponible |
| tout autre | Payé après 5 secondes |
Même règle pour le compte crédité d'un reversement. Les reversements de test puisent dans un solde de test : encaissements de test payés, moins les frais habituels.
Encaisser
POST /api/c2b envoie une demande de paiement au téléphone du client. L'opérateur est déduit du préfixe du numéro.
| Champ | Description |
|---|---|
phone_number | Numéro mobile money du client : 0812345678 ou 243812345678 |
amount | Montant |
currency | CDF ou USD |
trans_code | Votre référence, unique (ex. le numéro de commande) : elle revient dans la notification (order_id) |
callback_url | Votre URL de notification |
language | Facultatif : FR ou EN |
curl -X POST https://gateway.ntoprog.org/api/c2b \
-H "Authorization: Bearer $NTPAY_KEY" -H "Content-Type: application/json" -H "Accept: application/json" \
-d '{"phone_number":"0851234567","amount":5000,"currency":"CDF","trans_code":"CMD-1042","callback_url":"https://votre-site.cd/ntpay"}'
Réponse 201 : la demande est partie, le client doit la confirmer. Le contenu est la réponse de l'opérateur, plus ntpay_reference, la référence NTPay pour suivre le statut.
// Orange Money
{"content": {"result_code": "0", "result_description": "Paiement En Cours de traitement !", "transaction_code": "2610257K3QX9MZ2DAH4W", "ntpay_reference": "2610257K3QX9MZ2DAH4W"}}
// Vodacom M-Pesa
{"content": {"result_code": "3", "result_description": "Processed", "transaction_id": "…", "ntpay_reference": "261025R4M8C2XKQ7WZ5H"}}
Le résultat arrive par notification, en général en moins d'une minute.
Point d'accès historique
POST /api/new-transaction reste disponible pour les intégrations existantes : mêmes champs, plus operation (c2b), output et return_url. Il répond {"result": "success", "code": 200, "message": "Transaction successfully sent!"} ou {"result": "declined", …}.
Notifications
À chaque résultat, NTPay envoie un POST JSON à votre callback_url :
{"code": 4, "message": "Paiement de 5000.00 CDF … effectue.", "result": "success", "order_id": "CMD-1042"}
result vaut success ou failed ; code est le statut. Chaque notification porte trois en-têtes :
X-NTPay-Timestamp | Heure d'envoi (secondes Unix) |
X-NTPay-Signature | v1= + HMAC-SHA256 (hexadécimal) de horodatage + "." + corps avec la clé de signature de votre intégration |
X-NTPay-Notification-Id | Le même à chaque nouvel essai d'une notification |
Vérifier une notification
Refusez une notification dont la signature ne correspond pas ou dont l'horodatage a plus de 5 minutes. Après un changement de clé, deux valeurs v1= sont envoyées pendant 24 h : au moins une doit correspondre.
// PHP
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_NTPAY_TIMESTAMP'] ?? '';
$expected = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('NTPAY_CALLBACK_SECRET'));
$signed = false;
foreach (explode(',', $_SERVER['HTTP_X_NTPAY_SIGNATURE'] ?? '') as $part) {
$signed = $signed || hash_equals($expected, trim($part));
}
if (!$signed || abs(time() - (int)$timestamp) > 300) { http_response_code(401); exit; }
$notification = json_decode($body, true);
# Python (Flask)
import hashlib, hmac, os, time
from flask import request, abort
body = request.get_data()
timestamp = request.headers.get("X-NTPay-Timestamp", "")
expected = "v1=" + hmac.new(os.environ["NTPAY_CALLBACK_SECRET"].encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest()
signed = any(hmac.compare_digest(expected, part.strip()) for part in request.headers.get("X-NTPay-Signature", "").split(","))
if not signed or abs(time.time() - int(timestamp or 0)) > 300:
abort(401)
Répondez vite avec un code 2xx. Sinon NTPay réessaie après 1 min, 5 min, 30 min, 2 h et 6 h : traitez chaque X-NTPay-Notification-Id (ou order_id) une seule fois. Le guide complet et votre clé de signature sont sur la page de votre intégration, avec un bouton pour envoyer une notification de test.
Paiements sortants
Chaque reversement, remboursement et ligne de paiement de masse notifie aussi son résultat final, avec les mêmes en-têtes et les mêmes nouveaux essais : au callback_url donné avec le reversement, sinon à l'URL de notification des paiements sortants de votre intégration (page de l'intégration).
{"type": "payout", "kind": "payout", "code": 4, "result": "success",
"message": "Reversement de 4850.00 CDF au 0812345678 effectue",
"payout_reference": "2610267M2XKQ9ZTR4HAP", "collection_reference": "2610257K3QX9MZ2DAH4W",
"order_id": "CMD-1042", "amount": "4850.00", "currency": "CDF", "account_number": "0812345678"}
kind vaut payout, refund ou mass ; pour une ligne de paiement de masse, order_id est la référence de la ligne.
Suivre un statut
GET /api/status/{ntpay_reference}, avec votre clé : NTPay interroge l'opérateur et répond avec son résultat.
| Statut | Signification |
|---|---|
| 0 | Créée |
| 1 | En cours : en attente de la confirmation du client |
| 2 | Refusée |
| 3 | Annulée : pas de confirmation à temps |
| 4 | Réussie |
Reverser
POST /api/payout crée un reversement d'un encaissement réussi vers un compte mobile money, pris sur votre solde : même devise, au plus le montant encaissé, une fois par encaissement. Il est envoyé dans la minute.
Si vous avez choisi une devise des reversements (page Solde), currency peut aussi être celle-ci : amount est alors le montant reversé, et l'encaissement est converti au cours du moment, dans les plafonds des conversions. Les reversements automatiques sont faits dans cette devise quand la conversion est possible, sinon dans celle de l'encaissement.
curl -X POST https://gateway.ntoprog.org/api/payout \
-H "Authorization: Bearer $NTPAY_KEY" -H "Content-Type: application/json" -H "Accept: application/json" \
-d '{"trans_code":"CMD-1042","account_number":"0812345678","amount":4850,"currency":"CDF","callback_url":"https://votre-site.cd/ntpay/reversements"}'
callback_url est facultatif : le résultat du reversement y est notifié (voir Notifications, paiements sortants).
Réponse 201 {"result": "success", "message": "Payout created !"} ; sinon 404, 409 (déjà reversé) ou 422 (non réussi, devise, montant, solde insuffisant, conversion impossible) avec un message.
POST /api/b2c avec {"transaction_code": "référence NTPay de l'encaissement"} envoie tout de suite son reversement en attente.
Liens de paiement
Sans développement : créez un lien dans l'espace marchand et partagez-le. Depuis votre site, un simple lien vers la page de paiement suffit (le code du bouton est sur la page du lien). L'adresse peut pré-remplir la page : amount (montant libre), name, email, f1 à f3 (informations demandées).
https://pay.ntoprog.org/k3w7sabmwf?amount=15000&f1=E-2041
Erreurs
| Code HTTP | Cause |
|---|---|
| 401 | Clé absente, inconnue ou désactivée (sur /api/c2b : 500 avec le message) |
| 404 | Transaction introuvable pour votre intégration |
| 409 | Référence déjà utilisée, reversement déjà fait |
| 422 | Champ manquant ou invalide (errors détaille chaque champ), règle de reversement non respectée |
| 500 | Refus de l'opérateur ou indisponibilité : le message l'indique, réessayez plus tard |
Tous vos appels des 30 derniers jours, avec ce qui a été envoyé et répondu, sont dans l'espace marchand : Journal des appels API.
Téléchargements
- Spécification OpenAPI 3.1 (YAML), pour générer un client ou l'importer dans un outil
- Collection Postman : renseignez les variables
api_key(votre clé de test pour commencer) etbase_url
Une question : support@ntoprog.org.