> ## Documentation Index
> Fetch the complete documentation index at: https://developer.jeko.africa/llms.txt
> Use this file to discover all available pages before exploring further.

# Gérer les échecs de paiement

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

## Vue d'ensemble

Les paiements peuvent échouer pour diverses raisons. Ce guide vous aide à comprendre les erreurs courantes et comment les gérer dans votre application.

## Statuts de paiement

**Important** : Seules les **demandes de paiement** (`payment_requests`) ont un statut. Les liens de paiement (`payment_links`) n'ont pas de statut.

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** :

```javascript theme={null}
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** :

```javascript theme={null}
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 `/partner_api/stores` 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 soundbox, le `deviceId` est requis mais manquant ou invalide.

**Solution** :

* Vérifiez que le `deviceId` est fourni pour les paiements soundbox
* Vérifiez que le `deviceId` existe via l'endpoint `/partner_api/devices`
* Assurez-vous que le dispositif 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)
* Code devise invalide (doit être ISO 4217, 3 caractères)
* Référence manquante ou invalide (1-100 caractères)
* Titre manquant ou invalide pour payment\_links (10-255 caractères)
* URLs de callback invalides (doivent être HTTPS valides)
* 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 Jeko Cockpit 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** :

```bash theme={null}
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"
```

2. **Pour les paiements soundbox, vérifier le dispositif** :

```bash theme={null}
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"
```

3. **Pour les liens de paiement, vérifier la disponibilité** :

```bash theme={null}
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

```javascript theme={null}
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](../webhooks/introduction) 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 robuste** : Implémentez une gestion d'erreurs complète pour tous les cas d'échec
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 JEKO à [hello@jeko.africa](mailto:hello@jeko.africa).
