Jèko
Paiements

Gérer les échecs de paiement

Comprendre les raisons d'échec des paiements et comment les gérer

Vue d'ensemble

Un paiement échoue le plus souvent parce que le lien a expiré ou a déjà servi, parce qu'un identifiant est inconnu (storeId, deviceId, demande de paiement), ou parce que l'opérateur refuse l'opération. Cette page liste chaque erreur renvoyée par l'API et ce qu'il faut en faire.

Statuts de paiement

Seules les demandes de paiement ont un statut. Les liens de paiement, eux, exposent canReceivePayments.

Les demandes de paiement peuvent avoir les statuts suivants :

  • pending : Le paiement est en attente de confirmation
  • success : Le paiement a été effectué avec succès
  • error : Le paiement a échoué

Pour les liens de paiement, utilisez le champ canReceivePayments (booléen) pour vérifier si le lien peut accepter de nouveaux paiements.

Erreurs communes

Lien expiré

Erreur : link_expired ou payment_link_expired

Message : "Payment link has expired"

Cause : Le lien de paiement a expiré et ne peut plus accepter de paiements.

Solution :

  • Vérifiez toujours canReceivePayments avant de diriger un client vers un lien
  • Créez un nouveau lien de paiement si le lien a expiré
  • Informez le client que le lien a expiré et proposez de créer un nouveau lien

Exemple de gestion :

async function checkAndRedirectToPayment(paymentLinkId) {
  // Vérifier la disponibilité du lien
  const response = await fetch(
    `https://api.jeko.africa/partner_api/payment_links/${paymentLinkId}`,
    {
      headers: {
        'X-API-KEY': apiKey,
        'X-API-KEY-ID': apiKeyId
      }
    }
  );
  
  const paymentLink = await response.json();
  
  if (!paymentLink.canReceivePayments) {
    // Le lien a expiré ou a été utilisé
    if (paymentLink.allowMultiplePayments === false) {
      // Lien à usage unique - créer un nouveau lien
      const newLink = await createNewPaymentLink(paymentLink.storeId, paymentLink.amount);
      return newLink.link;
    } else {
      // Lien expiré - informer l'utilisateur
      throw new Error('Le lien de paiement a expiré. Veuillez créer un nouveau lien.');
    }
  }
  
  return paymentLink.link;
}

Lien déjà utilisé

Erreur : link_used ou payment_link_already_used

Message : "Payment link has already been used"

Cause : Le lien de paiement est à usage unique (allowMultiplePayments: false) et a déjà été utilisé pour un paiement réussi.

Solution :

  • Vérifiez canReceivePayments avant de diriger un client vers un lien
  • Pour les liens à usage unique, créez un nouveau lien pour chaque paiement
  • Informez le client que le lien a déjà été utilisé

Exemple de gestion :

async function handlePaymentLink(paymentLinkId) {
  const paymentLink = await getPaymentLink(paymentLinkId);
  
  if (!paymentLink.canReceivePayments) {
    if (paymentLink.allowMultiplePayments === false) {
      // Lien à usage unique déjà utilisé
      // Créer un nouveau lien
      const newLink = await createPaymentLink({
        storeId: paymentLink.storeId,
        title: paymentLink.title,
        amountCents: paymentLink.amount.amount,
        currency: paymentLink.amount.currency,
        allowMultiplePayments: false
      });
      
      return {
        error: 'LINK_USED',
        message: 'Ce lien a déjà été utilisé. Un nouveau lien a été créé.',
        newLink: newLink.link
      };
    }
  }
  
  return { link: paymentLink.link };
}

Demande de paiement non trouvée (404 Not Found)

Erreur : payment_request_not_found

Message : "Payment request not found"

Cause : La demande de paiement n'existe pas ou n'appartient pas à votre entreprise.

Solution :

  • Vérifiez que l'ID de la demande de paiement est correct
  • Vérifiez que la demande appartient à votre entreprise
  • Vérifiez que la demande n'a pas été supprimée

