Passer au contenu principal

Documentation Index

Fetch the complete documentation index at: https://developers.ligdicash.com/llms.txt

Use this file to discover all available pages before exploring further.

Cette page recense les erreurs les plus fréquentes rencontrées en intégration et en production, issues du retour terrain des marchands LigdiCash.
Les sous-codes (Echec (CodeXX)) sont propres à chaque endpoint. Consultez la liste des sous-codes pour la correspondance complète. Pour décoder un sous-code en production, utilisez le champ wiki retourné dans la réponse.

Authentification échouée

Symptôme : response_code: "01", response_text: "Echec (Code00)" sur chaque requête. Causes probables :
  • L’en-tête Apikey est absent ou incorrect
  • L’en-tête Authorization ne suit pas le format Bearer {AUTH_TOKEN}
  • Le token d’autorisation a expiré ou été révoqué
  • Les clés utilisées appartiennent à un autre projet API
Solution :
cURL
curl -X POST https://app.ligdicash.com/pay/v01/... \
  -H "Apikey: {API_KEY}" \
  -H "Authorization: Bearer {AUTH_TOKEN}" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json"
Vérifiez vos clés dans le dashboard LigdiCash sous le projet API concerné. Si le problème persiste, contactez le support.

Payin ou payout non activé sur le compte

Symptôme : response_code: "01", response_text: "Echec (Code01)" dès la création de la transaction. Cause : La fonctionnalité payin ou payout n’est pas activée sur votre projet API LigdiCash. Solution : Contactez l’équipe LigdiCash pour activer la fonctionnalité concernée : developper@ligdicash.com ou via votre Partner Manager. Précisez votre identifiant de projet API.

Mauvaise route pour le payout

Symptôme : response_code: "01", response_text: "Echec (Code02)" sur l’endpoint withdrawal/create, avec la description “Customer not registered on the platform”. Cause : L’endpoint POST /pay/v01/withdrawal/create effectue un payout vers le wallet LigdiCash du client. Il exige donc que le client ait un compte enregistré sur la plateforme LigdiCash. Si vous souhaitez envoyer directement vers un numéro mobile money sans compte LigdiCash préalable, vous devez utiliser l’autre endpoint.
LigdiCash propose deux routes de payout avec des usages distincts :
EndpointDestinationCondition
POST /pay/v01/withdrawal/createWallet LigdiCash du clientLe client doit avoir un compte LigdiCash
POST /pay/v01/straight/payoutNuméro mobile money directementAucun compte LigdiCash requis
Utiliser withdrawal/create pour un client sans compte LigdiCash retourne systématiquement Echec (Code02).
Consultez Payout vers wallet LigdiCash et Payout vers mobile money pour choisir la bonne route selon votre cas d’usage.

IP non whitelistée

Symptôme : response_code: "01", response_text: "Echec (Code03)" (createInvoice) ou "Echec (Code06)" (createWithdrawal / createStraightWithdrawal) — “IP Denied”. Cause : Les endpoints de payout requièrent que l’adresse IP du serveur appelant soit autorisée sur votre projet LigdiCash. Si votre serveur utilise une IP dynamique (qui change à chaque redémarrage ou reconnexion), vous serez bloqué dès que l’IP change. Contrainte importante : LigdiCash limite le nombre d’IPs whitelistées à 3 adresses maximum par projet. Solution :
  1. Identifiez l’adresse IP fixe de votre serveur de production.
  2. Transmettez-la au support technique LigdiCash (developper@ligdicash.com) pour qu’il l’ajoute à la whitelist de votre projet — cette opération ne peut pas être faite depuis le dashboard marchand.
  3. Si votre infrastructure utilise des IPs dynamiques (hébergement mutualisé, VPS sans IP fixe, CI/CD), vous devez soit :
    • Passer par un proxy sortant à IP fixe pour tous vos appels vers LigdiCash
    • Migrer vers un hébergement avec IP dédiée fixe
