Cartflox, accueilcartflox
Documentation

Documentation développeur

Intègre les paiements Mobile Money et carte de 15+ pays africains avec une seule API. Guides, référence complète et SDKs.

Documentation Cartflox v2.0

Introduction à l'API Cartflox

Cartflox est une API de paiement panafricaine qui vous permet d'accepter des paiements Mobile Money (Orange Money, Wave, MTN MoMo, Moov) et carte bancaire, ainsi que d'effectuer des transferts d'argent dans plus de 15 pays africains — avec une seule intégration.

✓ Bon à savoirL'API Cartflox est disponible en mode sandbox et production. Utilisez vos clés sandbox pour tester sans effectuer de vrais paiements.

Fonctionnalités principales

Paiements Mobile Money

Orange Money, Wave, MTN MoMo, Moov, Airtel, Free Money…

Paiements par carte

Visa, Mastercard via notre passerelle sécurisée PCI DSS

Smart Routing

5 algorithmes (Priority, Volume Split, DSL, Dynamic) + fallback automatique

Transferts d'argent

Envoi de fonds vers des comptes Mobile Money et bancaires

Webhooks temps réel

Notifications instantanées sur l'état de vos transactions

Personnalisation

Page de paiement entièrement brandée à votre identité

SDKs multi-langages

Node.js, Python, PHP, Flutter, React Native

Audit log complet

Traçabilité de chaque décision de routage pour debug en prod

Base URL

Toutes les requêtes API doivent être effectuées en HTTPS :

Endpoints
# Sandbox (tests)
https://sandbox.api.cartflox.com/v2

# Production
https://api.cartflox.com/v2

Format des réponses

Toutes les réponses sont au format JSON avec la structure suivante :

Réponse type
{
  "success": true,
  "data": { ... },
  "meta": {
    "requestId": "req_01HXYZ...",
    "timestamp": "2026-03-01T12:00:00Z"
  }
}

// En cas d'erreur :
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Le solde du compte est insuffisant.",
    "details": {}
  }
}

Environnements

Sandbox

Pour les tests. Aucun vrai argent n'est débité. Utilisez les numéros de test fournis.

Production

Pour les vrais paiements. Nécessite la validation KYB de votre compte.

Guide

Démarrage rapide

Intégrez Cartflox et effectuez votre premier paiement en moins de 5 minutes.

1. Créer votre compte

Rendez-vous sur dashboard.cartflox.com pour créer un compte. Après inscription, vous avez immédiatement accès à l'environnement sandbox.

2. Obtenir vos clés API

Dans votre dashboard → Paramètres → API, copiez votre clé secrète.

.env
CARTFLOX_SECRET_KEY=cf_live_sec_xxxxxxxxxxxxxxxxxxxxxxxx
CARTFLOX_PUBLIC_KEY=cf_live_pub_xxxxxxxxxxxxxxxxxxxxxxxx
CARTFLOX_BASE_URL=https://cartflox.com

3. Créer votre premier paiement

Appelez directement l'API REST avec votre clé secrète :

