Docs API

Référence

Référence API

Les 16 endpoints de l'API marchand v1. Tous sont authentifiés par Authorization: Bearer <secret_key> et relatifs à https://api.mivaapay.com/api/v1.

EndpointObjet
POST /paymentsCréer un paiement
GET /paymentsLister les paiements
GET /payments/{reference}Récupérer un paiement
POST /payments/{reference}/refundRembourser un paiement
GET /refundsLister les remboursements
GET /refunds/{reference}Récupérer un remboursement
POST /payment-linksCréer un lien de paiement
GET /payment-linksLister les liens de paiement
GET /payment-links/{id}Récupérer un lien de paiement
GET /payment-modesLister les moyens de paiement
POST /payment-modes/feesSimuler les frais
GET /balanceSolde du portefeuille
GET /webhooks/endpointConsulter l'endpoint webhook
PUT /webhooks/endpointConfigurer l'endpoint webhook
POST /webhooks/testEnvoyer un webhook de test
GET /webhooks/deliveriesJournal des envois

Paiements

Créer un paiement

POST /api/v1/payments

Initie un encaissement. En Mobile Money, le client reçoit une demande de validation sur son téléphone et le paiement reste pending. En carte, la réponse porte une payment_url vers laquelle rediriger le client.

Corps de la requête

ChampTypeRequisDescription
amountnombreouiMontant que vous encaissez, en unité principale. De 1 à 99 999 999.
currencychaînenonXOF (défaut), XAF, EUR, USD.
operatorchaîneoui*Code opérateur, ex. mtn. *Requis sans payment_mode_id. 32 caractères max.
payment_mode_identieroui*Identifiant du catalogue. *Requis sans operator.
phone_numberchaîneoui†8 à 15 chiffres, indicatif compris, + toléré. †Obligatoire en Mobile Money.
otpchaînenonCode à usage unique, obligatoire pour les opérateurs à OTP. 16 caractères max.
descriptionchaînenonLibellé de la commande. 255 caractères max. Défaut : « Paiement API ».
external_idchaînenonVotre identifiant de commande. Unique par marchand : rend l'appel idempotent. 100 caractères max.
return_urlURLnonPage de retour après paiement. Requise par free_sn, utile en carte.
callback_urlURLnonEndpoint webhook pour ce paiement. Prioritaire sur l'endpoint du compte.
metadataobjetnonDonnées libres, restituées telles quelles dans les réponses et les webhooks.
customerobjetnonIdentité du payeur. Obligatoire en carte : firstname, lastname, email.
customer.firstnamechaînenon100 caractères max.
customer.lastnamechaînenon100 caractères max.
customer.emaile-mailnon150 caractères max. Sert de clé de rapprochement de vos clients.
customer.addresschaînenon255 caractères max.
customer.countrychaînenon60 caractères max. Utilisé en carte ; défaut Benin.
curl -X POST https://api.mivaapay.com/api/v1/payments \
  -H "Authorization: Bearer $MIVAAPAY_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 5000, "operator": "mtn", "phone_number": "22997000000", "external_id": "cmd_1042" }'
{
  "amount": 5000,
  "currency": "XOF",
  "operator": "mtn",
  "phone_number": "22997000000",
  "description": "Commande #1042",
  "external_id": "cmd_1042",
  "callback_url": "https://boutique.example.com/webhooks/mivaapay",
  "customer": { "firstname": "Awa", "lastname": "Diallo", "email": "awa@example.com" },
  "metadata": { "panier": "1042" }
}

Réponse — 201 Created

Un objet paiement. payment_url n'est renseignée qu'en carte.

Erreurs

CodeCas
invalid_requestChamp manquant ou invalide ; opérateur inconnu ou inactif ; total débité hors des bornes opérateur (10 à 1 000 000) ; OTP ou identité du porteur manquants.
payment_failedL'opérateur a refusé l'initiation. Le paiement est enregistré en failed.
Idempotence

Renvoyer un external_id déjà utilisé retourne le paiement existant — avec le statut 201 également — au lieu d'en créer un second.

Lister les paiements

GET /api/v1/payments

Liste paginée des paiements de votre compte, du plus récent au plus ancien.

Paramètres de requête