Magasin non trouvé (404 Not Found)

Erreur : store_not_found

Message : "Store not found"

Cause : Le storeId fourni n'existe pas ou n'appartient pas à votre entreprise.

Solution :

  • Vérifiez que le storeId est correct
  • Utilisez l'endpoint pour lister vos magasins
  • Assurez-vous que le magasin existe et est actif

Dispositif non trouvé (422 Validation Error)

Erreur : validation_error

Message : "The paymentDetails.data.deviceId field is required when paymentDetails.type is soundbox"

Cause : Pour les paiements Jèko Box, le deviceId est requis mais manquant ou invalide.

Solution :

  • Vérifiez que le deviceId est fourni pour les paiements Jèko Box
  • Vérifiez que le deviceId existe via l'endpoint
  • Assurez-vous que le terminal est actif et associé au bon magasin

Erreur de validation (422 Unprocessable Entity)

Erreur : validation_error

Message : "Validation error"

Cause : Les données fournies ne respectent pas les règles de validation.

Problèmes courants :

  • Montant inférieur au minimum (100 centimes pour payment_requests, 10000 centimes pour payment_links) ou qui n'est pas un multiple de 100
  • Code devise invalide (doit être ISO 4217, 3 caractères)
  • Référence manquante ou invalide (5 à 100 caractères)
  • Titre manquant ou invalide pour payment_links (10-255 caractères)
  • URLs de callback invalides (doivent être des URLs bien formées ; HTTPS recommandé)
  • Méthode de paiement invalide

Solution :

  • Vérifiez que tous les champs requis sont présents
  • Vérifiez que les valeurs respectent les contraintes (montant minimum, longueur, format)
  • Vérifiez que les URLs sont valides et utilisent HTTPS

Paiement échoué

Erreur : payment_failed ou transaction_failed

Message : "Payment failed"

Cause : Le paiement a échoué côté opérateur (Mobile Money ou banque).

Solution :

  • Vérifiez que le client a suffisamment de fonds
  • Vérifiez que le compte du client est actif
  • Proposez au client de réessayer
  • Contactez le support si le problème persiste

Clé API invalide (401 Unauthorized)

Erreur : unauthorized

Message : "Unauthorized"

Cause : Les clés API sont invalides, manquantes ou expirées.

Solution :

  • Vérifiez que les en-têtes X-API-KEY et X-API-KEY-ID sont présents
  • Vérifiez que les clés API sont correctes
  • Régénérez vos clés API depuis le Dashboard Business si nécessaire

Accès interdit (403 Forbidden)

Erreur : forbidden ou forbidden_action

Message : "Forbidden" ou "Action interdite"

Cause : La clé API n'a pas la permission d'accéder à cette ressource ou d'effectuer cette action.

Solution :

  • Vérifiez les permissions de votre clé API
  • Contactez le support pour vérifier les permissions de votre compte

Conflit de référence (409 Conflict)

Erreur : payment_request_exists_with_reference

Message : "Une demande de paiement avec cette référence existe déjà"

Cause : Une demande de paiement avec la même référence existe déjà.

Solution :

  • Utilisez des références uniques pour chaque demande de paiement
  • Incluez un identifiant unique (timestamp, UUID, orderId) dans la référence

Gestion des erreurs dans votre application

Vérification préventive

Avant de créer un paiement, effectuez ces vérifications :

  1. Vérifier l'existence du magasin :
curl -X GET "https://api.jeko.africa/partner_api/stores/{storeId}" \
  -H "X-API-KEY: your_api_key_here" \
  -H "X-API-KEY-ID: your_api_key_id_here"
  1. Pour les paiements Jèko Box, vérifier le terminal :
curl -X GET "https://api.jeko.africa/partner_api/devices" \
  -H "X-API-KEY: your_api_key_here" \
  -H "X-API-KEY-ID: your_api_key_id_here"
  1. Pour les liens de paiement, vérifier la disponibilité :