La contrainte des 3 IPs maximum est importante si vous avez plusieurs environnements (développement, staging, production). Réservez les 3 slots à vos serveurs de production et testez en staging depuis le même IP si possible. Anticipez cette demande avant la mise en production — le délai de configuration dépend du support LigdiCash.

Solde marchand insuffisant par opérateur

Symptôme : response_code: "01", response_text: "Echec (Code04)" (createWithdrawal) ou "Echec (Code08)" (createStraightWithdrawal) — “Merchant balance low” ou “Merchant operator account low balance”. Cause : Votre compte marchand LigdiCash dispose d’un solde distinct par opérateur. Un payout vers Orange Money Burkina débitera votre solde Orange Burkina, pas votre solde global. Si ce solde opérateur est insuffisant, le payout échoue même si votre solde global est positif. Solution : Rechargez le solde de l’opérateur concerné via le dashboard LigdiCash. Surveillez les soldes par opérateur indépendamment et mettez en place des alertes de bas solde pour chaque opérateur que vous utilisez.

Montant hors limites

Symptôme : response_code: "01", response_text: "Echec (Code02)" sur createInvoice — “Wrong amount”. Causes probables :
  • Montant inférieur à 9 XOF ou supérieur à 2 000 000 XOF (limites globales LigdiCash)
  • Montant non entier (500.5 au lieu de 500 ou 501)
  • Plafond quotidien du client mobile money atteint côté opérateur
Le XOF n’a pas de sous-unité. Le montant doit toujours être un entier. 500.5 sera rejeté.
Validez le montant côté serveur avant d’appeler l’API :
Node.js
function validerMontant(montant) {
  if (!Number.isInteger(montant)) throw new Error("Montant non entier");
  if (montant < 9)        throw new Error("Montant minimum : 9 XOF");
  if (montant > 2000000)  throw new Error("Montant maximum : 2 000 000 XOF");
}

Transaction en pending malgré un OTP valide

Symptôme : La transaction reste indéfiniment à status: "pending" alors que le client affirme avoir généré l’OTP correctement. Cause : Pour les opérateurs en mode OTP USSD (Orange Money Burkina Faso…), le client génère un OTP via son menu USSD en saisissant le montant exact de la transaction. Si vous avez configuré votre intégration pour que les frais de transaction soient à la charge du client, le montant que le client doit saisir dans son USSD est le montant de base plus les frais. Si le client ne saisit que le montant de base, l’OTP est valide mais le montant attendu ne correspond pas — LigdiCash ne peut pas valider la transaction et la laisse en pending. Exemple :
  • Montant de la transaction : 10 000 XOF
  • Frais à la charge du client : 200 XOF
  • Montant à saisir dans le USSD : 10 200 XOF
  • Si le client saisit 10 000 XOF → OTP valide mais montant incorrect → pending indéfini
Solution : Si les frais sont à la charge du client, votre interface doit afficher clairement le montant total à saisir dans l’USSD, frais inclus. Communiquez ce montant dans vos instructions à l’écran avant que le client ne compose le code USSD.
⚠️ Composez le code USSD avec le montant exact : 10 200 XOF (10 000 XOF + 200 XOF de frais)
Si les frais sont à votre charge (côté marchand), le client saisit uniquement le montant de la transaction, et vous l’absorbez dans votre marge.

Incompréhension du statut pending