ParamètreDescription
statuspending, succeeded, failed ou refunded. Une valeur inconnue est traitée comme pending.
external_idFiltre sur votre identifiant de commande, en correspondance exacte.
fromDate de début, comparée à created_at. Format YYYY-MM-DD ou ISO-8601.
toDate de fin, comparée à created_at.
per_page1 à 100. Défaut 25.
pageNuméro de page, à partir de 1.
Exemple
curl -G https://api.mivaapay.com/api/v1/payments \
  -H "Authorization: Bearer $MIVAAPAY_SECRET_KEY" \
  --data-urlencode "status=succeeded" \
  --data-urlencode "from=2026-08-01" \
  --data-urlencode "per_page=100"

Réponse — 200

Un tableau d'objets paiement dans data, les compteurs dans meta.

Attention à to

to=2026-08-31 est interprété comme 2026-08-31 00:00:00 : les paiements de la journée du 31 sont exclus. Passez la date du lendemain, ou un horodatage complet, pour inclure toute la journée.

Récupérer un paiement

GET /api/v1/payments/{reference}

Renvoie un paiement. {reference} accepte la référence MivaaPay ou votre external_id.

Si le paiement est encore pending, son état est redemandé à l'opérateur avant la réponse : cet appel est donc plus lent qu'une simple lecture. Prévoyez un timeout d'au moins 30 secondes.

Exemple
curl https://api.mivaapay.com/api/v1/payments/cmd_1042 \
  -H "Authorization: Bearer $MIVAAPAY_SECRET_KEY"

Réponse — 200

Un objet paiement. payment_url y vaut toujours null : elle n'est renvoyée qu'à la création.

Erreurs

not_found si aucun paiement de votre compte ne porte cette référence.

Remboursements

Rembourser un paiement

POST /api/v1/payments/{reference}/refund

Enregistre une demande de remboursement intégral. Le décaissement est exécuté ensuite par MivaaPay ; suivez son issue par les webhooks refund.*. {reference} accepte la référence MivaaPay ou votre external_id.

Corps de la requête

ChampTypeRequisDescription
reasonchaînenonMotif, visible en back-office. 255 caractères max. Défaut : « Remboursement demandé par le marchand ».

Réponse — 201 Created

Un objet remboursement au statut pending. amount vaut le total_amount du paiement : votre montant plus la commission, c'est-à-dire ce que le client avait réellement payé.

Erreurs

CodeCas
not_foundAucun paiement de votre compte ne porte cette référence.
conflictLe paiement n'est pas succeeded, ou un remboursement existe déjà.

Lister les remboursements

GET /api/v1/refunds

Liste paginée de vos remboursements, du plus récent au plus ancien.

Paramètres de requête

ParamètreDescription
per_page1 à 100. Défaut 25.
pageNuméro de page, à partir de 1.

Réponse — 200

Un tableau d'objets remboursement.

Récupérer un remboursement

GET /api/v1/refunds/{reference}

Renvoie un remboursement. {reference} est ici la référence du remboursement, pas celle du paiement : c'est la valeur renvoyée par POST /payments/{reference}/refund.

Si le décaissement est en cours chez l'opérateur, son état est rafraîchi avant la réponse.

Réponse — 200

Un objet remboursement.

Erreurs

not_found si aucun remboursement de votre compte ne porte cette référence.

Liens de paiement

Créer un lien de paiement

POST /api/v1/payment-links

Crée une page de paiement hébergée, à montant et libellé fixés.

Corps de la requête

ChampTypeRequisDescription
amountnombreouiDe 1 à 99 999 999.
descriptionchaîneouiAffichée au client. 255 caractères max.
typechaînenonpermanent (défaut) ou limité — avec l'accent, en UTF-8.
max_usesentieroui**Requis si type vaut limité. Minimum 1.
starts_atdatenonOuverture du lien, ISO-8601.
expires_atdatenonFermeture, postérieure à starts_at.

Réponse — 201 Created

Un objet lien de paiement, dont le champ url est l'adresse à partager.

Lister les liens de paiement

GET /api/v1/payment-links

Liste paginée de vos liens, du plus récent au plus ancien. Paramètres : per_page (1 à 100, défaut 25) et page.

