Intégrez la caisse C-Pay à votre site ou votre application
Une API REST, des webhooks signés, des SDK et une extension WooCommerce pour que vos ventes en ligne tombent dans la même caisse que le comptoir. Base : https://api.c-pay.global/api/v1.
Périmètre de lancement : les six pays de la CEMAC et la RD Congo. Moyens prévus : cartes bancaires, opérateurs de monnaie mobile et GIMAC. Les fonctions sont disponibles selon le pays et le service activés ; la connexion GIMAC est en cours d’intégration. La détection d’un numéro ne garantit pas la disponibilité d’un paiement.
Trois étapes séparent votre compte de votre premier lien de paiement. Un compte de niveau 0 suffit pour intégrer ; la vérification KYB n'est nécessaire que pour relever vos limites.
Créez un compte marchand
Sur merchant.c-pay.global/register. Le portail marchand est aussi l'endroit où vous suivez les paiements reçus par l'API.
Créez une clé API dans le portail
Portail marchand → Clés API → nouvelle clé avec les permissions voulues (pay_links, transactions). La clé et son secret ne sont affichés qu'une fois : stockez-les côté serveur, jamais dans un navigateur ou une application mobile.
Créez votre premier lien de paiement
Trois appels cURL ci-dessous : vérifier la clé, créer le lien, retrouver le paiement dans vos transactions.
Votre premier lien de paiement en trois appels
# 1. Vérifier la clé : la liste (vide ou non) de vos transactions répond 200
curl https://api.c-pay.global/api/v1/merchant/public/transactions?limit=1 \
-H "X-API-Key: $CPAY_API_KEY" \
-H "X-API-Secret: $CPAY_API_SECRET"
# 2. Créer un lien de paiement de 25 000 XAF valable 48 h
curl -X POST https://api.c-pay.global/api/v1/merchant/public/pay-links \
-H "X-API-Key: $CPAY_API_KEY" \
-H "X-API-Secret: $CPAY_API_SECRET" \
-H "Content-Type: application/json" \
-d '{ "amount": 25000, "currency": "XAF", "description": "Facture INV-001", "expiresIn": 48 }'
# → 201 { "short_url": "https://pay.c-pay.global/AB12CD", "expires_at": "…", … }
# Envoyez short_url à votre client (SMS, WhatsApp, e-mail) ou affichez-le en QR.
# 3. Retrouver le paiement une fois le client passé par la page
curl "https://api.c-pay.global/api/v1/merchant/public/transactions?status=authorized&limit=5" \
-H "X-API-Key: $CPAY_API_KEY" \
-H "X-API-Secret: $CPAY_API_SECRET"
Pour être prévenu sans interroger l'API, configurez un webhook : C-Pay appelle votre serveur à chaque paiement confirmé ou échoué.
Authentification et conventions
Les endpoints /merchant/public/* s'authentifient par clé API. Les endpoints de l'API Caisse utilisent la session du marchand (jeton Bearer). Toutes les requêtes et réponses sont en JSON.
En-têtes HTTP attendus
En-tête
Obligatoire
Rôle
X-API-Key
oui (routes publiques)
Clé créée dans le portail marchand. Absente ou inconnue : 401 API_KEY_REQUIRED / 401 INVALID_API_KEY.
X-API-Secret
oui pour les clés récentes
Secret remis avec la clé. Les clés d'ancienne génération n'en ont pas ; les nouvelles le vérifient (401 INVALID_API_SECRET).
Idempotency-Key
recommandé sur tout POST, obligatoire sur les opérations de caisse
8 à 128 caractères, par exemple un UUID v4. Rejouer la même clé avec le même corps renvoie la réponse initiale ; la même clé avec un corps différent renvoie 409 IDEMPOTENCY_CONFLICT ; une requête identique encore en cours renvoie 409 IDEMPOTENCY_IN_PROGRESS. Longueur invalide : 400 INVALID_IDEMPOTENCY_KEY. Les clés sont conservées 24 h.
Content-Type
oui sur POST
application/json.
Authorization
API Caisse uniquement
Bearer <access>, jeton de 15 minutes obtenu par POST /merchant/auth/login et renouvelé par /merchant/auth/refresh.
Base URL
https://api.c-pay.global/api/v1
Montants
Les exemples du lancement utilisent des montants entiers en XAF ou en CDF. Aucune conversion de change : la devise du client doit être celle du marchand.
Erreurs
Corps { "error": "message lisible", "code": "CODE_STABLE", "details?": … }. Codez sur code, pas sur error. Liste dans Codes d'erreur.
Permissions
Chaque clé porte ses permissions (pay_links, transactions, collect, disburse). Permission manquante : 403.
Limites de débit
Les points d'entrée publics sont limités en débit ; 429 avec les en-têtes RateLimit-* standard. POST /detect/phone : 30 requêtes par minute et par adresse.
Sécurité
N'appelez l'API que depuis votre serveur. Une clé exposée dans un navigateur ou une application mobile doit être révoquée immédiatement dans le portail.
Référence des endpoints publics
Seuls les endpoints réellement en production figurent ici. La référence Swagger fait foi en cas d'écart.
POST/merchant/public/pay-links
Crée un lien de paiement hébergé sur pay.c-pay.global, partageable par SMS, WhatsApp, e-mail ou QR. Le client paie par Mobile Money ou par carte (si une passerelle carte est active dans votre pays) sans installer d'application. Permission pay_links.
Paramètres du corps
Champ
Type
Description
amount
nombre, requis
Montant strictement positif en unité de la devise.
currency
chaîne
XAF ou CDF. Défaut XAF.
description
chaîne
Libellé affiché au client sur la page de paiement.
expiresIn
nombre
Durée de validité en heures, de 1 à 720. Défaut 24. Hors plage : 422 INVALID_EXPIRES_IN.
Le statut public d'un lien est lisible sans clé par le client : GET https://pay.c-pay.global/<short_id>/status → { "status": "paid|pending|failed|expired|cancelled" }.
GET/merchant/public/transactions
Historique paginé de vos transactions, les plus récentes d'abord. Permission transactions.
Paramètres de requête
Paramètre
Type
Description
status
chaîne
Filtre sur le statut : pending, authorized, failed, expired.
Déduit le pays, la devise et l'opérateur Mobile Money d'un numéro au format international. Utile pour pré-afficher l'opérateur dans votre interface. Limité à 30 requêtes par minute et par adresse ; aucune transaction n'est créée.
Numéro non reconnu : 200 avec success: false et un code (PHONE_INVALID, COUNTRY_UNSUPPORTED, PHONE_LENGTH, OPERATOR_UNKNOWN, OPERATOR_NO_WALLET). Corps sans phone : 400 PHONE_REQUIRED. alternatives liste les services que le client choisit lui-même (par exemple Express Union dans le périmètre actuel).
POST/merchant/public/collectbientôt
Encaissement Mobile Money direct par clé API (demande de paiement poussée sur le téléphone du client). Corps prévu : amount, customerPhone, currency, description, reference. Cet endpoint répond aujourd'hui 410 FEATURE_DISABLED : il sera ouvert pays par pays au fil de l'activation des opérateurs. En attendant, les liens de paiement couvrent le même besoin, et l'API Caisse offre l'encaissement direct en session marchand.
API Caisse session marchand
Ces endpoints portent les opérations de caisse du portail marchand et de l'application Android : encaisser, retirer, transférer, suivre. Ils sont authentifiés par la session du marchand (Authorization: Bearer <access> obtenu via POST /merchant/auth/login, 15 minutes, renouvelé par /merchant/auth/refresh), pas par clé API. Ils conviennent à vos propres outils de caisse ; pour une intégration serveur, préférez les endpoints publics ci-dessus. Toute requête qui crée un mouvement exige Idempotency-Key.
Endpoints de l'API Caisse
Endpoint
Rôle
GET/merchant/cash/summary
Solde (available, pending, reserved), totaux du jour, limites et profil (niveau KYB, wallet, comptes bancaires).
POST/merchant/cash/collect
Encaisser. amount, method (mobile_money ou card), customerPhone (requis en Mobile Money, opérateur détecté ; operator seulement pour sélectionner un service alternatif activé, par exemple Express Union), currency, description, reference. Mobile Money : 201 avec txnId, status: "pending", operator, fee, net, statusUrl. Carte : 201 avec payLink { shortId, shortUrl, expiresAt } si une passerelle carte est configurée, sinon 410 FEATURE_DISABLED.
GET/merchant/cash/collect/:txnId
Statut d'un encaissement : pending, authorized, failed, expired, avec amount, fee, net, receiptUrl si disponible. Interrogez toutes les 3 secondes pendant 90 secondes, puis affichez « en attente de confirmation ».
GET / PUT/merchant/cash/wallet
Wallet Mobile Money de destination des retraits (operator, phone, verifiedAt). L'enregistrement exige confirm.
Transférer. directionwallet_to_bank (compte bancaire vérifié, confirm requis, référence TRF-XXXXXX, statut processing ou pending_manual) ou bank_to_wallet (ordre d'approvisionnement FND-XXXXXX, statut awaiting_funds, instructions de virement ; 409 FUNDING_UNAVAILABLE sans compte de collecte). Compléments : GET /merchant/cash/transfers, GET …/transfers/:id, POST …/transfers/:id/cancel.
GET/merchant/cash/history
Journal unifié (from, to, type ∈ collect, withdraw, transfer_out, transfer_in, limit, offset) avec totals { in, out }. Export : GET /merchant/cash/history/export.csv?from&to.
Confirmation forte : l'objet confirm
Les retraits, les transferts wallet_to_bank et l'enregistrement du wallet exigent un objet confirm dans le corps. Son contenu dépend de la configuration du compte :
Contenu de l'objet confirm
Compte
Corps attendu
Double authentification (TOTP) activée
"confirm": { "code": "123456" } — code à 6 chiffres de l'application d'authentification
Sans double authentification
"confirm": { "password": "…" } — mot de passe du compte
Objet absent ou invalide : 428 STEP_UP_REQUIRED { "methods": ["totp"] } ou ["password"], qui indique la méthode à présenter. Cinq échecs en 15 minutes : 429. Un ordre bank_to_wallet n'exige pas de confirmation : aucun fonds ne quitte le compte.
Déclarez une URL https:// dans le portail marchand (rubrique Webhooks) ; le secret de signature est affiché une seule fois à la création. À chaque changement de statut, C-Pay envoie un POST JSON signé en HMAC-SHA256. Les adresses privées ou non HTTPS sont refusées, les redirections ne sont pas suivies, le délai maximal est de 10 secondes et une réponse 5xx ou une erreur réseau déclenche jusqu'à 3 nouvelles tentatives (5 s puis 10 s).
En-têtes envoyés
Content-Type: application/json
User-Agent: CPay-Webhooks/1.0
X-CPay-Event: payment.completed
X-CPay-Signature: sha256=3f1a…c9 ← à vérifier
X-CPay-Timestamp: 2026-10-16T09:12:33.120Z (informatif, non signé)
X-CPlus-Event: payment.completed (transition, sera retiré)
X-CPlus-Signature: sha256=3f1a…c9 (transition, sera retiré)
Les en-têtes X-CPlus-* sont conservés provisoirement pour les consommateurs existants et seront retirés au plus tôt 90 jours après annonce sur cette page. Nouvelles intégrations : n'utilisez que X-CPay-Signature.
Événements et corps
Événements du webhook marchand : payment.completed, payment.failed, webhook.test (même forme, txnId préfixé test-, envoyé par le bouton « Tester » du portail).
signature = "sha256=" + hex(HMAC-SHA256(secret, corps brut)). Calculez sur le corps exactement tel qu'il est reçu (octets, pas une re-sérialisation JSON), comparez en temps constant, répondez 2xx rapidement puis traitez en asynchrone. Un même événement peut être livré plusieurs fois : dédupliquez sur txnId.
import hmac, hashlib
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
given = request.headers.get("X-CPay-Signature", "").removeprefix("sha256=")
if not hmac.compare_digest(expected, given):
abort(401)
event = json.loads(raw_body)
# répondre 200, puis traiter en asynchrone, idempotent sur event["txnId"]
SDK & plugin
Les SDK encapsulent l'authentification, l'idempotence et la vérification des webhooks. Ils sont facultatifs : l'API s'utilise avec n'importe quel client HTTP.
SDK JavaScript / Node.js @c-pay/sdk 1.1.0
Pour Node.js 18+, côté serveur uniquement. Fournit CPayClient (liens de paiement, transactions) et CPayClient.verifyWebhook(), qui choisit le bon en-tête de signature pendant la transition.
PHP 7.4+, extensions cURL et JSON. Classe CPay\CPayClient (createPayLink, listTransactions, getTransaction) et exceptions CPayException portant le code de l'API.
Les SDK 1.1.0 envoient X-API-Key mais pas encore X-API-Secret : utilisez-les avec une clé d'ancienne génération (sans secret) ou appelez l'API directement comme ci-dessus. Les sources sont remises par developers@c-pay.global si votre gestionnaire de paquets ne trouve pas encore le paquet.
Archives à télécharger
Version 2.7.0. Vérifiez l'empreinte SHA-256 après téléchargement (sha256sum fichier.zip).
Extension WooCommerce 2.7.0
Ajoute C-Pay comme moyen de paiement dans WooCommerce 6.0+ (PHP 7.4+). Encaissement Mobile Money et lien de paiement ; configuration avec votre clé API et votre secret ; webhook signé vérifié par l'extension.
1. WordPress → Extensions → Ajouter → Téléverser l'archive
2. Activer, puis WooCommerce → Réglages → Paiements → C-Pay
3. Renseigner la clé API et le secret (portail marchand → Clés API)
4. Déclarer l'URL de webhook affichée par l'extension dans le portail marchand (rubrique Webhooks)
SDK Android 2.7.0
Bibliothèque Kotlin/Java (AAR, Android 5.0+ / API 21) pour encaisser depuis une application marchande. Les identifiants doivent venir de votre backend, jamais être embarqués dans l'APK.
Un environnement de test est disponible sur demande : il n'est pas encore en libre-service. Écrivez à developers@c-pay.global en précisant votre cas d'usage, le pays visé et le type d'intégration (API, WooCommerce, Android). Nous revenons vers vous avec une clé de test, l'URL de base du bac à sable et les numéros de téléphone de test qui simulent un paiement confirmé, refusé ou expiré.
En production, le bouton Tester de la rubrique Webhooks du portail envoie un événement webhook.test signé à votre URL.
Codes d'erreur
Toute erreur renvoie { "error", "code", "details?" }. Les codes ci-dessous sont stables ; le message error peut changer.
Codes d'erreur de l'API
HTTP
Code
Signification
400
PHONE_REQUIRED
Corps de /detect/phone sans phone.
400
INVALID_IDEMPOTENCY_KEY
Idempotency-Key hors de 8 à 128 caractères.
401
API_KEY_REQUIRED
En-tête X-API-Key absent.
401
INVALID_API_KEY
Clé inconnue, désactivée ou expirée.
401
INVALID_API_SECRET
X-API-Secret absent ou incorrect pour une clé qui en possède un.
403
MERCHANT_INACTIVE
Compte marchand suspendu ou non activé.
403
—
Permission manquante sur la clé (pay_links, transactions…).
409
IDEMPOTENCY_CONFLICT
Même Idempotency-Key avec un corps différent.
409
IDEMPOTENCY_IN_PROGRESS
Requête identique encore en cours de traitement.
409
FUNDING_UNAVAILABLE
Aucun compte de collecte C-Pay pour la devise (ordre d'approvisionnement).
409
TRANSFER_NOT_CANCELLABLE
Le transfert n'est plus dans un statut annulable.
410
FEATURE_DISABLED
Fonction non activée : encaissement direct par clé API, carte sans passerelle, Tap-to-Phone, rail sortant absent.
422
VALIDATION_ERROR
Paramètres invalides (details précise le champ).
422
INVALID_EXPIRES_IN
expiresIn hors de 1 à 720 heures.
422
CURRENCY_MISMATCH
La devise du numéro du client n'est pas celle du marchand.
422
INSUFFICIENT_BALANCE
Solde disponible insuffisant (details.available).
422
WALLET_MISSING
Aucun wallet enregistré pour le retrait.
422
DAILY_LIMIT_EXCEEDED
Limite journalière de retrait du niveau KYB atteinte.
428
STEP_UP_REQUIRED
Objet confirm absent ou invalide ; methods indique totp ou password.
429
—
Limite de débit atteinte (en-têtes RateLimit-*), ou cinq confirmations erronées en 15 minutes.
503
IDEMPOTENCY_UNAVAILABLE
Contrôle d'idempotence momentanément indisponible ; réessayez avec la même clé.