Symptôme : Vous attendez un callback avec status: "notcompleted" après un OTP invalide ou expiré, mais aucun callback n’arrive — la transaction reste indéfiniment en pending. Cause : LigdiCash ne considère pas automatiquement une transaction comme échouée parce que l’OTP était incorrect. Du point de vue de LigdiCash, la transaction peut encore aboutir si le client fournit le bon OTP — la fenêtre d’attente est indéfinie. LigdiCash n’envoie un callback notcompleted que lorsque l’opérateur lui-même communique un signal d’échec définitif. Tant que ce signal n’arrive pas, la transaction reste en pending, qu’il s’écoule 5 minutes ou plusieurs heures.
Ne vous fiez pas à LigdiCash pour décider quand une transaction est “expirée”. C’est à votre système de définir un timeout métier et d’agir en conséquence.
Solution : Définissez un timeout côté marchand (par exemple 15 minutes après la création) au-delà duquel vous passez la transaction en timeout dans votre base et notifiez le client pour relancer la démarche :
Node.js
// Vérification périodique des transactions pending
async function verifierTransactionsPending() {
  const timeout = new Date(Date.now() - 15 * 60 * 1000); // 15 minutes

  const pendingTransactions = await db.transactions.findAll({
    where: {
      status: "pending",
      created_at: { [Op.lt]: timeout },
    },
  });

  for (const tx of pendingTransactions) {
    // Dernier appel à confirm avant d'abandonner
    const confirm = await appellerConfirm(tx.ligdicash_token);

    if (confirm.status === "completed") {
      await tx.update({ status: "completed" });
    } else {
      // Toujours pending après 15 min → timeout côté marchand
      await tx.update({ status: "timeout" });
      await notifierClient(tx, "Votre paiement n'a pas abouti. Veuillez réessayer.");
    }
  }
}

Double callback non géré

Symptôme : Une même transaction déclenche deux actions dans votre système (double livraison, double crédit de compte…). Cause : LigdiCash envoie systématiquement deux requêtes POST pour chaque événement de callback : une en application/x-www-form-urlencoded et une en application/json. Ce comportement est normal et documenté. Si votre handler callback ne déduplique pas, il traitera les deux requêtes. Solution : Vérifiez en base si la transaction a déjà été traitée avant tout traitement métier :
Node.js
async function handleCallback(payload) {
  const transactionId = payload.custom_data?.find(
    e => e.keyof_customdata === "transaction_id"
  )?.valueof_customdata;

  const transaction = await db.transactions.findOne({
    where: { transaction_id: transactionId }
  });

  if (transaction?.status === "completed") {
    return res.status(200).send("OK"); // Déjà traité
  }

  // ... traitement métier
}
Voir Idempotence et déduplication pour le pattern complet.

Callback reçu mais transaction introuvable en base

Symptôme : Vous recevez un callback valide mais votre système ne retrouve pas la transaction correspondante, ou l’appel à confirm retourne Echec (Code02) — Invoice not found. Causes probables :
  • Vous recherchez la transaction avec un identifiant autre que le transaction_id que vous avez stocké à la création (ID interne, numéro de commande, token callback…)
  • Vous utilisez le token du payload callback pour appeler confirm, alors qu’il faut utiliser le token de création
Deux tokens différents circulent dans votre intégration :
TokenUsage
Token de créationRetourné dans response_text à la créationÀ stocker en base, à utiliser pour confirm
Token de callbackPrésent dans le payload callbackÀ ne jamais utiliser pour confirm
Ces deux tokens sont distincts et non interchangeables.
Solution : Stockez le transaction_id que vous avez envoyé dans custom_data à la création, et retrouvez la transaction avec ce même transaction_id dans le callback :
Node.js
// À la création — stocker le transaction_id et le token LigdiCash
await db.transactions.create({
  transaction_id: "TXN-MON-ID-INTERNE",  // votre identifiant
  ligdicash_token: response.token,         // token LigdiCash de création
  status: "pending",
});

// Dans le callback — retrouver par transaction_id, pas par token callback
function extraireTransactionId(callbackPayload) {
  const customData = callbackPayload.custom_data;
  if (!Array.isArray(customData)) return null;
  const entry = customData.find(e => e.keyof_customdata === "transaction_id");
  return entry?.valueof_customdata ?? null;
}

const transactionId = extraireTransactionId(callbackPayload);
const transaction = await db.transactions.findOne({
  where: { transaction_id: transactionId }
});
Voir Pattern transaction_id et Sécurisation du callback pour les patterns complets.

Pages associées