MTPay

Intégrez MTPay

Créez des liens de paiement Mobile Money depuis votre serveur, recevez un webhook signé dès que le client paie, livrez automatiquement.

Base URL
mt-pay.vercel.app
Version
v1
Format
JSON

En 3 etapes

Demarrage

  1. 1

    Obtenez votre cle

    Depuis le dashboard, demandez un acces API. Un admin valide et vous recevez votre cle mt_live_… et votre secret webhook.

  2. 2

    Creez un lien

    Un appel POST /api/v1/payment-links renvoie une paymentUrl a envoyer au client.

  3. 3

    Ecoutez le webhook

    MTPay appelle votre URL des que le paiement aboutit. Verifiez la signature, puis livrez.

Claude Code, Cursor, Copilot...

Integrer avec une IA

Ouvrez votre projet dans Claude Code, Cursor ou un autre agent de codage, collez l'un de ces prompts tel quel. Chacun porte le contrat exact de l'endpoint concerne, l'agent n'a rien a deviner ni a inventer.

Ajouter un paiement

Prompt
Je veux integrer MTPay (paiement Mobile Money) dans ce projet.

Documentation : https://mt-pay.vercel.app/docs

Ce que je veux :
1. Un appel serveur POST https://mt-pay.vercel.app/api/v1/payment-links pour creer
   un lien de paiement des qu'un client valide sa commande. Header
   Authorization: Bearer <MTPAY_API_KEY> (variable d'env, jamais en dur).
   Body JSON : { clientName, clientPhone, amount, service, country,
   returnUrl }. returnUrl = l'URL de CE projet vers laquelle MTPay
   redirige le client une fois le paiement confirme (bouton immediat +
   retour automatique apres quelques secondes) — sans elle le client
   reste bloque sur la page de recu MTPay sans jamais revenir ici.
2. Rediriger le client vers la paymentUrl renvoyee par l'API
   (data.paymentUrl dans la reponse 201).
3. Une route webhook POST qui recoit les confirmations de paiement MTPay
   (voir la section Webhook de la doc), verifie la signature HMAC SHA-256
   avec MTPAY_WEBHOOK_SECRET avant de faire quoi que ce soit, puis marque
   la commande payee quand event === "payment.completed".
4. Gere aussi payment.failed et payment.expired (commande a rouvrir /
   ne pas livrer), et payment.refunded (paiement rembourse au client :
   annuler la commande).

Adapte les noms de fonctions/fichiers aux conventions deja utilisees dans
ce projet plutot que d'imposer une structure nouvelle.

Juste le webhook

Prompt
Ajoute a ce projet une route qui recoit les webhooks MTPay
(paiements Mobile Money).

Contrat exact (doc complete : https://mt-pay.vercel.app/docs#webhook) :
- MTPay envoie un POST avec le corps BRUT (ne pas re-serialiser avant de
  verifier la signature, sinon le HMAC ne correspond plus)
- En-tetes : X-MTPay-Event (payment.completed | payment.failed |
  payment.expired | payment.refunded) et X-MTPay-Signature
  (format "sha256=<hex>")
- payment.refunded : le paiement a ete rembourse au client par MTPay
  (status "REFUNDED") — annuler la commande / retirer l'acces
- Verification : HMAC SHA-256 du corps brut avec MTPAY_WEBHOOK_SECRET,
  comparaison en temps constant (pas de === naif sur les secrets)
- Payload JSON : { event, paymentId, amount, currency, status, customer:
  { name, phone }, failure? }
- failure (payment.failed / payment.expired, si l'operateur a donne un
  motif) : { code, message }, ex. code "INSUFFICIENT_BALANCE" — a
  afficher au client pour qu'il sache quoi corriger
- Toujours repondre 200 rapidement si la signature est valide, meme si
  le paymentId est inconnu (MTPay reessaie sinon)
- Repondre 401/403 uniquement si la signature ne correspond pas

Le paymentId correspond a l'id du lien cree via POST /api/v1/payment-links :
utilise-le pour retrouver la commande correspondante dans ce projet.

Afficher le solde

Prompt
Ajoute a ce projet un affichage du solde MTPay disponible
(paiements Mobile Money encaisses).

Appel serveur : GET https://mt-pay.vercel.app/api/v1/balance
Header : Authorization: Bearer <MTPAY_API_KEY>

Reponse (200) :
{
  "data": {
    "currency": "XOF",
    "availableBalance": 125000,
    "totalCollected": 340000,
    "totalWithdrawn": 200000,
    "pendingWithdrawalAmount": 15000,
    "counts": { "links": 42, "completed": 31, "pendingWithdrawals": 1 }
  }
}

Affiche availableBalance formate dans la devise (currency), avec
totalCollected et counts.completed en sous-texte. Cache le montant
derriere l'authentification existante de ce projet (jamais accessible
sans etre connecte a l'admin/au compte).

Variables d'environnement

Donnez a l'agent le nom des variables (MTPAY_API_KEY, MTPAY_WEBHOOK_SECRET) plutot que leur valeur. Collez les vraies valeurs vous-meme dans votre fichier .env, jamais dans le prompt.

Toutes les requetes

Authentification

Chaque appel serveur porte votre cle API dans l'en-tete Authorization. Gardez-la cote serveur : elle ne doit jamais apparaitre dans du code client.

En-tetes
Authorization: Bearer mt_live_xxxxxxxxxx
Content-Type: application/json

Cle exposee ?

Revoquez-la depuis le dashboard et generez-en une nouvelle. Une cle fuitee permet de creer des liens en votre nom.

Endpoint

Creer un lien de paiement

POST/api/v1/payment-links
curl -X POST https://mt-pay.vercel.app/api/v1/payment-links \
  -H "Authorization: Bearer mt_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "clientName": "Awa Konate",
    "clientPhone": "+22997000000",
    "amount": 15000,
    "service": "Commande boutique",
    "country": "BEN"
  }'

Corps de la requete

clientNamestringrequis

Nom du client affiche sur la page de paiement.

clientPhonestringrequis

Numero Mobile Money au format international.

amountnumberrequis

Montant a encaisser, en unite entiere de la devise.

servicestringrequis

Libelle de ce qui est vendu.

countrystringoptionnel

Code ISO-3 du pays. Par defaut : celui du marchand.

currencystringoptionnel

Par defaut : la devise du pays (XOF, XAF, CDF...).

descriptionstringoptionnel

Detail libre affiche au client.

returnUrlstringoptionnel

URL de votre site vers laquelle rediriger le client apres paiement reussi (bouton immediat + retour automatique apres quelques secondes). Sans elle, le client reste sur la page de recu MTPay.

Reponse 201

Reponse
{
  "data": {
    "id": "V1StGXR8Z5",
    "clientName": "Awa Konate",
    "clientPhone": "+22997000000",
    "amount": 15000,
    "currency": "XOF",
    "service": "Commande boutique",
    "description": "",
    "status": "PENDING",
    "createdAt": "2026-08-11T09:24:00.000Z",
    "country": "BEN",
    "paymentUrl": "https://mt-pay.vercel.app/pay/V1StGXR8Z5"
  }
}

Envoyez data.paymentUrl a votre client par WhatsApp, SMS ou e-mail.

Endpoint

Consulter un paiement

GET/api/v1/payment-links/:id
Requete
curl https://mt-pay.vercel.app/api/v1/payment-links/V1StGXR8Z5 \
  -H "Authorization: Bearer mt_live_xxx"

Utile pour verifier un statut a la demande. Pour la confirmation temps reel, preferez le webhook.

Endpoint

Lister vos liens

GET/api/v1/payment-links
Requete
curl "https://mt-pay.vercel.app/api/v1/payment-links?status=COMPLETED&page=1&pageSize=20" \
  -H "Authorization: Bearer mt_live_xxx"

Renvoie { "data": [ … ] }. Filtrez par status (optionnel : PENDING, COMPLETED, FAILED, EXPIRED). Paginez avec page et pageSize (100 max, 20 par defaut). Le total, la page courante et le nombre de pages sont renvoyes dans les en-tetes de reponse.

En-tetes de pagination
X-Total-Count: 134
X-Page: 1
X-Page-Size: 20
X-Total-Pages: 7

Endpoint

Consulter votre solde

GET/api/v1/balance
Requete
curl https://mt-pay.vercel.app/api/v1/balance \
  -H "Authorization: Bearer mt_live_xxx"
Reponse 200
{
  "data": {
    "currency": "XOF",
    "availableBalance": 125000,
    "totalCollected": 340000,
    "totalWithdrawn": 200000,
    "pendingWithdrawalAmount": 15000,
    "counts": { "links": 42, "completed": 31, "pendingWithdrawals": 1 }
  }
}

availableBalance suit la meme regle que le dashboard : total encaisse moins retraits deja approuves. Utile pour afficher votre solde sans repasser par le dashboard web.

Endpoint public

Pays et reseaux disponibles

GET/api/v1/countries

Aucune cle requise : construisez votre selecteur pays/operateur avant meme d'avoir un acces API approuve.

Requete
curl https://mt-pay.vercel.app/api/v1/countries
Reponse 200 (extrait)
{
  "data": [
    {
      "code": "BEN",
      "flag": "BJ",
      "name": "Bénin",
      "currency": "XOF",
      "feePercent": 4.9,
      "payout": true,
      "networks": [
        { "id": "MTN_MOMO_BEN", "label": "MTN MoMo", "brand": "MTN" },
        { "id": "MOOV_BEN", "label": "Moov Money", "brand": "MOOV" }
      ]
    }
  ]
}

Temps reel

Webhook

MTPay appelle votre URL en POST a chaque changement de statut. Repondez 200 rapidement : un autre code declenche une nouvelle tentative.

En-tetes recus
X-MTPay-Signature: sha256=...
X-MTPay-Event: payment.completed
Payload
{
  "event": "payment.completed",
  "paymentId": "V1StGXR8Z5",
  "amount": 15000,
  "currency": "XOF",
  "status": "COMPLETED",
  "customer": {
    "name": "Awa Konate",
    "phone": "+22997000000"
  }
}
Payload d'un echec (payment.failed)
{
  "event": "payment.failed",
  "paymentId": "V1StGXR8Z5",
  "amount": 6000,
  "currency": "XAF",
  "status": "FAILED",
  "customer": {
    "name": "Jean Moussavou",
    "phone": "+24177000000"
  },
  "failure": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "The customer does not have enough funds to complete the payment."
  }
}

