Introduction
L'API CowemaPay vous permet d'accepter des paiements Mobile Money (MTN MoMo, Airtel Money) dans vos applications. L'API suit les conventions REST et retourne du JSON.
URL de base :
https://pay.cowema.org/api/v1
https://pay.cowema.org/api/v1 si vous travaillez en local.
openapi.json dans Bruno, Postman ou Insomnia. Ou utilisez le fichier .http directement dans VS Code (REST Client) ou JetBrains.
Demarrage rapide
1. Creez votre compte
Rendez-vous sur le tableau de bord et creez un compte. Vous serez invite a creer une organisation.
2. Creez une application
Dans le menu Applications, cliquez sur New application. Donnez-lui un nom et configurez vos URLs de webhook si necessaire.
3. Generez vos cles API
Sur la liste des applications, cliquez sur l'icone cle a cote de votre application, puis Cles API (test). Une modale affichera vos cles :
| Cle | Prefixe | Visibilite |
|---|---|---|
| Cle publique | pk_test_... | Visible a tout moment dans le dashboard |
| Cle secrete | sk_test_... | Affichee une seule fois a la generation |
4. Faites votre premier appel
Testez avec un paiement sandbox :
curl -X POST https://pay.cowema.org/api/v1/payments \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"phone_number": "054553499",
"provider": "mtn_momo"
}'
Le numero 054553499 simule un paiement reussi en sandbox.
Flow de paiement API
Authentification
Toutes les requetes API doivent inclure votre cle secrete dans le header Authorization.
Authorization: Bearer sk_test_votre_cle_secrete
sk_). Elle donne un acces complet a votre compte. Utilisez la cle publique (pk_) cote frontend.
| Type de cle | Prefixe | Usage |
|---|---|---|
| Cle publique test | pk_test_ | Identification cote client (sandbox) |
| Cle secrete test | sk_test_ | Requetes API (sandbox) |
| Cle publique live | pk_live_ | Identification cote client (production) |
| Cle secrete live | sk_live_ | Requetes API (production) |
401 invalid_api_key, 403 kyc_verification_required, 403 ip_not_whitelisted, 409 idempotency_key_conflict, et 429 peuvent etre retournes par n'importe quel endpoint v1. Voir la reference des codes d'erreur.
Mode sandbox
Les cles sk_test_ activent automatiquement le mode sandbox. Aucun appel reel n'est fait aux fournisseurs. Utilisez ces numeros de test :
Creer un paiement
Initie une demande de paiement Mobile Money. Le client recevra une notification sur son telephone pour confirmer.
Idempotency-Key pour eviter les doublons en cas de retry. En savoir plus| Parametre | Type | Description | |
|---|---|---|---|
amount | integer | requis | Montant en unite minimale (ex: 5000 = 5 000 XAF) |
phone_number | string | requis | Numero de telephone du payeur |
provider | string | requis | mtn_momo ou airtel_money |
metadata | object | optionnel | Donnees personnalisees (ex: order_id) |
curl -X POST https://pay.cowema.org/api/v1/payments \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: votre-cle-unique" \
-d '{
"amount": 5000,
"phone_number": "054553499",
"provider": "mtn_momo",
"customer": {
"name": "Jean Dupont",
"email": "jean@exemple.com"
},
"metadata": {"order_id": "ORD-123"}
}'
201 -- Paiement cree
{
"data": {
"id": "9f8a2b3c-...",
"type": "collection",
"provider": "mtn_momo",
"amount": 5000,
"currency": "XAF",
"country": "CG",
"phone_number": "054553499",
"status": "pending",
"environment": "test",
"metadata": { "order_id": "ORD-123" },
"customer": {
"id": "a1b2c3d4-...",
"name": "Jean Dupont",
"phone": "054553499",
"email": "jean@exemple.com",
"country": null
},
"created_at": "2026-03-29T18:00:00+00:00"
}
}
401 -- Cle API invalide
{
"error": "Invalid API key.",
"code": "invalid_api_key"
}
401 -- Non authentifie
{
"error": "Missing API key.",
"code": "missing_api_key"
}
422 -- Erreur de validation
{
"message": "The amount field is required.",
"errors": {
"amount": ["The amount field is required."],
"provider": ["The selected provider is invalid."]
}
}
429 -- Rate limit depasse
{
"message": "Too Many Attempts."
}
Voir un paiement
Recupere les details d'un paiement par son identifiant.
curl https://pay.cowema.org/api/v1/payments/PAYMENT_UUID \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Paiement trouve
{
"data": {
"id": "9f8a2b3c-...",
"type": "collection",
"status": "successful",
// ... memes champs que la creation
}
}
404 -- Transaction introuvable
{
"error": "Transaction not found.",
"code": "not_found"
}
Lister les paiements
Retourne la liste paginee de vos paiements. Filtrable par statut, fournisseur et environnement.
| Parametre | Type | Description | |
|---|---|---|---|
status | string | filtre | pending, processing, successful, failed, cancelled |
provider | string | filtre | mtn_momo ou airtel_money |
environment | string | filtre | live ou test |
per_page | integer | filtre | Nombre de resultats (max 100) |
curl "https://pay.cowema.org/api/v1/payments?status=successful&per_page=10" \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Liste paginee
{
"data": [
{
"id": "9f8a2b3c-...",
"type": "collection",
"amount": 5000,
"status": "successful",
// ...
},
// ... autres transactions
],
"links": {
"first": "...?page=1",
"last": "...?page=3",
"prev": null,
"next": "...?page=2"
},
"meta": {
"current_page": 1,
"per_page": 10,
"total": 25,
"last_page": 3
}
}
422 -- Filtre invalide
{
"message": "The selected status is invalid.",
"errors": {
"status": ["The selected status is invalid."]
}
}
Creer un remboursement
Rembourse un paiement reussi. Le montant est reverse sur le compte Mobile Money du payeur.
Idempotency-Key pour eviter les doublons en cas de retry. En savoir plus| Parametre | Type | Description | |
|---|---|---|---|
payment_id | string | requis | UUID du paiement a rembourser |
amount | integer | requis | Montant a rembourser |
curl -X POST https://pay.cowema.org/api/v1/refunds \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: votre-cle-unique" \
-d '{
"payment_id": "PAYMENT_UUID",
"amount": 5000
}'
201 -- Remboursement cree
{
"data": {
"id": "b7c4e1f2-...",
"type": "refund",
"status": "pending",
// ... memes champs qu'une transaction
}
}
422 -- Transaction non remboursable
{
"error": "Original transaction not found or not refundable.",
"code": "not_refundable"
}
422 -- Montant de remboursement depasse
{
"error": "Total refund amount exceeds the original transaction amount.",
"code": "refund_amount_exceeded"
}
Voir un remboursement
curl https://pay.cowema.org/api/v1/refunds/REFUND_UUID \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Remboursement trouve
{
"data": {
"id": "b7c4e1f2-...",
"type": "refund",
"provider": "mtn_momo",
"amount": 5000,
"currency": "XAF",
"status": "successful",
"environment": "test",
"created_at": "2026-03-29T18:05:00+00:00"
}
}
404 -- Remboursement introuvable
{
"error": "Refund not found.",
"code": "not_found"
}
Creer une session Checkout
Cree une session de paiement hebergee. Redirigez votre client vers l'URL retournee.
Idempotency-Key pour eviter les doublons en cas de retry. En savoir plus| Parametre | Type | Description | |
|---|---|---|---|
amount | integer | requis | Montant |
success_url | string | requis | URL de redirection apres succes. Doit utiliser HTTPS et un host deja declare sur votre application (success_url / cancel_url). En environnement local, localhost et 127.0.0.1 sont autorises. |
cancel_url | string | requis | URL de redirection si annule. Doit utiliser HTTPS et un host deja declare sur votre application (success_url / cancel_url). En environnement local, localhost et 127.0.0.1 sont autorises. |
description | string | optionnel | Description affichee au client |
metadata | object | optionnel | Donnees personnalisees (ex: order_id) |
curl -X POST https://pay.cowema.org/api/v1/checkout/sessions \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: votre-cle-unique" \
-d '{
"amount": 10000,
"description": "Abonnement Premium",
"success_url": "https://votresite.com/success",
"cancel_url": "https://votresite.com/cancel"
}'
201 -- Session creee
{
"data": {
"id": "cs_abc123...",
"amount": 10000,
"currency": "XAF",
"country": "CG",
"url": "https://pay.cowema.org/checkout/cs_abc123",
"status": "open",
"expires_at": "2026-03-29T19:00:00+00:00"
}
}
422 -- Erreur de validation
{
"message": "The success url field is required.",
"errors": {
"success_url": ["The success url field is required."]
}
}
422 -- URLs success/annulation manquantes
{
"error": "Both success_url and cancel_url are required.",
"code": "missing_urls"
}
422 -- URL de redirection non autorisee
{
"error": "success_url and cancel_url must use HTTPS and match a host configured on your application.",
"code": "invalid_redirect_url"
}
Voir une session checkout
Recupere le statut d'une session checkout. Utilisez cet endpoint pour verifier cote serveur si le paiement a abouti.
curl https://pay.cowema.org/api/v1/checkout/sessions/SESSION_UUID \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Session trouvee
{
"data": {
"id": "cs_abc123...",
"amount": 10000,
"currency": "XAF",
"description": "Abonnement Premium",
"url": "https://pay.cowema.org/checkout/cs_abc123",
"status": "completed",
"expires_at": "2026-03-29T19:00:00+00:00",
"metadata": null,
"created_at": "2026-03-29T18:00:00+00:00"
}
}
404 -- Session introuvable
{
"error": "Checkout session not found.",
"code": "not_found"
}
Flow d'integration Checkout
1. Creez une session cote serveur
Appelez POST /api/v1/checkout/sessions depuis votre backend avec votre cle secrete.
2. Redirigez le client
Envoyez votre client vers l'URL data.url retournee dans la reponse.
3. Le client paie
Sur la page CowemaPay, il choisit son fournisseur (MTN/Airtel), entre son numero, et confirme.
4. Redirection automatique
Apres paiement, le client est redirige vers votre success_url ou cancel_url.
5. Verifiez cote serveur
Utilisez GET /api/v1/checkout/sessions/{id} ou les webhooks pour confirmer le paiement.
Diagramme
Comportement du checkout heberge
Le checkout heberge est un flux navigateur. Voici son comportement dans les cas limites que vos logs proxy peuvent afficher :
- Unknown UUID — Laravel returns a
404HTML page (the session does not exist). - Session not open or expired —
302redirect back to the checkout page with an expiry notice. - Validation error —
302redirect back with form errors in the session (name required, etc.). - Completed session — The checkout page displays a completed/expired view instead of the payment form.
Creer un decaissement
Initie un transfert d'argent vers un compte Mobile Money. Le montant est debite de votre solde marchand.
Idempotency-Key pour eviter les doublons en cas de retry. En savoir plus| Parametre | Type | Description | |
|---|---|---|---|
amount | integer | requis | Montant en unite minimale (ex: 5000 = 5 000 XAF) |
provider | string | requis | mtn_momo ou airtel_money |
phone_number | string | requis | Numero de telephone du beneficiaire |
metadata | object | optionnel | Donnees personnalisees (ex: order_id) |
Statuts d'un decaissement
| Statut | Description |
|---|---|
pending | Decaissement cree, en attente de validation |
approved | Decaissement approuve, pret a etre traite |
processing | Transfert en cours aupres du fournisseur |
completed | Transfert effectue avec succes |
failed | Le transfert a echoue |
rejected | Decaissement rejete |
curl -X POST https://pay.cowema.org/api/v1/payouts \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: votre-cle-unique" \
-d '{
"amount": 5000,
"provider": "mtn_momo",
"phone_number": "066000001"
}'
201 -- Decaissement cree
{
"data": {
"id": "d4e5f6a7-...",
"type": "payout",
"provider": "mtn_momo",
"amount": 5000,
"currency": "XAF",
"country": "CG",
"phone_number": "066000001",
"status": "pending",
"environment": "test",
"metadata": null,
"created_at": "2026-03-31T10:00:00+00:00"
}
}
422 -- Solde insuffisant
{
"error": "Insufficient balance for this payout.",
"code": "insufficient_balance"
}
422 -- Erreur de validation
{
"message": "The amount field is required.",
"errors": {
"amount": ["The amount field is required."]
}
}
Lister les decaissements
Retourne la liste paginee de vos decaissements. Filtrable par statut.
| Parametre | Type | Description | |
|---|---|---|---|
status | string | filtre | pending, approved, processing, completed, failed, rejected |
per_page | integer | filtre | Nombre de resultats (max 100) |
curl "https://pay.cowema.org/api/v1/payouts?status=completed&per_page=10" \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Liste paginee
{
"data": [
{
"id": "d4e5f6a7-...",
"type": "payout",
"amount": 5000,
"status": "completed",
// ...
}
],
"links": { // ... pagination },
"meta": {
"current_page": 1,
"per_page": 10,
"total": 8,
"last_page": 1
}
}
Voir un decaissement
Recupere les details d'un decaissement par son identifiant.
curl https://pay.cowema.org/api/v1/payouts/PAYOUT_UUID \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Decaissement trouve
{
"data": {
"id": "d4e5f6a7-...",
"type": "payout",
"provider": "mtn_momo",
"amount": 5000,
"currency": "XAF",
"country": "CG",
"phone_number": "066000001",
"status": "completed",
"environment": "test",
"created_at": "2026-03-31T10:00:00+00:00"
}
}
404 -- Decaissement introuvable
{
"error": "Payout not found.",
"code": "not_found"
}
Solde marchand
Retourne le solde disponible de votre compte marchand, ventile par devise.
curl https://pay.cowema.org/api/v1/merchant/balance \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Solde recupere
{
"data": [
{
"currency": "XAF",
"available_balance": 15885960,
"pending_balance": 0
}
]
}
Idempotence
Tous les endpoints POST acceptent un header Idempotency-Key pour eviter les doublons en cas de retry reseau.
Idempotency-Key: votre-cle-unique-uuid
Fonctionnement
| Scenario | Comportement |
|---|---|
| Meme cle + meme body | Retourne la reponse mise en cache (validite 30 jours) |
| Meme cle + body different | Retourne une erreur 409 Conflict |
| Pas de header | Requete traitee normalement (pas d'idempotence) |
curl -X POST https://pay.cowema.org/api/v1/payments \
-H "Authorization: Bearer sk_test_VOTRE_CLE" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-d '{
"amount": 5000,
"phone_number": "054553499",
"provider": "mtn_momo"
}'
409 -- Conflit d'idempotence
{
"error": "A request with this idempotency key already exists with a different body.",
"code": "idempotency_conflict"
}
Idempotency-Key est optionnel. Utilisez un UUID v4 unique par requete pour garantir l'unicite.
Quand une reponse en cache est rejouee, l'API ajoute un en-tete Idempotency-Replayed: true a la reponse pour distinguer les replays des nouvelles executions.
Securite
CowemaPay propose plusieurs mecanismes de securite configurables par application.
IP Whitelisting
Vous pouvez restreindre les appels API a une liste d'adresses IP autorisees. Configurez les IPs dans le dashboard, sur la page d'edition de votre application.
403 -- IP non autorisee
{
"error": "Votre adresse IP n'est pas autorisée pour cette application.",
"code": "ip_not_whitelisted"
}
Rate Limiting
Par defaut, l'API autorise 100 requetes par minute par application. Cette limite est configurable par organisation.
| Header | Description |
|---|---|
X-RateLimit-Limit | Nombre maximum de requetes par minute |
X-RateLimit-Remaining | Nombre de requetes restantes |
Retry-After | Secondes avant la prochaine fenetre (si limite atteinte) |
KYC Gate
Le mode live necessite une verification KYC valide pour votre organisation. Les cles sk_live_ sont inutilisables tant que le KYC n'est pas approuve.
403 -- KYC requis
{
"error": "La vérification KYC est requise pour utiliser le mode live.",
"code": "kyc_verification_required"
}
Webhooks sortants
CowemaPay envoie des notifications HTTP (webhooks) vers votre webhook_url a chaque changement de statut d'un paiement ou d'un decaissement.
Signature
Chaque webhook inclut un header X-CowemaPay-Signature contenant un HMAC-SHA256 du body brut, signe avec votre webhook_secret.
// Verifiez toujours la signature avant de traiter l'evenement $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_COWEMAPAY_SIGNATURE']; $secret = 'votre_webhook_secret'; $expected = hash_hmac('sha256', $payload, $secret); if (!hash_equals($expected, $signature)) { http_response_code(401); exit('Signature invalide'); } $event = json_decode($payload, true); // Traitez l'evenement...
Types d'evenements
| Evenement | Description |
|---|---|
payment.successful | Un paiement a ete confirme |
payment.failed | Un paiement a echoue |
refund.successful | Un remboursement a ete confirme |
refund.failed | Un remboursement a echoue |
payout.completed | Un decaissement a ete effectue |
payout.failed | Un decaissement a echoue |
Politique de retry
Si votre serveur ne repond pas avec un code 2xx, CowemaPay reessaie jusqu'a 3 fois avec un delai croissant :
| Tentative | Delai |
|---|---|
| 1er retry | 60 secondes |
| 2e retry | 5 minutes |
| 3e retry | 30 minutes |
Payload exemple
// Headers Content-Type: application/json X-CowemaPay-Signature: a1b2c3d4e5... X-CowemaPay-Event: payment.successful // Body { "event": "payment.successful", "data": { "id": "9f8a2b3c-...", "type": "collection", "amount": 5000, "currency": "XAF", "country": "CG", "phone_number": "054553499", "provider": "mtn_momo", "status": "successful", "environment": "test", "metadata": { "order_id": "ORD-123" }, "created_at": "2026-03-29T18:00:00+00:00" }, "timestamp": "2026-03-29T18:00:15+00:00" }
X-CowemaPay-Signature avant de traiter un webhook. Ne faites jamais confiance au contenu sans verification.
Solde fournisseur
Interroge directement le solde aupres du fournisseur Mobile Money.
curl "https://pay.cowema.org/api/v1/balance?provider=mtn_momo" \ -H "Authorization: Bearer sk_test_VOTRE_CLE"
200 -- Solde recupere
{
"data": {
"available": 1000000,
"currency": "XAF"
}
}
422 -- Fournisseur invalide
{
"message": "The selected provider is invalid.",
"errors": {
"provider": ["The selected provider is invalid."]
}
}
Webhooks
CowemaPay envoie des webhooks a l'URL configuree dans votre application lorsqu'un evenement se produit.
Configuration
Dans le dashboard, editez votre application et renseignez l'URL du webhook. Un webhook_secret est genere automatiquement -- vous le trouverez sur la page d'edition de l'application, dans le champ Secret du webhook.
Evenements
| Evenement | Description |
|---|---|
payment.successful | Un paiement a ete confirme |
payment.failed | Un paiement a echoue |
refund.successful | Un remboursement a ete confirme |
refund.failed | Un remboursement a echoue |
Verification de la signature
Chaque webhook inclut un header X-CowemaPay-Signature contenant un HMAC SHA-256 du body avec votre webhook_secret.
// Verifiez la signature du webhook $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_COWEMAPAY_SIGNATURE']; $secret = 'votre_webhook_secret'; $expected = hash_hmac('sha256', $payload, $secret); if (hash_equals($expected, $signature)) { // Webhook valide $event = json_decode($payload, true); // Traitez l'evenement... }
Payload complet
Voici un exemple de payload envoye par CowemaPay :
// Headers Content-Type: application/json X-CowemaPay-Signature: a1b2c3d4e5... X-CowemaPay-Event: payment.successful // Body { "event": "payment.successful", "data": { "id": "9f8a2b3c-...", "type": "collection", "amount": 5000, "currency": "XAF", "country": "CG", "phone_number": "054553499", "provider": "mtn_momo", "status": "successful", "environment": "test", "metadata": { "order_id": "ORD-123" }, "created_at": "2026-03-29T18:00:00+00:00" }, "timestamp": "2026-03-29T18:00:15+00:00" }
Retries
Si votre serveur ne repond pas avec un code 2xx, CowemaPay reessaie jusqu'a 3 fois avec un delai croissant : 1 minute, 5 minutes, 30 minutes.
Codes d'erreur
Chaque erreur retourne un champ code lisible par machine. Faites correspondre sur code, pas sur le statut HTTP.
| Code | HTTP | Description |
|---|---|---|
missing_api_key | 401 | Aucun en-tete Authorization fourni |
invalid_api_key | 401 | Le token Bearer ne correspond a aucune cle active |
ip_not_whitelisted | 403 | L'IP appelante n'est pas dans la liste blanche de l'application |
kyc_verification_required | 403 | KYC de l'organisation non approuve -- cles live uniquement |
idempotency_key_conflict | 409 | Meme Idempotency-Key envoye avec un corps different |
not_found | 404 | La ressource demandee n'existe pas ou appartient a une autre application |
not_refundable | 422 | Le paiement n'est pas dans un etat remboursable |
missing_urls | 422 | success_url ou cancel_url manquante lors de la creation d'une session checkout |
invalid_redirect_url | 422 | success_url ou cancel_url n'utilise pas HTTPS ou pointe vers un host qui n'est pas declare sur votre application. Pour autoriser un nouveau domaine, configurez success_url / cancel_url sur l'application correspondante. |
refund_amount_exceeded | 422 | Le total rembourse depasserait le montant de la transaction originale |
insufficient_balance | 422 | Solde marchand insuffisant pour le decaissement demande |
Erreurs
L'API utilise les codes HTTP standards :
| Code | Description |
|---|---|
200 | Succes |
201 | Ressource creee |
401 | Cle API manquante ou invalide |
403 | Acces interdit (IP non autorisee, KYC requis) |
404 | Ressource introuvable |
409 | Conflit d'idempotence |
422 | Erreur de validation / solde insuffisant |
429 | Trop de requetes (rate limit) |
500 | Erreur serveur |
{
"message": "The amount field is required.",
"errors": {
"amount": ["The amount field is required."]
}
}