Documentation développeur

L'API BaobaPay pour encaisser en zones CEMAC (XAF) et UEMOA (XOF) — mobile money, carte et liens de paiement. Le paiement de votre client est routé vers votre compte marchand.

Introduction

L'API est organisée autour de ressources REST, avec des réponses JSON et des codes HTTP standards. Toutes les requêtes passent par HTTPS.

BASE URL
https://api.baobapay.com/api/v1           // production
https://api.sandbox.baobapay.com/api/v1   // sandbox

Deux environnements : sandbox (clés sk_sb_…, aucun argent réel) et production (clés sk_live_…, activées à la signature du contrat).

Les zones XAF (CEMAC) et XOF (UEMOA) sont des devises distinctes : chaque encaissement est libellé dans la devise de votre marché.

Quickstart

Encaissez un premier paiement mobile money en sandbox, en trois étapes.

1. Créez un pay-in mobile money

cURL
curl https://api.sandbox.baobapay.com/api/v1/payins/mobile-money \
  -H "Authorization: Bearer sk_sb_9f2a…" \
  -H "Idempotency-Key: 8f14e45f" \
  -H "Content-Type: application/json" \
  -d '{
    "reference": "CMD-10412",
    "amount": { "currency": "XAF", "value": 24500 },
    "operator": "MTN",
    "phone": "+237671224188"
  }'

2. Lisez la réponse

200 · JSON
{
  "id": "pi_10482",
  "status": "PENDING",
  "amount": { "currency": "XAF", "value": 24500 }
}

3. Recevez la confirmation

Quand le client confirme sur son téléphone, BaobaPay envoie payin.succeeded à votre endpoint de webhook — voir Webhooks plus bas.

Événement · JSON
{
  "type": "payin.succeeded",
  "data": { "id": "pi_10482", "status": "SUCCEEDED" }
}

Authentification

Chaque requête s'authentifie par une clé secrète passée en en-tête Authorization, au format Bearer. Ne jamais exposer une clé sk_live_ côté client.

HEADER
Authorization: Bearer sk_sb_9f2a3b7c1d…
PréfixeEnvironnement
sk_sb_…Sandbox — actif immédiatement
sk_live_…Production — après signature du contrat
Une clé non authentifiée renvoie 401 unauthorized. Gérez et régénérez vos clés dans le tableau de bord → Développeurs.

Idempotence

Toutes les requêtes POST qui créent des mouvements acceptent un en-tête Idempotency-Key. Rejouer la même clé renvoie la réponse d'origine, sans créer de doublon — indispensable pour les reprises réseau et les retries mobile money.

HEADER
Idempotency-Key: 8f14e45f-ceea-467d-9c1b-2a…

La clé est conservée 24 h. Utilisez un UUID unique par intention de paiement.

Montants & devises

Les montants sont représentés par un objet Money — une devise et une valeur entière. XAF et XOF sont des devises sans sous-unité : la valeur est exprimée en francs entiers (pas de centimes).

Money
{ "currency": "XAF", "value": 24500 }   // 24 500 FCFA

Modèle de fonds

Comprendre où l'argent se trouve à chaque instant est le préalable à toute intégration.

Topologie des wallets

Deux wallets sont créés à l'ouverture d'une plateforme sur les verticales à tiers — marketplace, freelance, crowdfunding, cagnottes, voyage, B2B. En vente directe (e-commerce), seul le wallet principal existe.

WalletContientQui le débite
WLT-MAINVos commissionsVous, vers vos comptes de règlement
WLT-BENEFLes fonds appartenant à vos tiersVous, vers le compte du tiers uniquement
WLT-SM-xxxxLe solde d'un tiers vérifiéReversement vers son propre compte
Les wallets individuels se créent seuls. Un wallet est ouvert automatiquement à la validation KYC d'un titulaire — jamais avant, et sans appel de votre part. Écoutez wallet.created plutôt que d'appeler POST /wallets.

Ségrégation

Un retrait depuis WLT-MAIN ne peut aller que vers un compte de règlement de la plateforme. Un reversement depuis un wallet de tiers ne peut aller que vers le compte de ce tiers. L'API refuse toute combinaison croisée — un propriétaire de marketplace ne peut pas vider le wallet de ses vendeurs vers son propre compte.

Cycle d'un retrait

Séquence
POST /withdrawals            → status: PENDING_APPROVAL
  ↓  (validation interne)
