Développeurs
L'API est REST, en JSON. Base : https://api.kelyko.africa/v1. Tous les montants sont des entiers en centimes (amountCents) : 5 000 F CFA s'écrivent 500000, et un montant en XOF est toujours un multiple de 100.
Authentification
Chaque appel porte deux en-têtes, créés dans le tableau de bord, rubrique Développeurs. Le secret n'est affiché qu'une fois.
X-API-KEY-ID: key_uem1_…
X-API-KEY: sk_…
Une clé est limitée à un magasin de test, ou couvre les magasins réels du marchand. La limite est de 500 requêtes par minute et 1 000 par 5 minutes par entreprise ; au-delà, l'API répond 429 pendant 10 à 15 minutes.
Encaisser
curl https://api.kelyko.africa/v1/payment_requests \
-H "X-API-KEY-ID: $KELYKO_KEY_ID" -H "X-API-KEY: $KELYKO_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: CMD-1001" \
-d '{
"storeId": "sto_uem1_…",
"amountCents": 2500000,
"reference": "CMD-1001",
"description": "Commande 1001",
"successUrl": "https://votre-site.ci/merci",
"errorUrl": "https://votre-site.ci/echec"
}'
La réponse contient redirectUrl : envoyez-y votre client. Il choisit son moyen de paiement et paie. Le statut passe de pending à success, ou à error si le paiement expire. Ne livrez jamais une commande sur la seule foi du retour du client sur successUrl : attendez le webhook.
Transférer
curl https://api.kelyko.africa/v1/transfers \
-H "X-API-KEY-ID: $KELYKO_KEY_ID" -H "X-API-KEY: $KELYKO_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: PAIE-2026-11-AWA" \
-d '{
"storeId": "sto_uem1_…",
"amountCents": 10000000,
"reference": "PAIE-2026-11-AWA",
"beneficiary": { "name": "Awa Koné", "paymentMethod": "wave", "phone": "0700000000" }
}'
Le montant et les frais sont réservés sur le solde disponible dès l'appel. Un transfert en pending garde ses fonds réservés jusqu'à la réponse définitive de l'opérateur ; un échec les rend au solde. Pour un compte bancaire, paymentMethod vaut bank avec bankCode, branchCode, accountNumber et ribKey.
Frais et limites
La réponse d'un paiement réussi donne feesCents et netCents. Pour une carte, la commission dépend de la zone de la banque émettrice (1,5 % en UEMOA, 2,5 % hors zone), connue seulement après le paiement ; l'événement payment.succeeded la précise dans cardScope (uemoa ou international). Un transfert renvoie ses feesCents dès sa création. Grille et montants limites : Tarifs.
Idempotence
Envoyez un en-tête Idempotency-Key sur chaque création. Le même appel rejoué avec la même clé renvoie la première réponse, sans créer de second paiement ou de second transfert.
Webhooks
Kelyko envoie un POST à votre adresse https à chaque événement : payment.succeeded, payment.attempt_failed, payment.expired, transfer.succeeded, transfer.failed. L'en-tête Kelyko-Event donne le type, Kelyko-Event-Id l'identifiant, à garder pour ignorer un doublon.
Vérifiez toujours la signature. L'en-tête Kelyko-Signature vaut t=<horodatage>,v1=<signature>, où la signature est le HMAC-SHA256 en hexadécimal de <horodatage>.<corps brut> avec votre secret de webhook. Refusez un horodatage de plus de 5 minutes.
const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const valid = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
&& Math.abs(Date.now() / 1000 - Number(t)) < 300;
Magasins de test
Un magasin de test simulé utilise des opérateurs simulés : aucun argent ne circule. Les deux derniers chiffres du numéro de téléphone choisissent le scénario :
| Numéro se terminant par | Résultat |
|---|---|
00 (et tout autre) | Succès après confirmation |
01 | Solde insuffisant |
02 | Refusé par le payeur |
03 | Délai dépassé, puis succès : le statut reste pending quelques secondes |
04 | Aucune réponse : la tentative expire (paiement), le compte est rejeté (transfert) |
05 | Opérateur indisponible |
Pour Wave, Orange Money, Djamo et la carte, une page simulée de l'opérateur ou de la banque vous laisse choisir l'issue : paiement, refus, solde insuffisant, carte refusée, échec 3-D Secure.
Un magasin de test réel passe par les vrais opérateurs, avec de l'argent réel et des plafonds bas, pour vérifier la chaîne complète avant d'ouvrir.
Erreurs
Une erreur renvoie un code stable et un message : {"error": {"code": "insufficient_balance", "message": "…"}}. Les principaux codes : invalid_api_key (401), permission_denied (403), store_not_found (404), duplicate_reference (409), validation_error, amount_too_small, amount_too_large et insufficient_balance (422), rate_limited (429).