Docs API

Démarrer

Mode test

Chaque compte MivaaPay dispose d'un bac à sable complet. Vous y créez des paiements, recevez de vrais webhooks signés et exercez toute votre intégration — sans qu'un centime ne bouge.

Deux jeux de clés

Le mode n'est pas un paramètre de la requête : il est porté par la clé. Votre tableau de bord vous en donne deux, reconnaissables au préfixe :

CléEffet
sk_test_… Bac à sable. Aucun opérateur n'est contacté, aucun argent ne circule.
sk_… Production. Le client est réellement débité.

Basculer de l'un à l'autre ne demande donc aucun changement de code : une variable d'environnement suffit. C'est le point important — ce que vous avez validé en test est exactement ce qui partira en production.

curl -X POST https://api.mivaapay.com/api/v1/payments \
  -H "Authorization: Bearer sk_test_VOTRE_CLE" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 5000, "operator": "mtn", "phone_number": "22997000000" }'
// MIVAAPAY_SECRET_KEY=sk_test_… en recette, sk_… en production.
// Le code, lui, est identique dans les deux environnements.
$payment = Http::withToken(env('MIVAAPAY_SECRET_KEY'))
    ->post('https://api.mivaapay.com/api/v1/payments', $payload)
    ->json('data');
// MIVAAPAY_SECRET_KEY=sk_test_… en recette, sk_… en production.
const res = await fetch('https://api.mivaapay.com/api/v1/payments', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.MIVAAPAY_SECRET_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(payload)
});
Utilisable avant la validation de votre compte

Les clés de test fonctionnent même tant que votre KYC est en cours. Vous pouvez donc intégrer et valider pendant l'instruction du dossier, et n'attendre l'activation que pour le passage en production.

Déclencher un scénario

En Mobile Money, l'issue est décidée par les quatre derniers chiffres du numéro payeur. C'est déterministe : le même numéro donne toujours le même résultat, ce qui rend vos tests automatisés reproductibles.

Numéro se terminant parCe qui se passeStatut final
0000 Le client valide aussitôt. succeeded
0001 L'opérateur refuse dès l'initiation : POST /payments répond 402 payment_failed. failed
0002 Le client ne valide jamais. Utile pour tester vos délais d'attente. pending indéfiniment
0003 Le client refuse sur son téléphone après l'envoi de la demande. failed
tout autre numéro Cas nominal : en attente, puis validé au bout de 10 secondes. succeeded
Exemples de numéros
22997000000   # succès immédiat
22997000001   # refus de l'opérateur
22997000002   # jamais validé
22997000003   # refus du client
22991234567   # succès après 10 secondes (le cas le plus réaliste)
La réponse à la création reste toujours pending

Même pour le scénario à succès immédiat. C'est volontaire : en production, l'état n'est jamais connu au moment de la réponse. Si votre code livrait la commande sur la réponse à POST /payments, le mode test ne vous laisserait pas croire que ça marche. L'issue arrive par webhook, ou par GET /payments/{reference}.

Paiement par carte

Un paiement carte en mode test renvoie une payment_url comme en production, mais elle mène à une page hébergée par MivaaPay où vous choisissez l'issue : deux boutons, « paiement réussi » ou « refus ». Le client est ensuite redirigé vers votre return_url, avec reference et status en paramètres.

Cela permet d'exercer le parcours de redirection en entier, y compris le cas que l'on oublie le plus souvent : fermer l'onglet sans payer.

Remboursements de test

En production, un remboursement est une demande qui passe par un contrôle back-office. En mode test, il est résolu immédiatement : POST /payments/{reference}/refund rend un remboursement déjà succeeded, et vous recevez dans la foulée les webhooks refund.succeeded puis payment.refunded. Vous testez donc la séquence complète sans attendre une validation humaine.

Webhooks

Les webhooks du bac à sable partent sur le même endpoint que les vrais, avec la même signature HMAC — c'est ce qui vous permet de valider votre vérification de signature avant la production. Ils se distinguent par le champ livemode :

Webhook émis en mode test
{
  "id": "5c8b8a1e-1f0e-4c0a-9a3e-6f8c2b1d4e77",
  "event": "payment.succeeded",
  "livemode": false,
  "created_at": "2026-08-05T10:25:02+00:00",
  "data": { "reference": "…", "status": "succeeded", "amount": 5000 }
}

Chaque environnement a son propre endpoint et son propre secret. PUT /webhooks/endpoint configure celui du mode de la clé employée : avec une clé de test, vous ne pouvez ni détourner ni faire taire vos notifications de production. Vous avez donc deux URL à renseigner, une par clé, et deux secrets à stocker.

Si vous préférez malgré tout un endpoint unique, testez livemode à la réception — c'est ce qui évite qu'une commande fictive parte en préparation.

Ce qui est cloisonné

Les deux environnements ne se voient pas, et les paiements de test ne touchent à rien de réel :

GarantieDétail
Listes séparées Une clé de test ne voit que des ressources de test, une clé live que des ressources live. Une référence de l'autre environnement renvoie not_found.
Aucun encaissement Un paiement de test ne crédite jamais votre solde réel.
Aucun reversement Les paiements de test ne comptent dans aucun seuil et ne peuvent pas être reversés.
Idempotence par environnement Un external_id utilisé en test reste disponible en live. Vous pouvez rejouer vos commandes de recette en production.

Solde en mode test

GET /balance avec une clé de test ne renvoie pas votre vrai solde — ce serait exposer une donnée financière réelle dans un environnement de test. Il renvoie la somme de vos encaissements de test, ce qui vous laisse un chiffre cohérent à voir bouger.

Ce qui n'existe pas en test

Liens de paiement

POST /payment-links est refusé avec une clé de test. Un lien mène à une page que le client ouvre sans clé API : rien, à son ouverture, ne permettrait de savoir qu'elle doit simuler plutôt qu'encaisser. Testez vos encaissements avec POST /payments, puis créez vos liens avec votre clé live.

Passer en production

Avant de remplacer sk_test_… par votre clé live, vérifiez que :

  • votre compte marchand est actif (KYC validé) ;
  • votre endpoint webhook de production est configuré et vérifie la signature ;
  • votre code ne livre jamais sur la réponse à POST /payments, mais sur succeeded ;
  • votre traitement de webhook est idempotent (voir Rejeux et idempotence) ;
  • vous envoyez un external_id sur chaque paiement, pour ne jamais débiter deux fois ;
  • vous avez testé les quatre scénarios, pas seulement le cas passant.
Le premier paiement live reste un vrai paiement

Faites-le avec un petit montant, sur un numéro qui vous appartient. Le bac à sable simule nos règles, pas celles de l'opérateur : plafonds du compte du client, réseau, délais de validation réels ne s'observent qu'en production.