failure accompagne payment.failed et payment.expiredquand l'operateur a donne un motif. Codes frequents : INSUFFICIENT_BALANCE (solde insuffisant, frais inclus), PAYMENT_NOT_APPROVED (PIN non valide), PAYER_NOT_FOUND (numero inconnu de l'operateur), PAYER_LIMIT_REACHED (plafond atteint).

payment.refunded (statut REFUNDED) signale qu'un paiement a été remboursé au client : annulez la commande ou retirez l'accès correspondant.

Verifiez toujours la signature

Recalculez un HMAC SHA-256 du corps brut avec votre secret webhook et comparez-le a X-MTPay-Signature avant de livrer quoi que ce soit.

Reference

Statuts d'un paiement

PENDING

Lien cree, le client n'a pas encore paye.

COMPLETED

Paiement confirme par l'operateur. Livrez la commande.

FAILED

Paiement refuse ou annule par le client.

EXPIRED

Le lien a depasse sa duree de validite.

Reference

Pays et devises

Passez le code ISO-3 dans country. La devise est deduite automatiquement si vous ne la precisez pas.

BENBénin
4,9%XOF
CIVCôte d'Ivoire
4,9%XOF
SENSénégal
4,9%XOF
TGOTogo
4,9%XOF
CMRCameroun
7,6%XAF
GABGabon
7,6%XAF
COGCongo
7,6%XAF
CODRD Congo
7,6%CDF
KENKenya
7,6%KES
UGAOuganda
7,6%UGX
ZMBZambie
7,6%ZMW
SLESierra Leone
7,6%SLE
ETHÉthiopie
7,6%ETB

Reference

Erreurs

Toute erreur renvoie le meme format : un code stable sur lequel brancher de la logique (retry, message specifique...), et un message informatif qui peut evoluer sans casser votre integration.

Format d'erreur
{
  "error": {
    "code": "NOT_FOUND",
    "message": "Lien de paiement introuvable."
  }
}
400INVALID_REQUEST

Corps de requete invalide ou champ requis manquant/incorrect.

401UNAUTHORIZED

En-tete Authorization absent ou cle API inconnue.

403FORBIDDEN

Cle valide mais acces API non approuve pour ce compte.

404NOT_FOUND

L'identifiant n'existe pas ou appartient a un autre marchand.

409CONFLICT

L'action est refusee dans cet etat (ex. lien deja paye).

429RATE_LIMITED

Trop de requetes en peu de temps. Reessayez plus tard.

500INTERNAL_ERROR

Erreur cote MTPay. Reessayez ; contactez le support si persistant.

Une question sur l'integration ?

Demandez votre acces API : vous recevrez votre cle, votre URL webhook et votre secret.