Récupérer un lien de paiement

GET /api/v1/payment-links/{id}

Renvoie un lien par son id numérique — celui de la création, pas le fragment aléatoire de l'URL publique. not_found s'il n'appartient pas à votre compte.

Moyens de paiement

Lister les moyens de paiement

GET /api/v1/payment-modes

Catalogue des moyens de paiement actifs, avec les frais qui vous sont appliqués. Cette réponse n'est pas paginée.

Paramètres de requête

ParamètreDescription
countryCode pays ISO-2, ex. CI. Insensible à la casse.
typemobile_money, card, bank_transfer ou e_wallet.

Réponse — 200

Un tableau d'objets moyen de paiement.

Simuler les frais

POST /api/v1/payment-modes/fees

Calcule la commission et le total qui sera débité au client, sans rien créer. À appeler avant d'afficher un prix.

Corps de la requête

ChampTypeRequisDescription
amountnombreouiMontant que vous encaissez. De 1 à 99 999 999.
operatorchaîneoui**Requis sans payment_mode_id.
payment_mode_identieroui**Requis sans operator.

Réponse — 200

data
{
  "payment_mode_id": 1,
  "operator": "mtn",
  "amount": 5000,
  "fee_amount": 90,
  "total_amount": 5090,
  "fee_percent": 1.8,
  "fee_fixed": 0
}

La commission vaut amount × fee_percent / 100 + fee_fixed, arrondie au centième. total_amount est ce que le client verra sur son téléphone.

Erreurs

invalid_request si le moyen de paiement est introuvable ou inactif.

Portefeuille

Solde du portefeuille

GET /api/v1/balance

Solde disponible : les encaissements validés, moins les reversements déjà effectués.

Avec une clé sk_test_…, cet endpoint ne renvoie pas votre solde réel mais la somme de vos encaissements de test : exposer une donnée financière réelle dans le bac à sable serait une fuite.

Paramètres de requête

ParamètreDescription
currencyDevise du portefeuille. Défaut XOF. Insensible à la casse.

Réponse — 200

data
{
  "currency": "XOF",
  "balance": 12845,
  "balance_minor": 1284500,
  "status": "Actif",
  "updated_at": "2026-08-05T09:41:02+00:00"
}
ChampDescription
balanceSolde en unité principale — c'est la valeur à afficher, ici 12 845 XOF.
balance_minorLe même solde en entier, multiplié par 100. La comptabilité interne travaille en entiers pour éviter les arrondis : le facteur 100 est appliqué à toutes les devises, y compris le XOF qui n'a pourtant pas de subdivision. Utilisez-le si vous voulez éviter les flottants, mais ne l'affichez pas tel quel.
statusÉtat du portefeuille : Actif, Gelé ou Fermé. Hors de l'état actif, aucun mouvement n'est accepté sur le portefeuille.
updated_atDernier mouvement, en ISO-8601.

Webhooks

Consulter l'endpoint webhook

GET /api/v1/webhooks/endpoint

Renvoie l'URL configurée, le secret de signature et la liste des événements émis, pour l'environnement de la clé employée. livemode rappelle de quel environnement il s'agit : la configuration de test et celle de production sont indépendantes.

data
{
  "url": "https://boutique.example.com/webhooks/mivaapay",
  "secret": "whsec_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "events": [
    "payment.succeeded",
    "payment.failed",
    "payment.refunded",
    "refund.succeeded",
    "refund.failed"
  ],
  "livemode": true
}

Sans endpoint configuré, url et secret valent null.

Configurer l'endpoint webhook

PUT /api/v1/webhooks/endpoint

Définit l'URL de notification du compte, ou la supprime.

Corps de la requête

ChampTypeRequisDescription
urlURL ou nullouiURL HTTPS de notification, 255 caractères max. null désactive les webhooks. Le champ doit être présent, même à null.
rotate_secretbooléennontrue régénère le secret de signature. L'ancien cesse aussitôt d'être valide.

Réponse — 200

{ "url": …, "secret": …, "livemode": … }. Un secret est créé automatiquement à la première configuration d'une URL. L'écriture ne porte que sur l'environnement de la clé : une clé de test ne peut pas modifier la configuration de production.