curl -X GET "https://api.jeko.africa/partner_api/payment_links/{paymentLinkId}" \
  -H "X-API-KEY: your_api_key_here" \
  -H "X-API-KEY-ID: your_api_key_id_here"

Gestion des erreurs dans le code

async function createPaymentRequest(paymentData) {
  try {
    // Vérifications préventives
    await validateStore(paymentData.storeId);
    
    if (paymentData.type === 'soundbox') {
      await validateDevice(paymentData.deviceId);
    }
    
    // Créer la demande de paiement
    const response = await fetch('https://api.jeko.africa/partner_api/payment_requests', {
      method: 'POST',
      headers: {
        'X-API-KEY': apiKey,
        'X-API-KEY-ID': apiKeyId,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(paymentData)
    });
    
    if (!response.ok) {
      const error = await response.json();
      
      // Gérer les erreurs spécifiques
      switch (error.id) {
        case 'payment_request_exists_with_reference':
          throw new Error('Une demande avec cette référence existe déjà. Utilisez une référence unique.');
        case 'store_not_found':
          throw new Error('Magasin introuvable. Vérifiez le storeId.');
        case 'validation_error':
          throw new Error(`Erreur de validation: ${error.extras?.errors?.map(e => e.message).join(', ')}`);
        case 'unauthorized':
          throw new Error('Clés API invalides. Vérifiez vos identifiants.');
        default:
          throw new Error(`Erreur: ${error.message}`);
      }
    }
    
    return await response.json();
  } catch (error) {
    console.error('Erreur lors de la création du paiement:', error);
    throw error;
  }
}

async function handlePaymentLink(paymentLinkId) {
  try {
    // Vérifier la disponibilité du lien
    const response = await fetch(
      `https://api.jeko.africa/partner_api/payment_links/${paymentLinkId}`,
      {
        headers: {
          'X-API-KEY': apiKey,
          'X-API-KEY-ID': apiKeyId
        }
      }
    );
    
    if (!response.ok) {
      const error = await response.json();
      
      if (error.id === 'payment_link_not_found') {
        throw new Error('Lien de paiement introuvable.');
      }
      
      throw new Error(`Erreur: ${error.message}`);
    }
    
    const paymentLink = await response.json();
    
    if (!paymentLink.canReceivePayments) {
      if (paymentLink.allowMultiplePayments === false) {
        throw new Error('LINK_USED', 'Ce lien a déjà été utilisé.');
      } else {
        throw new Error('LINK_EXPIRED', 'Ce lien a expiré.');
      }
    }
    
    return paymentLink.link;
  } catch (error) {
    console.error('Erreur lors de la vérification du lien:', error);
    throw error;
  }
}

Utilisation des webhooks pour le suivi

Configurez des webhooks pour être notifié automatiquement des changements de statut des paiements. Cela vous permet de :

  • Suivre les paiements en temps réel
  • Gérer les échecs automatiquement
  • Mettre à jour votre système lorsque le statut change

Consultez la documentation Webhooks pour plus d'informations.

Bonnes pratiques

  1. Vérifications préventives : Toujours vérifier les prérequis avant de créer un paiement
  2. Gestion d'erreurs : Traitez chaque code d'erreur listé ci-dessus
  3. Logging : Enregistrez toutes les erreurs pour le débogage et l'analyse
  4. Messages utilisateur : Informez l'utilisateur des erreurs de manière claire et actionnable
  5. Retry logic : Pour les erreurs temporaires, implémentez une logique de réessai avec backoff exponentiel
  6. Monitoring : Surveillez les taux d'échec pour identifier les problèmes récurrents
  7. Vérification des liens : Toujours vérifier canReceivePayments avant de diriger un client vers un lien

Support

Si vous rencontrez des erreurs persistantes ou des problèmes non documentés, contactez le support Jèko à hello@jeko.africa.

On this page