> ## 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.

# Pièges courants — payin avec redirection

> Les erreurs classiques à éviter avec le payin redirect LigdiCash : iframe bloqué, customer non vide, popup bloqué, validation côté client, et plus.

Page de référence recensant les erreurs les plus fréquentes rencontrées lors de l'intégration du payin avec redirection. Chaque piège décrit le symptôme observé, la cause et la correction à appliquer.

<AccordionGroup>
  <Accordion title="customer non vide — opérateurs masqués sur la page de paiement">
    **Symptôme** : la page de paiement LigdiCash n'affiche qu'un seul opérateur, ou aucun, alors que plusieurs sont disponibles dans votre région.

    **Cause** : si le champ `customer` contient un numéro de téléphone, LigdiCash filtre la page pour n'afficher que les opérateurs compatibles avec ce numéro. Les autres opérateurs sont masqués.

    **Correction** : toujours envoyer `customer: ""` dans la requête de création de facture.

    ```json theme={null}
    {
      "commande": {
        "invoice": {
          "customer": "",
          ...
        }
      }
    }
    ```

    <Note>
      Les champs `customer_firstname`, `customer_lastname` et `customer_email` peuvent être renseignés sans impact sur l'affichage des opérateurs. Seul `customer` (le numéro de téléphone) est concerné.
    </Note>
  </Accordion>

  <Accordion title="Iframe bloqué — page blanche ou erreur console">
    **Symptôme** : l'iframe reste vide, ou la console du navigateur affiche une erreur du type `Refused to display … in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'`.

    **Cause** : LigdiCash bloque délibérément le chargement de sa page de paiement dans un `<iframe>`. Il s'agit d'une mesure de sécurité contre le clickjacking, pas d'un bug.

    **Correction** : ouvrir l'URL de paiement dans un contexte de navigation réel.

    | Mode          | Implémentation                          |
    | ------------- | --------------------------------------- |
    | Même onglet   | `window.location.href = url`            |
    | Nouvel onglet | `window.open(url, '_blank')`            |
    | Popup         | Pattern `about:blank` (voir ci-dessous) |
    | Mobile natif  | WebView native iOS / Android            |

    Consultez [Rediriger le client](/api-paiement/payin-redirect/rediriger-client) pour les implémentations complètes.
  </Accordion>

  <Accordion title="Popup bloqué par le navigateur — window.open() retourne null">
    **Symptôme** : `window.open()` retourne `null` et rien ne s'ouvre. Aucune erreur visible pour l'utilisateur.

    **Cause** : les navigateurs bloquent les appels à `window.open()` qui ne sont pas directement déclenchés par un geste utilisateur. Un appel effectué après un `await fetch(...)` est considéré comme asynchrone et bloqué.

    **Correction** : ouvrir `about:blank` de manière synchrone au clic, puis naviguer vers l'URL une fois la réponse API reçue.

    ```javascript JavaScript theme={null}
    element.addEventListener("click", async () => {
      // Synchrone — avant tout await
      const popup = window.open("about:blank", "paiement", "width=500,height=700");

      const data = await creerFacture();

      if (data.response_code === "00") {
        popup.location.href = data.response_text;
      } else {
        popup.close();
      }
    });
    ```

    Consultez [Rediriger le client](/api-paiement/payin-redirect/rediriger-client) pour le pattern complet.
  </Accordion>

  <Accordion title="Valider le paiement sur la base de return_url ou cancel_url seulement">
    **Symptôme** : des commandes sont honorées sans paiement réel, ou l'état de la commande est incorrect après annulation.

    **Cause** : `return_url` et `cancel_url` sont de simples redirections navigateur. N'importe qui connaissant l'URL peut y accéder directement — elles ne constituent pas une preuve de statut de paiement.

    **Correction** : dans les deux cas (`return_url` et `cancel_url`), toujours vérifier le statut réel de la transaction en appelant `confirm` côté serveur avant de prendre toute décision métier.

    ```javascript JavaScript theme={null}
    // Que l'on arrive via return_url ou cancel_url
    const confirmation = await appellerBackend("/verifier-paiement", { token });

    if (confirmation.status === "completed") {
      afficherSucces();
    } else {
      afficherEchec();
    }
    ```

    <Warning>
      Ne jamais déduire le statut d'un paiement depuis l'URL de redirection. Le seul statut fiable est celui retourné par l'endpoint `confirm`.
    </Warning>
  </Accordion>

  <Accordion title="Utiliser le token du callback pour appeler confirm">
    **Symptôme** : la réconciliation avec votre commande échoue, ou vous confirmez la mauvaise transaction.

    **Cause** : le `token` présent dans le payload du callback est différent du token retourné à la création de la facture. Les deux permettent d'appeler `confirm`, mais seul le token de création permet de relier la réponse à votre commande côté marchand.

    **Correction** : stocker le token retourné par l'endpoint `create` en base de données dès la création de la facture, et le préférer pour appeler `confirm`.

    ```javascript JavaScript theme={null}
    // À la création
    const { token, response_text } = await creerFacture(commande);
    await db.save({ orderId, token, paymentUrl: response_text });

    // Dans le handler callback ou après return_url
    const { token } = await db.findByOrderId(orderId);
    const confirmation = await confirmerPaiement(token);
    ```

    Consultez [Le pattern transaction\_id](/concepts/transaction-id-pattern) pour la stratégie de réconciliation recommandée.
  </Accordion>

  <Accordion title="cancel_url et return_url inversées">
    **Symptôme** : l'utilisateur atterrit sur la page de succès après avoir annulé, ou sur la page d'annulation après un paiement réussi.

    **Cause** : les deux champs sont souvent confondus. L'ancienne documentation LigdiCash les avait d'ailleurs inversés dans ses exemples.

    **Correction** :

    * `return_url` → URL vers laquelle LigdiCash redirige après un **paiement réussi**
    * `cancel_url` → URL vers laquelle LigdiCash redirige après une **annulation**

    ```json theme={null}
    {
      "actions": {
        "return_url": "https://monapp.com/paiement/succes",
        "cancel_url": "https://monapp.com/paiement/annule",
        "callback_url": "https://monapp.com/api/callback/ligdicash"
      }
    }
    ```

    <Warning>
      Quelle que soit l'URL d'atterrissage, vérifiez toujours le statut réel avec `confirm` côté serveur avant d'afficher un résultat définitif à l'utilisateur.
    </Warning>
  </Accordion>
</AccordionGroup>

## Pages associées

* [Rediriger le client](/api-paiement/payin-redirect/rediriger-client) — patterns d'ouverture et popup anti-bloqueur
* [Vérifier le statut](/api-paiement/payin-redirect/verifier-statut) — appeler `confirm` correctement
* [Callback — sécurisation](/api-paiement/callback/securisation) — re-vérification et protection du handler
* [Le pattern transaction\_id](/concepts/transaction-id-pattern) — réconciliation côté marchand