payment.ts (Next.js)
const response = await fetch(
  `${process.env.CARTFLOX_BASE_URL}/api/v1/checkout/sessions`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${process.env.CARTFLOX_SECRET_KEY}`,
    },
    body: JSON.stringify({
      amount: 5000,
      currency: "XOF",
      customer_name: "Kouassi Jean",
      customer_email: "client@example.com",
      customer_phone: "+22507070707",
      description: "Commande #order_123",
      success_url: "https://monsite.com/paiement/succes",
      cancel_url: "https://monsite.com/paiement/annule",
      metadata: { orderId: "order_123" },
    }),
  }
);

const session = await response.json();

// Rediriger le client vers la page de paiement
console.log(session.url);
// → https://cartflox.com/checkout/cmmtlfj27...
ℹ InfoEn mode sandbox, le paiement sera automatiquement approuvé si vous utilisez le numéro +22500000001, ou refusé avec +22500000002.

4. Configurer le webhook

Enregistrez votre URL de réception pour être notifié des paiements :

bash
curl -X PATCH https://cartflox.com/api/v1/config/webhook \
  -H "Authorization: Bearer cf_live_sec_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "webhookUrl": "https://monsite.com/api/webhooks/cartflox" }'

5. Écouter les webhooks

Cartflox vous notifie en temps réel du statut de chaque paiement.

app/api/webhooks/cartflox/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const { event, data } = await req.json();

  if (event === "payment.completed") {
    // ✅ Mettre à jour votre base de données
    await db.orders.update({
      where: { cartfloxId: data.id },
      data: { status: "paid" }
    });
  }

  return NextResponse.json({ received: true });
}

6. Vérifier le paiement (optionnel)

typescript
const res = await fetch(
  `${process.env.CARTFLOX_BASE_URL}/api/v1/checkout/sessions/${sessionId}`,
  { headers: { "Authorization": `Bearer ${process.env.CARTFLOX_SECRET_KEY}` } }
);
const session = await res.json();

if (session.status === "SUCCESS") {
  console.log("Paiement reçu :", session.amount, session.currency);
}
⚠ AttentionNe jamais accorder l'accès à un service uniquement sur la base de l'URL de redirection. Vérifiez toujours le statut via webhook ou l'API.

Numéros de test (Sandbox)

NuméroRésultatDescription
+22500000001✅ SuccèsPaiement approuvé immédiatement
+22500000002❌ ÉchecFonds insuffisants
+22500000003⏳ En attentePaiement en attente de confirmation (timeout après 30s)
+22500000004🚫 RefusNuméro non enregistré auprès de l'opérateur
Sécurité

Authentification & Clés API

Cartflox utilise des clés API pour authentifier les requêtes. Vous pouvez gérer vos clés API depuis votre dashboard → Paramètres → API.

Types de clés

Clé secrète

cf_live_sec_... / cf_live_pub_...

Utilisée côté serveur uniquement. Ne jamais exposer dans le frontend. Donne accès à toutes les opérations.

Clé publique

cf_live_sec_... / cf_live_pub_...

Peut être utilisée dans le frontend (initialiser le SDK JS, identifier votre compte). Ne peut pas créer de paiements directement.

✕ ImportantNe jamais committer vos clés secrètes dans votre code source. Utilisez des variables d'environnement. Si une clé est compromise, révoquez-la immédiatement depuis le dashboard.

Utilisation

Incluez votre clé secrète dans le header Authorization de chaque requête :

Header HTTP
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Avec curl

bash
curl https://cartflox.com/api/v1/checkout/sessions \
  -H "Authorization: Bearer cf_live_sec_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json"

Avec le SDK Node.js

typescript
import Cartflox from "@cartflox/node";

const cartflox = new Cartflox(process.env.CARTFLOX_SECRET_KEY!);
// Le SDK gère automatiquement l'authentification

Environnements

Chaque clé est associée à un environnement :

PréfixeEnvironnementBase URL
af_sandbox_sec_ / af_sandbox_pub_Sandboxhttps://cartflox.com
cf_live_sec_ / cf_live_pub_Productionhttps://cartflox.com

Rotation des clés

Il est recommandé de faire tourner vos clés API tous les 90 jours. Pour ce faire :

  1. Créez une nouvelle clé dans le dashboard
  2. Mettez à jour vos variables d'environnement en production
  3. Attendez que votre déploiement soit actif
  4. Révoquez l'ancienne clé dans le dashboard

IP Allowlist (optionnel)

Pour une sécurité renforcée, vous pouvez restreindre les requêtes API à une liste d'adresses IP dans Paramètres → Sécurité → IP Allowlist.

Paiements

Créer un paiement

Initialisez une session de paiement et redirigez votre client vers la page de paiement Cartflox.

POST
/api/v1/checkout/sessions

Créer une nouvelle session de paiement

Paramètres

ParamètreTypeRequisDescription
amountintegerOuiMontant entier (ex: 5000 XOF)
currencystringNonCode ISO 4217 : XOF, XAF, GHS, KES… (défaut : XOF)
customer_namestringNonNom complet du client
customer_emailstringNonEmail du client
customer_phonestringNonTéléphone du client au format international
descriptionstringNonDescription affichée sur la page de paiement
success_urlstringNonURL de redirection après paiement réussi
cancel_urlstringNonURL de redirection si le client annule
metadataobjectNonDonnées personnalisées retournées dans les webhooks

Objet customer

ParamètreTypeRequisDescription
phonestringOuiNuméro de téléphone au format international (+22507…)
emailstringNonAdresse email du client
namestringNonNom complet du client
countrystringNonCode pays ISO 3166-1 alpha-2 (ex: CI, SN, BJ…)

Exemples

Node.js

create-payment.ts
const payment = await cartflox.payments.create({
  amount: 25000,
  currency: "XOF",
  method: "wave",
  customer: {
    phone: "+22507070707",
    email: "client@example.com",
    name: "Kouassi Jean"
  },
  description: "Abonnement Premium - Janvier 2026",
  redirectUrl: "https://monsite.com/success",
  cancelUrl: "https://monsite.com/cancel",
  metadata: {
    orderId: "ORD-2026-001",
    userId: "usr_123"
  }
});

// Rediriger le client
res.redirect(payment.checkoutUrl);

cURL

bash
curl -X POST https://cartflox.com/api/v1/checkout/sessions \
  -H "Authorization: Bearer cf_live_sec_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25000,
    "currency": "XOF",
    "customer_name": "Kouassi Jean",
    "customer_email": "client@example.com",
    "customer_phone": "+22507070707",
    "description": "Abonnement Premium",
    "success_url": "https://monsite.com/success",
    "cancel_url": "https://monsite.com/cancel"
  }'

Réponse

201 Created
{
  "id": "cmmtlfj27000004l4nuzy1zz5",
  "object": "checkout.session",
  "url": "https://cartflox.com/checkout/cmmtlfj27000004l4nuzy1zz5",
  "order_id": "CS-MMTLFJ1Z-B4HEE",
  "amount": 25000,
  "currency": "XOF",
  "status": "pending",
  "created": "2026-03-01T12:00:00Z"
}

Méthode "auto"

En utilisant method: "auto", Cartflox affiche automatiquement toutes les méthodes disponibles dans le pays du client et laisse ce dernier choisir.

ℹ InfoLe mode auto est recommandé pour maximiser le taux de conversion. Cartflox détecte automatiquement le pays via le préfixe du numéro de téléphone.
Paiements

Vérifier un paiement

Récupérez les détails et le statut actuel d'un paiement existant.

GET
/api/v1/checkout/sessions/:id

Récupérer une session de paiement par son identifiant

Statuts possibles

pending

En attente de paiement

processing

Traitement en cours

completed

Paiement réussi

failed

Paiement échoué

cancelled

Annulé par le client

refunded

Remboursé

Exemple

Node.js

typescript
const payment = await cartflox.payments.retrieve("pi_01HXYZ789ABC");

switch (payment.status) {
  case "completed":
    await fulfillOrder(payment.metadata.orderId);
    break;
  case "failed":
    await notifyCustomerOfFailure(payment.customer.email);
    break;
  default:
    console.log("Statut:", payment.status);
}

cURL

bash
curl https://cartflox.com/api/v1/checkout/sessions/cmmtlfj27000004l4nuzy1zz5 \
  -H "Authorization: Bearer cf_live_sec_xxxx"

Réponse

200 OK
{
  "id": "cmmtlfj27000004l4nuzy1zz5",
  "object": "checkout.session",
  "url": "https://cartflox.com/checkout/cmmtlfj27000004l4nuzy1zz5",
  "order_id": "CS-MMTLFJ1Z-B4HEE",
  "amount": 25000,
  "currency": "XOF",
  "status": "SUCCESS",
  "created": "2026-03-01T12:00:00Z"
}
⚠ AttentionPréférez toujours les webhooks plutôt que le polling pour être notifié du statut d'un paiement. Le polling excessif peut entraîner une limitation de débit.
Paiements

Lister les paiements

Récupérez la liste paginée de tous vos paiements avec des filtres avancés.

GET
/v2/payments

Lister les paiements avec filtres et pagination

Paramètres de requête

ParamètreTypeRequisDescription
limitintegerNonNombre de résultats par page (défaut: 20, max: 100)
cursorstringNonCurseur de pagination (retourné dans la réponse précédente)
statusstringNonFiltrer par statut : pending | completed | failed | cancelled | refunded
methodstringNonFiltrer par méthode de paiement
currencystringNonFiltrer par devise (ex: XOF, XAF)
fromstringNonDate de début ISO 8601 (ex: 2026-01-01T00:00:00Z)
tostringNonDate de fin ISO 8601
customerIdstringNonFiltrer par identifiant client

Exemple

Node.js

typescript
const payments = await cartflox.payments.list({
  limit: 20,
  status: "completed",
  currency: "XOF",
  from: "2026-01-01T00:00:00Z",
  to:   "2026-03-01T23:59:59Z"
});

for (const payment of payments.data) {
  console.log(payment.id, payment.amount, payment.status);
}

// Pagination
if (payments.hasMore) {
  const nextPage = await cartflox.payments.list({
    cursor: payments.nextCursor
  });
}

cURL

bash
curl "https://api.cartflox.com/v2/payments?limit=20&status=completed&currency=XOF" \
  -H "Authorization: Bearer sk_live_xxxx"

Réponse

200 OK
{
  "success": true,
  "data": [
    {
      "id": "pi_01HXYZ789ABC",
      "status": "completed",
      "amount": 25000,
      "currency": "XOF",
      "method": "wave",
      "customer": { "phone": "+22507070707" },
      "createdAt": "2026-03-01T12:00:00Z"
    },
    { ... }
  ],
  "meta": {
    "total": 1543,
    "count": 20,
    "hasMore": true,
    "nextCursor": "cursor_eyJpZCI6InBpXzAxSFhZ..."
  }
}
Paiements

Rembourser un paiement

Remboursez un paiement complété, entièrement ou partiellement.

POST
/v2/payments/:id/refund

Créer un remboursement pour un paiement

Paramètres

ParamètreTypeRequisDescription
amountintegerNonMontant à rembourser (en centimes). Si omis, remboursement total.
reasonstringNonRaison du remboursement : duplicate | fraudulent | requested_by_customer | other
notestringNonNote interne (non visible par le client)
metadataobjectNonDonnées personnalisées
ℹ InfoLes remboursements sont traités vers le même moyen de paiement utilisé lors de la transaction originale. Le délai de remboursement dépend de l'opérateur (généralement 1-3 jours ouvrables).

Exemples

Remboursement total

typescript
const refund = await cartflox.payments.refund("pi_01HXYZ789ABC");

console.log(refund.status);  // "processing"

Remboursement partiel

typescript
const refund = await cartflox.payments.refund("pi_01HXYZ789ABC", {
  amount: 10000, // Rembourser 100 XOF sur 250 XOF
  reason: "requested_by_customer",
  note: "Remboursement partiel suite à réclamation client #TKT-456"
});

cURL

bash
curl -X POST https://api.cartflox.com/v2/payments/pi_01HXYZ789ABC/refund \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 10000, "reason": "requested_by_customer" }'

Réponse

200 OK
{
  "success": true,
  "data": {
    "id": "ref_01HABC123",
    "paymentId": "pi_01HXYZ789ABC",
    "status": "processing",
    "amount": 10000,
    "currency": "XOF",
    "reason": "requested_by_customer",
    "createdAt": "2026-03-01T15:30:00Z"
  }
}

Conditions de remboursement

Paiements complétés (status: completed)

Remboursements partiels multiples jusqu'au montant total

Paiements en attente ou échoués

Paiements de plus de 90 jours (contactez le support)

Transferts

Transfert d'argent

Envoyez des fonds directement vers des comptes Mobile Money ou bancaires dans toute l'Afrique.

⚠ AttentionLes transferts nécessitent un compte vérifié (KYB complet) et un solde suffisant sur votre compte Cartflox. Contactez notre équipe pour activer cette fonctionnalité.
POST
/v2/transfers

Initier un transfert d'argent

Paramètres

ParamètreTypeRequisDescription
amountintegerOuiMontant en centimes à transférer
currencystringOuiDevise du transfert (XOF, XAF, GHS…)
destinationobjectOuiInformations du destinataire (voir ci-dessous)
descriptionstringNonDescription du transfert
referencestringNonRéférence unique pour éviter les doublons (idempotency key)
metadataobjectNonDonnées personnalisées

Objet destination

ParamètreTypeRequisDescription
typestringOuimobile_money | bank_account
phonestringNonNuméro Mobile Money (requis si type=mobile_money)
networkstringNonOpérateur : wave | orange-money | mtn-momo | moov | airtel
ibanstringNonIBAN du compte bancaire (si type=bank_account)
namestringNonNom du destinataire
countrystringNonCode pays ISO (ex: CI, SN)

Exemples

Transfert Mobile Money

typescript
const transfer = await cartflox.transfers.create({
  amount: 50000,
  currency: "XOF",
  destination: {
    type: "mobile_money",
    phone: "+22507070707",
    network: "orange-money",
    name: "Kouassi Jean",
    country: "CI"
  },
  description: "Paiement fournisseur Mars 2026",
  reference: "PAY-2026-03-001" // Unique - évite les doublons
});

console.log(transfer.status); // "processing"

Transfert bancaire

typescript
const transfer = await cartflox.transfers.create({
  amount: 500000,
  currency: "XOF",
  destination: {
    type: "bank_account",
    iban: "CI00XY1234567890123456789",
    name: "Société XYZ SARL",
    country: "CI"
  },
  description: "Virement salaire Mars 2026"
});

Réponse

200 OK
{
  "success": true,
  "data": {
    "id": "tr_01HABC456DEF",
    "status": "processing",
    "amount": 50000,
    "currency": "XOF",
    "fee": 500,
    "netAmount": 49500,
    "destination": {
      "type": "mobile_money",
      "phone": "+22507070707",
      "network": "orange-money",
      "name": "Kouassi Jean"
    },
    "estimatedArrival": "2026-03-01T13:00:00Z",
    "reference": "PAY-2026-03-001",
    "createdAt": "2026-03-01T12:00:00Z"
  }
}

Récupérer un transfert

GET
/v2/transfers/:id

Vérifier le statut d'un transfert

typescript
const transfer = await cartflox.transfers.retrieve("tr_01HABC456DEF");
console.log(transfer.status); // processing | completed | failed

Frais de transfert

TypeFraisMin
Mobile Money (même pays)0.8% du montant50 XOF
Mobile Money (international)1.5% du montant100 XOF
Virement bancaire (UEMOA)0.5% du montant200 XOF
Smart Routing v1

Smart Routing — orchestration multi-passerelles

Cartflox ne se limite pas à transmettre vos paiements à une seule passerelle. Le moteur de routage choisit dynamiquement la meilleure passerelle pour chaque paiement, tente automatiquement un fallback en cas d'échec, et expose une trace complète de chaque décision.

ℹ InfoLe routage se configure depuis le dashboard /methods?tab=routing — aucun appel API supplémentaire n'est nécessaire. Vos clés marchand restent inchangées.

Les 5 algorithmes

Choisissez la stratégie adaptée à votre usage. Vous pouvez basculer à tout moment depuis le dashboard.

SINGLE

Une seule passerelle, toujours. Aucun fallback. Le plus simple — utile si vous avez une seule intégration.

PRIORITY

Liste ordonnée. Essaie la passerelle 1, si échec → passe à la 2, etc. Recommandé pour la plupart des cas — c'est l'algo par défaut.

VOLUME_SPLIT

Répartition probabiliste pondérée (ex: 70% PayDunya, 30% PawaPay). Idéal pour A/B tester deux passerelles ou répartir la charge.

ADVANCED

Règles métier en DSL : « SI montant > 50000 ET pays = CI ALORS PayDunya ». Première règle correspondante gagne.

DYNAMIC

Auto-pilote. Le moteur classe les passerelles par taux de succès récent (EMA) et favorise la plus performante. Mise à jour automatique après chaque paiement.

Override par méthode

Indépendamment de l'algorithme choisi, vous pouvez forcer une passerelle pour une combinaisonméthode + pays spécifique. Exemple : « pour Orange Money CI, utiliser toujours PayDunya même si l'algo dit autre chose ». L'override gagne sur l'algorithme.

Configuration via API (à venir)

Pour l'instant le routage se configure exclusivement via le dashboard. Une API publique/v2/routing/config sera exposée prochainement. En attendant, voici le modèle de données qu'Cartflox stocke par application :

Modèle RoutingConfig
{
  "algorithmKind": "PRIORITY",
  "fallbackOrder": ["gw_paydunya_xxx", "gw_pawapay_yyy"],
  "volumeSplits": [
    { "gatewayId": "gw_paydunya_xxx", "split": 70 },
    { "gatewayId": "gw_pawapay_yyy", "split": 30 }
  ],
  "routingRules": [
    {
      "name": "Gros montants → PayDunya",
      "conditions": [
        { "field": "amount",  "operator": "gt", "value": 50000 },
        { "field": "country", "operator": "eq", "value": "CI" }
      ],
      "gatewayIds": ["gw_paydunya_xxx"]
    }
  ],
  "methodAssignments": {
    "orange_money_ci||CI": "gw_paydunya_xxx"
  },
  "allowedProviders": ["gw_paydunya_xxx", "gw_pawapay_yyy"],
  "maxRetries": 3
}

Champs des règles ADVANCED

Le DSL accepte les champs suivants :

text
amount       — montant numérique de la transaction
currency     — code ISO de la devise (XOF, XAF, USD, …)
country      — code ISO alpha-2 (CI, SN, BJ, …)
methodCode   — code de la méthode (orange_money_ci, mtn_momo_ben, …)
methodType   — MOBILE_MONEY | CARD | BANK_TRANSFER

Opérateurs supportés :

text
eq, neq          — égalité / inégalité
gt, gte, lt, lte — comparaisons numériques (ou lexicographiques pour les strings)
in               — appartenance à une liste (value: ["CI","SN","ML"])

Simulateur de routage

Avant de déployer un changement de config, testez son comportement sans paiement réel. Le simulateur (/methods?tab=routing) prend en entrée un paiement type (montant, pays, méthode) et retourne :

  • la passerelle qui serait choisie ;
  • l'ordre complet des tentatives en cas d'échec ;
  • la trace de décision (algo appliqué, override hit, filtres autorisés) ;
  • le nombre maximum de tentatives.

Audit log des décisions

Chaque décision de routage est persistée pour debugging et analyse. Visible dans/methods?tab=decisions — les 50 dernières par défaut, avec :

  • date · méthode · pays · montant ;
  • algorithme appliqué (badge) + indicateur d'override ;
  • chaîne complète des passerelles tentées ;
  • passerelle finalement choisie ;
  • issue : PENDING / SUCCESS / FAILED / CANCELLED.
ℹ InfoL'outcome est mis à jour automatiquement lors de la réception du webhook de la passerelle — inutile d'écrire du code côté marchand.

Bonnes pratiques

Démarrez en PRIORITY

C'est l'algo le plus prévisible. Configurez votre passerelle préférée en 1ère position et au moins une de fallback.

Validez avec le simulateur

Avant chaque modification de config en prod, simulez 3-4 paiements types pour valider l'ordre attendu.

Activez DYNAMIC après stabilisation

Une fois que vous avez du volume (>100 paiements/jour), DYNAMIC optimise automatiquement les coûts/succès.

Monitorez les décisions

Consultez l'audit log régulièrement pour repérer les patterns d'échec — ex: une passerelle qui plante toujours sur Orange Money RDC.

Réservez ADVANCED aux cas spécifiques

Les règles DSL sont puissantes mais cassent vite si la config change. Documentez chaque règle.

Webhooks

Configuration des Webhooks

Les webhooks permettent à Cartflox de notifier votre serveur en temps réel lorsqu'un événement se produit (paiement reçu, transfert effectué, remboursement traité…).

Configurer votre URL webhook

Enregistrez l'URL de votre endpoint via l'API ou depuis le dashboard Cartflox.

Via l'API (recommandé)

bash
curl -X PATCH https://cartflox.com/api/v1/config/webhook \
  -H "Authorization: Bearer cf_live_sec_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "webhookUrl": "https://monsite.com/api/webhooks/cartflox" }'

Vérifier la configuration

bash
curl https://cartflox.com/api/v1/config/webhook \
  -H "Authorization: Bearer cf_live_sec_xxxx"
ℹ InfoCartflox enverra un POST à votre URL configurée à chaque événement de paiement (succès, échec, mise à jour).
⚠ AttentionVotre endpoint doit répondre avec un code HTTP 2xx dans les 30 secondes, sinon Cartflox considère la livraison comme échouée et retentera automatiquement.

Implémenter l'endpoint

Next.js (App Router)

app/api/webhooks/cartflox/route.ts
import { NextRequest, NextResponse } from "next/server";

export async function POST(req: NextRequest) {
  const body = await req.json();

  // Structure du payload Cartflox
  const { event, data, timestamp } = body;

  switch (event) {
    case "payment.completed":
      // ✅ Mettre à jour votre base de données
      await db.orders.update({
        where: { cartfloxId: data.id },
        data: { status: "paid" }
      });
      break;

    case "payment.failed":
      await db.orders.update({
        where: { cartfloxId: data.id },
        data: { status: "failed" }
      });
      break;

    case "payment.updated":
      console.log("Statut mis à jour:", data.status);
      break;
  }

  return NextResponse.json({ received: true });
}

Express.js

webhook.ts
import express from "express";
import Cartflox from "@cartflox/node";

const app = express();
const cartflox = new Cartflox(process.env.CARTFLOX_SECRET_KEY!);

// Important: utiliser raw body parser pour les webhooks
app.post(
  "/webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sig = req.headers["cartflox-signature"] as string;

    let event;
    try {
      event = cartflox.webhooks.constructEvent(
        req.body,
        sig,
        process.env.CARTFLOX_WEBHOOK_SECRET!
      );
    } catch (err) {
      return res.status(400).send("Signature invalide");
    }

    // Traitement
    if (event.type === "payment.completed") {
      console.log("Paiement reçu:", event.data.id);
    }

    res.json({ received: true });
  }
);

Logique de retry

En cas d'échec, Cartflox retente la livraison selon le calendrier suivant :

1re retry

après 5 min

2e retry

après 30 min

3e retry

après 2 h

4e retry

après 8 h

ℹ InfoAprès 4 échecs consécutifs, l'endpoint est désactivé et vous recevez un email d'alerte. Vous pouvez réactiver l'endpoint et rejouer les événements manqués depuis le dashboard.
Webhooks

Événements Webhook

Liste de tous les événements que vous pouvez écouter via vos endpoints webhook.

Paiements

payment.created

Un nouveau paiement a été créé

payment.processing

Le paiement est en cours de traitement

payment.completed

Le paiement a été complété avec succès

payment.failed

Le paiement a échoué

payment.cancelled

Le paiement a été annulé par le client

payment.expired

La session de paiement a expiré

Remboursements

refund.created

Un remboursement a été initié

refund.completed

Le remboursement a été traité avec succès

refund.failed

Le remboursement a échoué

Transferts

transfer.created

Un transfert a été initié

transfer.completed

Le transfert a été effectué avec succès

transfer.failed

Le transfert a échoué

Structure d'un événement

Chaque webhook envoyé à votre endpoint a la structure suivante :

Payload webhook
{
  "event": "payment.completed",
  "data": {
    "id": "cmmtlfj27000004l4nuzy1zz5",
    "order_id": "CS-MMTLFJ1Z-B4HEE",
    "status": "SUCCESS",
    "amount": 25000,
    "currency": "XOF",
    "provider": "paydunya",
    "provider_reference": "WAVE-TXN-98765",
    "customer_name": "Kouassi Jean",
    "customer_email": "client@example.com",
    "customer_phone": "+22507070707",
    "metadata": {
      "orderId": "ORD-2026-001"
    },
    "completed_at": "2026-03-01T12:05:33Z"
  },
  "timestamp": "2026-03-01T12:05:33Z"
}

Gestion des doublons

Cartflox peut parfois envoyer le même événement plusieurs fois (en cas de retry). Utilisez le champ id de l'événement pour dédupliquer :

typescript
// Stocker les IDs d'événements déjà traités
const processedEvents = new Set<string>();

function handleWebhook(event: CartfloxEvent) {
  if (processedEvents.has(event.id)) {
    console.log("Événement déjà traité:", event.id);
    return;
  }
  
  processedEvents.add(event.id);
  // Traiter l'événement...
}

Tester vos webhooks en local

Utilisez ngrok pour exposer votre serveur local :

bash
# Installer ngrok et exposer votre port local
ngrok http 3000

# Puis configurer l'URL ngrok comme webhookUrl
curl -X PATCH https://cartflox.com/api/v1/config/webhook \
  -H "Authorization: Bearer cf_live_sec_xxxx" \
  -H "Content-Type: application/json" \
  -d '{ "webhookUrl": "https://xxxx.ngrok.io/api/webhooks/cartflox" }'
Webhooks

Sécurité des Webhooks

Cartflox signe chaque webhook avec une clé HMAC-SHA256 pour garantir que la requête provient bien d'Cartflox et n'a pas été altérée en transit.

Vérification de la signature

Chaque requête webhook contient le header cartflox-signature qui contient le timestamp et la signature HMAC.

Format du header

bash
cartflox-signature: t=1740823233,v1=5257a869e7ecebeda32affa62cdca3fa...

Algorithme de vérification

Vérification manuelle
import crypto from "crypto";

function verifyWebhookSignature(
  rawBody: string,
  signature: string,
  secret: string
): boolean {
  const parts = signature.split(",");
  const timestamp = parts.find(p => p.startsWith("t="))?.split("=")[1];
  const hash      = parts.find(p => p.startsWith("v1="))?.split("=")[1];

  if (!timestamp || !hash) return false;

  // Rejeter les événements de plus de 5 minutes (replay attack)
  const age = Math.abs(Date.now() / 1000 - parseInt(timestamp));
  if (age > 300) return false;

  // Calculer la signature attendue
  const payload = `${timestamp}.${rawBody}`;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");

  // Comparaison sécurisée (timing-safe)
  return crypto.timingSafeEqual(
    Buffer.from(hash),
    Buffer.from(expected)
  );
}

Avec le SDK (recommandé)

typescript
const event = cartflox.webhooks.constructEvent(
  rawBody,     // string — corps brut de la requête
  signature,   // string — header cartflox-signature
  webhookSecret // string — votre secret de signature
);
// Lance une erreur si la signature est invalide
✕ ImportantN'utilisez jamais req.body parsé par JSON. Vous devez utiliser le corps brut (raw body)pour la vérification de signature, sinon elle échouera toujours.

Protection contre les replay attacks

Le header contient un timestamp que vous devez valider. Si l'horodatage est trop ancien (plus de 5 minutes), rejetez la requête pour prévenir les attaques par rejeu.

typescript
const MAX_AGE = 5 * 60; // 5 minutes en secondes

const timestamp = parseInt(headerParts.find(p => p.startsWith("t="))!.split("=")[1]);
if (Date.now() / 1000 - timestamp > MAX_AGE) {
  throw new Error("Webhook trop ancien — possible replay attack");
}

Bonnes pratiques

Vérifiez toujours la signature avant de traiter l'événement
Validez que le timestamp est récent (< 5 minutes)
Stockez votre secret webhook dans une variable d'environnement
Répondez rapidement (< 5 s) et traitez en tâche de fond si nécessaire
Ne pas ignorer les erreurs de signature
Ne pas faire confiance à l'URL de redirection sans vérification API
SDK

SDK Node.js

Le SDK officiel Cartflox pour Node.js et TypeScript. Supporte CommonJS et ESM.

Installation

bash
npm install @cartflox/node
# ou
yarn add @cartflox/node
# ou
pnpm add @cartflox/node

Configuration

lib/cartflox.ts
import Cartflox from "@cartflox/node";

export const cartflox = new Cartflox(process.env.CARTFLOX_SECRET_KEY!, {
  // Options avancées
  timeout: 30000,        // Timeout en ms (défaut: 30s)
  maxRetries: 3,         // Nombre de retries automatiques
  idempotencyKey: true,  // Générer des clés d'idempotence automatiquement
});

Paiements

typescript
// Créer
const payment = await cartflox.payments.create({ ... });

// Récupérer
const payment = await cartflox.payments.retrieve("pi_01HXYZ...");

// Lister
const list = await cartflox.payments.list({ status: "completed", limit: 50 });

// Rembourser
const refund = await cartflox.payments.refund("pi_01HXYZ...", { amount: 5000 });

// Annuler (si en attente)
await cartflox.payments.cancel("pi_01HXYZ...");

Transferts

typescript
// Créer un transfert
const transfer = await cartflox.transfers.create({
  amount: 50000,
  currency: "XOF",
  destination: { type: "mobile_money", phone: "+22507070707", network: "wave" }
});

// Récupérer
const transfer = await cartflox.transfers.retrieve("tr_01HABC...");

// Lister
const list = await cartflox.transfers.list({ status: "completed" });

Webhooks

typescript
// Vérifier et parser l'événement
const event = cartflox.webhooks.constructEvent(rawBody, signature, secret);

// Accéder aux données
console.log(event.type);    // "payment.completed"
console.log(event.data.id); // "pi_01HXYZ..."

// Types TypeScript disponibles
import type { CartfloxEvent, Payment, Transfer } from "@cartflox/node";

TypeScript

Le SDK est entièrement typé. Vous bénéficiez de l'autocomplétion et de la validation des types.

typescript
import Cartflox, { Payment, CreatePaymentParams } from "@cartflox/node";

const params: CreatePaymentParams = {
  amount: 5000,
  currency: "XOF",
  method: "wave",
  customer: { phone: "+22507070707" }
};

const payment: Payment = await cartflox.payments.create(params);

Gestion des erreurs

typescript
import { CartfloxError } from "@cartflox/node";

try {
  const payment = await cartflox.payments.create({ ... });
} catch (err) {
  if (err instanceof CartfloxError) {
    console.error("Code:", err.code);       // INSUFFICIENT_FUNDS
    console.error("Message:", err.message); // "Solde insuffisant"
    console.error("Status:", err.status);   // 400
    console.error("RequestId:", err.requestId);
  }
}

Compatibilité

Node.js16, 18, 20, 22
TypeScript4.7+
ESM / CJSLes deux supportés
Edge RuntimeVercel Edge, Cloudflare Workers
SDK

SDK Python

Le SDK officiel Cartflox pour Python 3.8+. Compatible avec Django, FastAPI, Flask.

Installation

bash
pip install cartflox-python

Configuration

cartflox_client.py
import os
from cartflox import Cartflox

cartflox = Cartflox(
    api_key=os.environ["CARTFLOX_SECRET_KEY"],
    timeout=30,
    max_retries=3
)

Paiements

python
# Créer un paiement
payment = cartflox.payments.create(
    amount=25000,
    currency="XOF",
    method="wave",
    customer={
        "phone": "+22507070707",
        "email": "client@example.com",
        "name": "Kouassi Jean"
    },
    redirect_url="https://monsite.com/success",
    metadata={"order_id": "ORD-2026-001"}
)

print(payment.checkout_url)  # Rediriger le client

# Récupérer
payment = cartflox.payments.retrieve("pi_01HXYZ...")

# Lister avec filtres
payments = cartflox.payments.list(
    status="completed",
    currency="XOF",
    limit=50
)
for p in payments.data:
    print(p.id, p.amount, p.status)

# Rembourser
refund = cartflox.payments.refund(
    payment_id="pi_01HXYZ...",
    amount=5000,
    reason="requested_by_customer"
)

Webhooks avec FastAPI

main.py
from fastapi import FastAPI, Request, HTTPException
from cartflox import Cartflox, WebhookVerificationError

app = FastAPI()
cartflox = Cartflox(api_key=os.environ["CARTFLOX_SECRET_KEY"])

@app.post("/webhook/cartflox")
async def handle_webhook(request: Request):
    body = await request.body()
    signature = request.headers.get("cartflox-signature", "")

    try:
        event = cartflox.webhooks.construct_event(
            payload=body.decode("utf-8"),
            sig_header=signature,
            secret=os.environ["CARTFLOX_WEBHOOK_SECRET"]
        )
    except WebhookVerificationError:
        raise HTTPException(status_code=400, detail="Invalid signature")

    if event.type == "payment.completed":
        payment = event.data
        await fulfill_order(payment.metadata["order_id"])

    return {"received": True}

Webhooks avec Django

views.py
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from cartflox import Cartflox

cartflox = Cartflox(api_key=settings.CARTFLOX_SECRET_KEY)

@csrf_exempt
@require_POST
def cartflox_webhook(request):
    try:
        event = cartflox.webhooks.construct_event(
            payload=request.body.decode("utf-8"),
            sig_header=request.META.get("HTTP_CARTFLOX_SIGNATURE", ""),
            secret=settings.CARTFLOX_WEBHOOK_SECRET
        )
    except Exception:
        return JsonResponse({"error": "Invalid signature"}, status=400)

    if event.type == "payment.completed":
        Order.objects.filter(
            id=event.data.metadata["order_id"]
        ).update(status="paid")

    return JsonResponse({"received": True})

Gestion des erreurs

python
from cartflox import Cartflox, CartfloxError

try:
    payment = cartflox.payments.create(...)
except CartfloxError as e:
    print(f"Code: {e.code}")          # INSUFFICIENT_FUNDS
    print(f"Message: {e.message}")    # "Solde insuffisant"
    print(f"Status: {e.http_status}") # 400
except Exception as e:
    print(f"Erreur réseau: {e}")
SDK

SDK PHP

Le SDK officiel Cartflox pour PHP 8.0+. Compatible avec Laravel, Symfony, WordPress.

Installation

bash
composer require cartflox/cartflox-php

Configuration

config/cartflox.php
<?php
use Cartflox\Cartflox;

$cartflox = new Cartflox(getenv('CARTFLOX_SECRET_KEY'), [
    'timeout'     => 30,
    'max_retries' => 3,
]);

Paiements

php
<?php
// Créer un paiement
$payment = $cartflox->payments->create([
    'amount'       => 25000,
    'currency'     => 'XOF',
    'method'       => 'wave',
    'customer'     => [
        'phone' => '+22507070707',
        'email' => 'client@example.com',
        'name'  => 'Kouassi Jean',
    ],
    'redirect_url' => 'https://monsite.com/success',
    'metadata'     => ['order_id' => 'ORD-2026-001'],
]);

// Rediriger le client
header('Location: ' . $payment->checkout_url);
exit;

// Récupérer un paiement
$payment = $cartflox->payments->retrieve('pi_01HXYZ...');

// Lister
$list = $cartflox->payments->list(['status' => 'completed', 'limit' => 20]);
foreach ($list->data as $payment) {
    echo $payment->id . ' - ' . $payment->amount . ' ' . $payment->currency . PHP_EOL;
}

// Rembourser
$refund = $cartflox->payments->refund('pi_01HXYZ...', [
    'amount' => 5000,
    'reason' => 'requested_by_customer',
]);

Webhooks

webhook.php
<?php
use Cartflox\Cartflox;
use Cartflox\Exception\WebhookSignatureException;

$cartflox = new Cartflox(getenv('CARTFLOX_SECRET_KEY'));

$payload   = file_get_contents('php://input');
$signature = $_SERVER['HTTP_CARTFLOX_SIGNATURE'] ?? '';

try {
    $event = $cartflox->webhooks->constructEvent(
        $payload,
        $signature,
        getenv('CARTFLOX_WEBHOOK_SECRET')
    );
} catch (WebhookSignatureException $e) {
    http_response_code(400);
    echo json_encode(['error' => 'Signature invalide']);
    exit;
}

switch ($event->type) {
    case 'payment.completed':
        $payment = $event->data;
        // Mettre à jour la commande en base de données
        updateOrderStatus($payment->metadata['order_id'], 'paid');
        break;

    case 'payment.failed':
        notifyCustomer($event->data->customer->email);
        break;
}

http_response_code(200);
echo json_encode(['received' => true]);

Laravel — Intégration rapide

app/Http/Controllers/PaymentController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Cartflox\Cartflox;

class PaymentController extends Controller
{
    private Cartflox $cartflox;

    public function __construct()
    {
        $this->cartflox = new Cartflox(config('services.cartflox.secret'));
    }

    public function create(Request $request)
    {
        $payment = $this->cartflox->payments->create([
            'amount'       => $request->amount,
            'currency'     => 'XOF',
            'method'       => $request->method ?? 'auto',
            'customer'     => ['phone' => $request->phone],
            'redirect_url' => route('payment.success'),
            'metadata'     => ['order_id' => $request->order_id],
        ]);

        return redirect($payment->checkout_url);
    }

    public function webhook(Request $request)
    {
        $event = $this->cartflox->webhooks->constructEvent(
            $request->getContent(),
            $request->header('cartflox-signature'),
            config('services.cartflox.webhook_secret')
        );

        if ($event->type === 'payment.completed') {
            // Traiter le paiement
        }

        return response()->json(['received' => true]);
    }
}
SDK

SDK Flutter

Le SDK officiel Cartflox pour Flutter (iOS & Android). Intègre la page de paiement native ou via WebView.

Installation

pubspec.yaml
dependencies:
  cartflox_flutter: ^2.0.0
bash
flutter pub get

Configuration Android

android/app/src/main/AndroidManifest.xml
<activity android:name=".MainActivity"
    android:launchMode="singleTop">
  <intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="cartflox" android:host="callback"/>
  </intent-filter>
</activity>

Initialisation

main.dart
import 'package:cartflox_flutter/cartflox_flutter.dart';

void main() {
  Cartflox.initialize(
    publicKey: const String.fromEnvironment('CARTFLOX_PUBLIC_KEY'),
    environment: CartfloxEnvironment.production, // ou .sandbox
  );
  runApp(const MyApp());
}

Lancer un paiement

dart
import 'package:cartflox_flutter/cartflox_flutter.dart';

class PaymentScreen extends StatelessWidget {
  Future<void> _startPayment(BuildContext context) async {
    final result = await CartfloxCheckout.present(
      context: context,
      params: PaymentParams(
        amount: 25000,
        currency: 'XOF',
        method: PaymentMethod.auto, // Affiche toutes les méthodes
        customer: Customer(
          phone: '+22507070707',
          email: 'client@example.com',
          name: 'Kouassi Jean',
        ),
        metadata: {'orderId': 'ORD-2026-001'},
      ),
    );

    switch (result.status) {
      case PaymentStatus.completed:
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text('✅ Paiement réussi !')),
        );
        // Valider la commande côté serveur via webhook
        break;
      case PaymentStatus.failed:
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text('❌ Échec : ${result.errorMessage}')),
        );
        break;
      case PaymentStatus.cancelled:
        // Le client a annulé
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => _startPayment(context),
      child: const Text('Payer maintenant'),
    );
  }
}

Personnalisation du thème

dart
CartfloxCheckout.present(
  context: context,
  params: PaymentParams( ... ),
  theme: CartfloxTheme(
    primaryColor: const Color(0xFF10B981),  // Votre couleur de marque
    backgroundColor: const Color(0xFF1A1A1A),
    textColor: Colors.white,
    borderRadius: 16.0,
    logoUrl: 'https://monsite.com/logo.png',
    businessName: 'Ma Boutique',
  ),
);
⚠ AttentionN'utilisez jamais votre clé secrète dans votre application mobile. Utilisez uniquement la clé publique côté client. La création de paiement doit se faire côté serveur.

Architecture recommandée

dart
// 1. Votre backend crée le paiement
// POST https://votre-api.com/create-payment
// → Retourne { checkoutToken: "pi_01HXYZ...", checkoutUrl: "..." }

// 2. Le SDK Flutter utilise le token
final paymentIntent = await api.createPayment(amount: 25000);

final result = await CartfloxCheckout.presentWithToken(
  context: context,
  checkoutToken: paymentIntent.checkoutToken,
);
Plugins

Plugin WooCommerce

Acceptez les paiements Mobile Money et carte directement dans votre boutique WooCommerce, sans écrire une ligne de code.

Installation

Option 1 — Via le dashboard WordPress

  1. Allez dans Extensions → Ajouter
  2. Recherchez Cartflox Payment
  3. Cliquez sur Installer maintenant puis Activer

Option 2 — Installation manuelle

bash
# Télécharger le plugin
curl -L https://downloads.cartflox.com/woocommerce/cartflox-payment-latest.zip -o cartflox-payment.zip

# Dézipper dans le dossier plugins
unzip cartflox-payment.zip -d /var/www/html/wp-content/plugins/

Configuration

  1. Allez dans WooCommerce → Réglages → Paiements
  2. Activez Cartflox Payment
  3. Cliquez sur Gérer
  4. Entrez vos clés API (sandbox pour les tests, production pour les vrais paiements)
  5. Configurez l'URL webhook : copiez l'URL affichée dans WordPress et ajoutez-la dans votre dashboard Cartflox
  6. Sauvegardez

Configuration du webhook

L'URL webhook WordPress générée par le plugin :

bash
https://votre-boutique.com/wc-api/cartflox_webhook

Ajoutez cette URL dans votre dashboard Cartflox → Paramètres → Webhooks → Ajouter un endpoint et sélectionnez tous les événements payment.*.

Méthodes de paiement disponibles

Orange Money
Wave
MTN MoMo
Moov Money
Free Money
Visa / Mastercard

Options avancées

OptionDescription
Titre affichéTexte affiché à la caisse (défaut: "Cartflox Payment")
DescriptionDescription affichée sous le titre
Méthodes activesChoisir quelles méthodes de paiement afficher
Mode sandboxActiver/désactiver le mode test
Logo personnaliséAfficher votre logo sur la page de paiement
Couleur de marqueCouleur principale de la page de paiement
✓ Bon à savoirLe plugin gère automatiquement la mise à jour des statuts de commande WooCommerce lors de la réception des webhooks :
payment.completed → Statut commande : Traitement
payment.failed → Statut commande : Échoué
refund.completed → Statut commande : Remboursé

Compatibilité

WordPress5.8+
WooCommerce6.0+
PHP8.0+
HPOSSupporté (High-Performance Order Storage)
Référence

Codes d'erreur

Lorsqu'une requête échoue, l'API retourne un objet d'erreur structuré avec un code machine et un message lisible.

Format d'erreur

Réponse d'erreur
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_FUNDS",
    "message": "Le solde du compte est insuffisant pour effectuer cette transaction.",
    "details": {
      "field": "customer.phone",
      "value": "+22507070707"
    },
    "requestId": "req_01HXYZ123"
  }
}

Codes HTTP

CodeSignification
200 OKRequête réussie
201 CreatedRessource créée avec succès
400 Bad RequestRequête malformée
401 UnauthorizedAuthentification invalide ou manquante
403 ForbiddenAccès refusé
404 Not FoundRessource introuvable
422 Unprocessable EntityErreur de validation métier
429 Too Many RequestsLimite de débit dépassée
500 Internal Server ErrorErreur côté Cartflox
503 Service UnavailableService ou opérateur temporairement indisponible

Codes d'erreur métier

CodeHTTPDescription
INVALID_API_KEY401La clé API est invalide ou a été révoquée.
UNAUTHORIZED401Authentification requise.
FORBIDDEN403Vous n'avez pas les droits pour cette opération.
NOT_FOUND404La ressource demandée est introuvable.
VALIDATION_ERROR422Les données envoyées sont invalides. Vérifiez les champs requis.
INVALID_AMOUNT422Le montant est invalide (trop petit, trop grand ou non entier).
INVALID_CURRENCY422La devise n'est pas supportée.
INVALID_PHONE422Le numéro de téléphone est invalide ou non supporté.
METHOD_NOT_AVAILABLE422La méthode de paiement n'est pas disponible dans ce pays.
INSUFFICIENT_FUNDS422Le solde du client est insuffisant.
ACCOUNT_NOT_REGISTERED422Le numéro n'est pas enregistré auprès de l'opérateur.
TRANSACTION_LIMIT422La limite de transaction journalière ou mensuelle est atteinte.
PAYMENT_EXPIRED422La session de paiement a expiré.
ALREADY_REFUNDED422Ce paiement a déjà été intégralement remboursé.
REFUND_AMOUNT_EXCEEDED422Le montant de remboursement dépasse le montant payé.
RATE_LIMIT_EXCEEDED429Trop de requêtes. Attendez avant de réessayer.
PROVIDER_UNAVAILABLE503L'opérateur de paiement est temporairement indisponible.
INTERNAL_ERROR500Erreur interne Cartflox. Réessayez ou contactez le support.

Gestion des erreurs côté client

typescript
import Cartflox, { CartfloxError } from "@cartflox/node";

try {
  const payment = await cartflox.payments.create({ ... });
} catch (err) {
  if (err instanceof CartfloxError) {
    switch (err.code) {
      case "INSUFFICIENT_FUNDS":
        return res.status(400).json({ message: "Solde insuffisant, veuillez recharger votre compte." });
      case "ACCOUNT_NOT_REGISTERED":
        return res.status(400).json({ message: "Ce numéro n'est pas enregistré sur ce réseau." });
      case "METHOD_NOT_AVAILABLE":
        return res.status(400).json({ message: "Cette méthode n'est pas disponible dans votre pays." });
      case "RATE_LIMIT_EXCEEDED":
        // Retry après un délai
        await sleep(1000);
        return retryPayment();
      default:
        // Erreur inattendue — logguer et afficher message générique
        logger.error("Cartflox error", { code: err.code, requestId: err.requestId });
        return res.status(500).json({ message: "Une erreur est survenue. Veuillez réessayer." });
    }
  }
  throw err; // Re-throw si ce n'est pas une erreur Cartflox
}
Référence

Limites de taux (Rate Limiting)

Cartflox applique des limites de débit pour garantir la stabilité de la plateforme. Les limites sont appliquées par clé API.

Limites par plan

PlanRequêtes / minuteRequêtes / jourWebhooks / sec
Sandbox6010 00010
Starter12050 00030
Business600500 000100
Enterprise3 000Illimité500

Headers de réponse

Chaque réponse API inclut des headers indiquant votre utilisation :

Headers HTTP
X-RateLimit-Limit:     120       # Limite totale par minute
X-RateLimit-Remaining: 87        # Requêtes restantes dans la fenêtre
X-RateLimit-Reset:     1740823293 # Timestamp UNIX de réinitialisation
X-RateLimit-Window:    60         # Durée de la fenêtre en secondes

Gestion du dépassement

Quand la limite est atteinte, l'API retourne une erreur 429 Too Many Requests avec un header Retry-After :

429 Too Many Requests
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1740823293

{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Limite de débit atteinte. Veuillez attendre 23 secondes.",
    "retryAfter": 23
  }
}

Retry avec backoff exponentiel

typescript
async function apiCallWithRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (err: any) {
      if (err.code === "RATE_LIMIT_EXCEEDED" && attempt < maxRetries) {
        const delay = (err.retryAfter ?? Math.pow(2, attempt)) * 1000;
        console.log(`Rate limit. Retry dans ${delay}ms...`);
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      throw err;
    }
  }
  throw new Error("Max retries atteint");
}

// Utilisation
const payment = await apiCallWithRetry(() =>
  cartflox.payments.create({ ... })
);

Bonnes pratiques

Utilisez les webhooks plutôt que le polling pour surveiller les statuts
Implémentez un backoff exponentiel pour les retries
Mettez en cache les réponses GET quand c'est possible
Utilisez des clés d'idempotence pour éviter les doublons en cas de retry
Ne faites pas de polling en boucle sur les paiements en attente
Ne créez pas plusieurs paiements identiques simultanément

Idempotence

Pour les requêtes POST, vous pouvez envoyer un header Idempotency-Key pour éviter les doublons en cas de retry :

bash
curl -X POST https://api.cartflox.com/v2/payments \
  -H "Authorization: Bearer sk_live_xxxx" \
  -H "Idempotency-Key: order_ORD-2026-001_attempt_1" \
  -H "Content-Type: application/json" \
  -d '{ ... }'

Si vous renvoyez la même clé d'idempotence dans les 24h, Cartflox retourne le résultat de la première requête sans re-créer de paiement.

Référence

Changelog

Historique des mises à jour de l'API Cartflox et des SDKs officiels.

Nouveau
Amélio.
Correction
Breaking
Déprécié
v2.1.0minor
Mars 2026
NouveauAjout du support Flutter SDK pour iOS & Android
NouveauNouvelle méthode de paiement : Free Money (Sénégal)
NouveauEndpoint GET /v2/transfers pour lister les transferts
Amélio.Performance améliorée du checkout mobile (-40% temps de chargement)
CorrectionCorrection d'un bug de signature webhook avec des payloads > 1MB
v2.0.0major
Janvier 2026
BreakingNouvelle version majeure de l'API — migration requise depuis v1
NouveauSupport du transfert d'argent inter-pays (15+ pays)
NouveauSDK officiel Python avec support Django & FastAPI
NouveauSystème d'idempotence sur toutes les requêtes POST
NouveauPagination par curseur sur toutes les routes de liste
NouveauNouveau dashboard avec analytics avancés
Amélio.Réponse API unifiée avec champ success/data/error
Amélio.Retry automatique avec backoff dans les SDKs
DépréciéDépréciation de l'API v1 (support jusqu'au 1er juillet 2026)
v1.8.2patch
Novembre 2025
CorrectionCorrection du header de signature webhook pour les requêtes Express
CorrectionCorrection du timeout sur les paiements MTN MoMo en Côte d'Ivoire
Amélio.Amélioration des messages d'erreur INSUFFICIENT_FUNDS
v1.8.0minor
Octobre 2025
NouveauSupport Moov Money Bénin & Togo
NouveauPlugin WooCommerce v2 avec support HPOS
NouveauNouveau webhook : payment.expired
Amélio.Réduction du délai de confirmation Wave de 5s à 2s
v1.7.0minor
Août 2025
NouveauRemboursements partiels disponibles sur tous les plans
NouveauSupport Airtel Money (Kenya, Nigeria, Ghana)
Amélio.IP Allowlist disponible sur les plans Business et Enterprise

Restez informé des mises à jour

Abonnez-vous à notre newsletter technique ou suivez notre GitHub pour être notifié des nouvelles versions.