withdrawal.approved          → les fonds ne sont pas encore partis
  ↓  (exécution par l'opérateur)
withdrawal.paid              → le solde du wallet est décrémenté ici
      ou
withdrawal.failed            → le solde reste intact, la demande est rejouable
Ne décrémentez pas votre propre compteur sur `approved`. Le solde ne bouge qu'à paid. Un versement validé puis échoué chez l'opérateur laisse les fonds en place.

Encaissements espèces

Les déclarations d'espèces sont des données du marchand : aucun wallet n'est crédité, aucun flux n'est réconciliable en banque. Elles sont conservées et exportables, mais n'entrent pas dans le calcul des soldes.

Erreurs

L'API utilise les codes HTTP conventionnels et renvoie un objet d'erreur structuré.

422 · JSON
{
  "type": "invalid_request",
  "code": "insufficient_kyc",
  "message": "Le compte marchand doit être vérifié pour encaisser en production."
}
CodeSignification
400 / 422Requête invalide ou règle métier non respectée
401Clé d'API manquante ou invalide
402Paiement refusé (opérateur indisponible, plafond…)
403Action non autorisée (flux gelés, KYC)
409Conflit — clé d'idempotence rejouée avec un corps différent
429Trop de requêtes (rate limit)

Plugins & modules e-commerce

Encaissez sans écrire de code : modules officiels maintenus par BaobaPay. Chaque module installe le checkout hébergé (momo, carte, lien) et la réconciliation automatique des commandes via webhooks.

WooCommerce
WordPress · installation en 5 min · baobapay-woocommerce.zip
PrestaShop
Module officiel · 1.7+ · baobapay-prestashop.zip
Shopify
App Store · paiement hébergé redirigé

Configuration commune : coller la clé API publique + l'URL de webhook générée par le module. Le statut de commande passe automatiquement à « payée » sur payin.succeeded. Sandbox supporté dans chaque module.

Endpoints

Les ressources d'encaissement de l'API agrégateur.

MéthodeEndpointUsage
POST/payins/mobile-moneyEncaisser par mobile money (Orange, MTN, Express Union…)
POST/payins/cardEncaisser par carte (avec 3-DS/SCA)
POST/payins/recurringPaiement récurrent (sur enregistrement / mandat)
GET/payins/{payInId}Récupérer un encaissement — statut, méthode, wallet crédité
POST/payins/recurring/registrationsCréer un mandat de paiement récurrent
POST/preauthorizationsEmpreinte / caution carte (capture différée)bientôt
POST/payment-linksCréer un lien de paiement / QR hébergé
POST/refundsRembourser un encaissement
GET/transactionsJournal unifié des encaissements (natures PAYIN / REFUND)
GET/reportsÉtats et exports de réconciliation
POST/developers/webhooksGérer les endpoints de webhook
GET/developers/api-keysLister les clés API — métadonnées seulement, jamais la valeur
Titulaires & vérification
POST/usersCréer un titulaire (vendeur, bénéficiaire, payeur)
GET/usersLister les titulaires
GET/users/{userId}Récupérer un titulaire et son niveau KYC
POST/users/{userId}/kyc/documentsSoumettre une pièce KYC/KYB
Wallets
GET/walletsLister les wallets — principal, bénéficiaires, individuels
POST/walletsCréer un wallet (automatisation ; la création se fait normalement à la validation KYC)
GET/wallets/{walletId}Consulter un wallet — solde total, engagé, disponible
GET/wallets/{walletId}/transactionsMouvements d'un wallet — pagination par curseur
Sortie des fonds
GET/settlement-accountsLister les comptes de règlement du marchand
POST/settlement-accountsDéclarer un compte de règlement — authentification renforcée exigée
PATCH/settlement-accounts/{settlementAccountId}Modifier un compte — authentification renforcée exigée
POST/settlement-accounts/{settlementAccountId}/verifyRelancer la vérification du titulaire
GET/withdrawalsLister les demandes de retrait
POST/withdrawalsDemander un retrait vers un compte de règlement vérifié
DELETE/withdrawals/{withdrawalId}Annuler une demande encore en attente de validation
Espèces, litiges & contexte
GET/cash-declarationsLister les encaissements espèces déclarés
POST/cash-declarationsDéclarer un encaissement espèces — aucun wallet crédité
POST/cash-declarations/{id}/refundDéclarer un remboursement en espèces
GET/disputesLister les litiges
GET/disputes/{disputeId}Récupérer un litige et ses pièces
GET/operator-enrollmentsLister les enrôlements opérateurs
GET/operator-enrollments/{enrollmentId}Consulter un enrôlement
GET/me/platformsPlateformes accessibles à l'utilisateur
POST/session/active-platformBasculer le contexte de session — authentification renforcée à la première ouverture
GET/platformConfiguration de la plateforme — verticale, fonctionnalités, zone

Webhooks

BaobaPay notifie votre serveur des événements asynchrones (confirmation d'un paiement, changement KYC, litige…). Chaque livraison est signée et rejouée en cas d'échec (backoff jusqu'à 24 h).

Vérifier la signature

Chaque requête porte un en-tête BaobaPay-Signature — un HMAC-SHA256 du corps brut, calculé avec le secret de votre endpoint. Comparez-le avant de traiter l'événement.

Node.js
const sig = req.headers['baobapay-signature'];
const expected = crypto
  .createHmac('sha256', endpointSecret)
  .update(rawBody)
  .digest('hex');
if (sig !== expected) return res.status(400).end();
// signature valide → traiter l'événement

Catalogue des événements

ÉvénementDéclenché quand…
EVENT payin.succeededun encaissement est confirmé
EVENT payin.failedun encaissement échoue (refus, délai, solde)
EVENT refund.doneun remboursement est exécuté
EVENT kyc.updatedle statut de vérification d'un client change
EVENT kyb.updatedle statut KYB d'une entreprise change
EVENT dispute.openedune contestation / un rejet est ouvert
EVENT dispute.closedune contestation est clôturée (issue connue)
EVENT user.blockedla conformité suspend un titulaire — il ne peut plus encaisser ni être payé
EVENT user.unblockedla suspension d'un titulaire est levée
EVENT user.closedun titulaire est clôturé (données conservées selon la rétention légale)
Wallets
EVENT wallet.createdun wallet est créé — automatiquement à la validation KYC d'un titulaire
EVENT wallet.balance_updatedle solde d'un wallet change — encaissement, reversement, retrait
EVENT wallet.frozenun wallet est gelé par la conformité
Retraits
EVENT withdrawal.approvedune demande de retrait est validée — les fonds ne sont pas encore partis
EVENT withdrawal.paidle retrait est confirmé par l'opérateur — le solde est décrémenté à ce moment
EVENT withdrawal.rejectedla demande est refusée avant exécution
EVENT withdrawal.failedl'opérateur n'a pas pu exécuter le versement — numéro invalide, compte clos
Comptes de règlement
EVENT settlement_account.verifiedun compte de règlement est vérifié — il devient utilisable après le délai de sécurité
EVENT settlement_account.rejectedun compte de règlement est refusé — titulaire non conforme au KYB
Espèces, opérateurs & conditions
EVENT cash.declaredun encaissement espèces est déclaré — aucun wallet n'est crédité
EVENT cash.refund_declaredun remboursement en espèces est déclaré
EVENT operator_enrollment.updatedle statut d'un enrôlement opérateur change
EVENT terms.publishedde nouvelles conditions générales sont publiées
L'ordre d'arrivée n'est pas garanti. withdrawal.paid peut précéder withdrawal.approved. Traitez chaque événement comme un fait indépendant, et fiez-vous à l'horodatage du corps plutôt qu'à l'ordre de réception. Les livraisons sont réessayées : votre endpoint doit être idempotent sur event.id.
Exemple d'événement
{
  "type": "payin.succeeded",
  "created": 1752931320,
  "data": {
    "id": "pi_10482",
    "amount": { "currency": "XAF", "value": 24500 },
    "operator": "MTN"
  }
}

Sandbox & tests

La sandbox reproduit l'intégralité de l'API sans argent réel. Déclenchez des succès et des échecs de façon déterministe.

Cartes de test

NuméroRésultat
4970 1000 0000 0055Paiement réussi (Visa)
4000 0000 0000 0002Carte refusée
4000 0000 0000 9995Solde insuffisant

Mobile money de test

  • +237 6 71 22 41 88 → confirmation réussie
  • +237 6 00 00 00 00 → délai dépassé (non confirmé)
Depuis le tableau de bord → Kit sandbox, vous pouvez émettre des événements webhook à la demande pour tester votre intégration.