Environnements
Chaque compte NguePay dispose de deux environnements totalement séparés : comptes, clés, transactions et webhooks ne sont jamais partagés entre eux.
| Sandbox | Live | |
|---|---|---|
| Usage | Développement et tests | Paiements réels de vos clients |
| Dashboard | dashboard.sandbox.nguepay.com | dashboard.live.nguepay.com |
| URL de l'API | https://api.sandbox.nguepay.com/api/v1 | https://api.live.nguepay.com/api/v1 |
| Clé secrète | sk_sandbox_… | sk_live_… |
| Clé publique | pk_sandbox_… | pk_live_… |
| Argent réel | Non | Oui |
| Accès | Dès l'inscription | Après validation de votre dossier KYC |
Une clé sandbox ne fonctionne que sur l'API sandbox, et une clé live que sur l'API live. Si vous mélangez les deux, l'API répond 401 avec le code NGP001.
Clés API
Créez vos clés depuis la rubrique Clés API du dashboard de l'environnement voulu. Chaque jeu contient deux clés :
- Clé secrète (
sk_…) : pour les appels depuis votre serveur. Elle ne doit jamais apparaître dans une page web, une application mobile ou un dépôt de code. - Clé publique (
pk_…) : pour le navigateur (SDK JS). Elle ne permet que de créer une session de paiement, et seulement depuis les domaines que vous avez déclarés.
La clé secrète n'est affichée qu'une fois, à sa création. Rangez-la dans une variable d'environnement de votre serveur. Toutes les requêtes passent la clé dans l'en-tête Authorization :
Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxPour vérifier votre clé, appelez GET /ping :
curl https://api.sandbox.nguepay.com/api/v1/ping \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Premier paiement
- Créez le paiement depuis votre serveur avec
POST /payments, en indiquant le montant en XOF, le moyen de paiement et votre référence de commande. - Redirigez votre client vers le
checkout_urlrenvoyé, ou laissez-le valider sur son téléphone selon le moyen choisi. - Attendez le webhook
payment.succeededoupayment.failedavant de valider la commande.
curl -X POST https://api.sandbox.nguepay.com/api/v1/payments \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \ -H "Content-Type: application/json" \ -d '{ "amount": 5000, "provider": "orange", "reference": "CMD-20481", "description": "Commande CMD-20481", "webhook_url": "https://votresite.ci/webhooks/nguepay" }'
const res = await fetch("https://api.sandbox.nguepay.com/api/v1/payments", { method: "POST", headers: { Authorization: `Bearer ${process.env.NGUEPAY_SECRET_KEY}`, "Content-Type": "application/json" }, body: JSON.stringify({ amount: 5000, provider: "orange", reference: "CMD-20481", phone: "+2250700000000" }) }); const { data } = await res.json(); // data.uuid, data.status ("pending"), data.checkout_url
// .env : NGUEPAY_API_URL=https://api.sandbox.nguepay.com/api/v1 $response = Http::withToken(env('NGUEPAY_SECRET_KEY')) ->post(env('NGUEPAY_API_URL').'/payments', [ 'amount' => 5000, 'provider' => 'orange', 'reference' => 'CMD-20481', 'phone' => '+2250700000000', ]); $payment = $response->json('data');
Réponse (201 à la création, 200 si la même référence a déjà été soumise) :
{
"success": true,
"message": "Paiement initié.",
"data": {
"uuid": "9d3f1c2a-…",
"status": "pending",
"provider_code": "orange",
"amount_xof": 5000,
"currency": "XOF",
"reference": "CMD-20481",
"checkout_url": "https://pay.…nguepay.com/…",
"expires_at": "2026-10-14T10:15:00Z"
}
}Idempotence. Une même reference ne peut être payée qu'une fois. Si votre serveur renvoie la requête après une coupure réseau, NguePay renvoie la transaction existante au lieu d'en créer une seconde.
Suivre un paiement
Le webhook est la source de vérité. Vous pouvez aussi interroger une transaction à tout moment avec son uuid :
curl https://api.sandbox.nguepay.com/api/v1/payments/9d3f1c2a-… \ -H "Authorization: Bearer sk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
| Statut | Signification |
|---|---|
| pending | Paiement créé, en attente du client. |
| processing | Le client a validé, confirmation de l'opérateur en cours. |
| succeeded | Paiement encaissé. Vous pouvez livrer. |
| failed | Paiement refusé, annulé par le client ou expiré. Le motif est dans failure_reason (par exemple expired si le client n'a pas payé avant expires_at). |
| canceled | Paiement annulé avant d'être payé. |
Webhooks
Déclarez l'URL de votre serveur dans Paramètres → Webhook du dashboard, ou transmettez webhook_url à la création d'un paiement. Faites-le dans chaque environnement : l'URL et le secret de signature sandbox ne servent pas en live.
NguePay envoie un POST JSON à chaque changement d'état :
| Événement | Quand |
|---|---|
payment.initiated | Le paiement est créé. |
payment.succeeded | Le paiement est encaissé. |
payment.failed | Le paiement a échoué ou expiré. |
Chaque envoi porte les en-têtes X-NguePay-Event, X-NguePay-Delivery, X-NguePay-Timestamp et X-NguePay-Signature. La signature est un HMAC-SHA256 (hexadécimal) de la chaîne {timestamp}.{corps brut}, calculé avec votre secret de webhook. Vérifiez-la avant tout traitement, et refusez les requêtes dont l'horodatage a plus de 5 minutes.
import crypto from "node:crypto"; // Express : app.post("/webhooks/nguepay", express.raw({ type: "application/json" }), handler) function verifierNguePay(req) { const ts = req.get("X-NguePay-Timestamp"); const sig = req.get("X-NguePay-Signature") || ""; if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false; const attendu = crypto .createHmac("sha256", process.env.NGUEPAY_WEBHOOK_SECRET) .update(`${ts}.${req.body}`) .digest("hex"); return sig.length === attendu.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(attendu)); }
$ts = $_SERVER['HTTP_X_NGUEPAY_TIMESTAMP'] ?? ''; $sig = $_SERVER['HTTP_X_NGUEPAY_SIGNATURE'] ?? ''; $body = file_get_contents('php://input'); $attendu = hash_hmac('sha256', $ts.'.'.$body, getenv('NGUEPAY_WEBHOOK_SECRET')); if (abs(time() - (int) $ts) > 300 || !hash_equals($attendu, $sig)) { http_response_code(401); exit; } $event = json_decode($body, true); // ['event' => 'payment.succeeded', 'data' => [...]] http_response_code(200);
Répondez par un code 2xx rapidement. Sans réponse 2xx, NguePay réessaie jusqu'à 3 fois (après 1, 5 puis 30 minutes). Un même événement peut donc arriver plusieurs fois : traitez-le une seule fois, en vous appuyant sur l'uuid de la transaction.
Passer en live
Votre intégration sandbox fonctionne de bout en bout ? Le passage en live se fait sans changer votre code, seulement votre configuration.
- Faites valider votre KYC depuis le dashboard sandbox : RCCM, pièce d'identité du représentant légal et compte de reversement.
- Connectez-vous au dashboard live sur dashboard.live.nguepay.com et activez vos moyens de paiement.
- Générez vos clés live (
sk_live_…,pk_live_…) dans Clés API. Déclarez les domaines autorisés pour la clé publique. - Configurez le webhook live : URL de production et nouveau secret de signature.
- Changez trois variables sur votre serveur de production, puis faites un premier paiement réel de faible montant.
.env sandbox
NGUEPAY_API_URL=https://api.sandbox.nguepay.com/api/v1 NGUEPAY_SECRET_KEY=sk_sandbox_… NGUEPAY_WEBHOOK_SECRET=…secret sandbox…
.env live
NGUEPAY_API_URL=https://api.live.nguepay.com/api/v1 NGUEPAY_SECRET_KEY=sk_live_… NGUEPAY_WEBHOOK_SECRET=…secret live…
Référence : créer un paiement
POST /payments · authentification par clé secrète.
| Champ | Type | Description |
|---|---|---|
amount requis | entier | Montant en XOF, sans décimales, de 100 à 5 000 000. |
provider requis | texte | orange (Orange Money) ou stripe (carte bancaire). wave et mtn seront acceptés à leur ouverture. |
reference requis | texte (150 max) | Votre référence de commande, unique. |
phone | texte | Numéro mobile money du client, au format international (+225…). |
email | E-mail du client. | |
description | texte (255 max) | Libellé affiché au client. |
webhook_url | URL | Remplace l'URL de webhook du compte pour ce paiement. |
metadata | objet | Données libres, renvoyées telles quelles. |
Dans la réponse, amount_xof donne le montant en XOF. Le champ amount est exprimé en centimes (5000 XOF → 500000).
Codes d'erreur
En cas d'erreur, la réponse contient "success": false, un message et un error_code.
| Code | Signification |
|---|---|
NGP001 | Clé API invalide, ou clé utilisée sur le mauvais environnement. |
NGP002 | Montant invalide (hors de 100 à 5 000 000 XOF). |
NGP003 | Moyen de paiement momentanément indisponible. |
NGP004 | Numéro de téléphone invalide. |
NGP005 | Transaction déjà traitée. |
NGP006 | Signature webhook invalide. |
NGP007 | Trop de requêtes, réessayez plus tard. |
NGP008 | Ressource introuvable. |
NGP009 | Transaction refusée pour raison de sécurité. |
NGP010 | Moyen de paiement non activé sur votre compte. |
NGP011 | Plafond mensuel dépassé. |
NGP015 | Clé publique sans domaine déclaré. |
Documentation complète
Le détail de chaque sujet, avec des exemples en cURL, PHP, JavaScript et Python :
- Guide d'intégration (version Markdown)
- Démarrage rapide · Authentification
- Paiements · Remboursements · Virements sortants
- Webhooks · Référence des événements
- Idempotence · Erreurs · SDK JavaScript
- Passage en production · Référence de l'API
Une question sur l'intégration ? Écrivez à contact@nguepay.com. Pour une vue d'ensemble, lisez aussi comment accepter les paiements en ligne en Côte d'Ivoire.