Envoyer un webhook de test

POST /api/v1/webhooks/test

Envoie un événement webhook.test sur l'endpoint configuré, signé comme un vrai. Aucun corps de requête.

data — endpoint configuré
{
  "sent": true,
  "delivery_id": "1f0c9d3a-4f11-9c22-7d5a-1e0b3c68b71e",
  "url": "https://boutique.example.com/webhooks/mivaapay"
}

Sans endpoint configuré, la réponse reste un 200 mais porte "sent": false et le motif dans message.

Journal des envois

GET /api/v1/webhooks/deliveries

Historique paginé des notifications envoyées, du plus récent au plus ancien, limité à l'environnement de la clé employée. Paramètres : per_page (1 à 100, défaut 25) et page.

ChampDescription
idIdentifiant de la livraison, identique au champ id du corps envoyé et à l'en-tête X-MivaaPay-Delivery.
eventNom de l'événement.
urlEndpoint appelé.
statuspending, success ou failed.
attemptsNombre de tentatives effectuées, jusqu'à 5.
response_codeStatut HTTP renvoyé par votre serveur, null s'il était injoignable.
created_atDate de mise en file, en ISO-8601.
delivered_atDate de livraison réussie, null sinon.

Objets

Les objets ci-dessous sont ceux que renvoie l'API. Ce sont exactement les mêmes structures que celles envoyées dans le champ data des webhooks.

Objet paiement

ChampTypeDescription
referenceUUIDIdentifiant MivaaPay du paiement. C'est la clé à conserver.
identifierchaîneNuméro lisible séquentiel, ex. Id2841. Confort d'affichage uniquement.
external_idchaîneVotre identifiant de commande, null si non fourni.
statuschaînepending, succeeded, failed ou refunded.
amountnombreCe que vous encaissez.
fee_amountnombreCommission MivaaPay, à la charge du client.
total_amountnombreCe que le client est réellement débité.
currencychaîneDevise du paiement.
descriptionchaîneLibellé de la commande.
payment_modeobjetLe moyen de paiement utilisé, null si indisponible.
customerobjet{ firstname, lastname, email, phone_number }, valeurs à null si non fournies.
payment_urlURLPage de paiement hébergée. Renseignée uniquement dans la réponse à la création d'un paiement carte ; null partout ailleurs.
metadataobjetCe que vous aviez envoyé, tel quel. null si absent.
created_atdateISO-8601.
updated_atdateISO-8601. Change au passage de statut.

Objet remboursement

ChampTypeDescription
referenceUUIDIdentifiant du remboursement.
payment_referenceUUIDRéférence du paiement remboursé.
amountnombreMontant rendu au client : le total_amount du paiement.
currencychaîneDevise du paiement d'origine.
statuschaînepending, succeeded ou failed.
reasonchaîneMotif enregistré.
processed_atdateDate d'exécution, null tant que le remboursement est en attente.
created_atdateDate de la demande, ISO-8601.

Objet lien de paiement

ChampTypeDescription
identierIdentifiant du lien, à passer à GET /payment-links/{id}.
urlURLAdresse publique à partager.
typechaînepermanent ou limité.
amountnombreMontant que vous encaissez.
descriptionchaîneLibellé affiché au client.
max_usesentierNombre d'utilisations autorisées, null pour un lien permanent.
uses_countentierUtilisations déjà consommées.
uses_leftentierUtilisations restantes, null pour un lien permanent.
starts_atdateOuverture, null si non définie.
expires_atdateFermeture, null si non définie.
created_atdateISO-8601.

Objet moyen de paiement

ChampTypeDescription
identierIdentifiant, utilisable comme payment_mode_id.
namechaîneNom affichable, ex. « MTN Bénin ».
typechaînemobile_money, card, bank_transfer, e_wallet ou other.
operatorchaîneCode opérateur, utilisable comme operator.
countrychaîneNom du pays, null si inconnu.
country_codechaîneCode ISO-2, null si inconnu.
fee_percentnombrePart proportionnelle de la commission, en pourcentage.
fee_fixednombrePart fixe de la commission.
requires_otpbooléentrue si le champ otp est obligatoire à la création du paiement.
logoURLLogo de l'opérateur, null si absent.