NTPay Documentation de l'API Espace marchand

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éroRésultat
…0002Refusé : solde insuffisant
…0003Refusé : code secret incorrect
…0004Jamais confirmé : annulé après 5 min, sans notification
…0005Refusé tout de suite : opérateur indisponible
tout autrePayé 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.

ChampDescription
phone_numberNuméro mobile money du client : 0812345678 ou 243812345678
amountMontant
currencyCDF ou USD
trans_codeVotre référence, unique (ex. le numéro de commande) : elle revient dans la notification (order_id)
callback_urlVotre URL de notification
languageFacultatif : 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-TimestampHeure d'envoi (secondes Unix)
X-NTPay-Signaturev1= + HMAC-SHA256 (hexadécimal) de horodatage + "." + corps avec la clé de signature de votre intégration
X-NTPay-Notification-IdLe 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.

StatutSignification
0Créée
1En cours : en attente de la confirmation du client
2Refusée
3Annulée : pas de confirmation à temps
4Ré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 HTTPCause
401Clé absente, inconnue ou désactivée (sur /api/c2b : 500 avec le message)
404Transaction introuvable pour votre intégration
409Référence déjà utilisée, reversement déjà fait
422Champ manquant ou invalide (errors détaille chaque champ), règle de reversement non respectée
500Refus 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

Une question : support@ntoprog.org.