Dépannage de Wajub Components
Le symptôme, sa cause et la ligne qui le corrige.
Presque tous les problèmes de Wajub Components proviennent de quatre causes : un jeton qui n'est plus valide, une page qui a chargé le SDK sur le serveur, un callback non configuré ou une clé de configuration que le SDK ne reconnaît pas. Commencez ici avant de lire le code.
Rien ne s'affiche
| Symptôme | Cause | Correction |
|---|---|---|
| Conteneur vide sans erreur | Jeton inconnu, ou jeton qui n'est plus valide parce que son paiement est terminé | Configurez onLoadError. Il contient session_not_found ou session_terminal_expired |
Intégration embed incorrecte (Embed configuration error en anglais) | URL de checkout collée manuellement dans une iframe | Appelez mount(), open() ou CheckoutEmbed |
| Aucune action ni requête réseau | mount() s'est exécuté sur le serveur | Importez depuis @wajub/js/pure ou marquez le fichier avec 'use client' |
Error lors de l'appel | Aucun sessionId ou sélecteur sans correspondance | window.wajub la lève de façon synchrone ; les helpers de @wajub/js (mount(), open()…) sont asynchrones et renvoient plutôt une promesse rejetée : attendez-les avec await dans un try |
| L'iframe n'apparaît jamais | load_error dans onLoadError | L'origine du checkout est inaccessible. Vérifiez l'onglet réseau et les règles de contenu |
| Un cadre vide sur un site, normal sur un autre, sans aucun callback | Paramètres → Intégration de la page de paiement ne contient pas l'origine de cette page | Ajoutez https:// suivi du domaine, port compris s'il y en a un. Où le checkout peut être intégré |
| La console signale une violation de Content Security Policy | La politique de votre page n'autorise pas les hôtes de Wajub | Autorisez les hôtes listés dans Content Security Policy |
onLoadError explique une intégration vide
onError concerne les paiements qui échouent. onLoadError concerne les checkouts qui ne
démarrent jamais, comme dans la plupart des lignes ci-dessus. Configurez-le en premier dans chaque intégration.
Clés
Les clés live commencent par pk. ou sk., les clés de sandbox par pk_test. ou sk_test..
| Symptôme | Cause | Correction |
|---|---|---|
secret_key_in_browser | Clé secrète transmise à Wajub() | Le navigateur reçoit un jeton de session, jamais une clé secrète |
invalid_publishable_key | La valeur n'est pas une clé publique | Elle commence par pk et ne correspond pas au jeton de session |
missing_publishable_key | Appel de createPayment() sans clé | Configurez-la sur le client ou créez la session sur votre serveur |
Réponse 403 de l'API et e-mail | Clé secrète envoyée depuis un navigateur | L'API la refuse et alerte son propriétaire. Renouvelez la clé |
CORS sur createPayment | Appel direct de l'API depuis le code de la page | Créez la session sur votre backend |
Pour distinguer la sandbox du mode live dans le navigateur, lisez la session plutôt que la clé.
import { fetchSession } from '@wajub/js';
const preview = await fetchSession(sessionId);
if (preview.environment === 'sandbox') showSandboxBanner();Champs de paiement
| Symptôme | Cause | Correction |
|---|---|---|
complete ne devient jamais true | Champ obligatoire encore vide ou invalide | Enregistrez event.error dans le handler change, il identifie le champ |
confirmPayment rejette avec wallet_not_supported | Composant wallet | Apple Pay et Google Pay utilisent leur propre bouton |
confirmPayment rejette avec address_not_payment | Composant address | Il collecte sans débiter. Lisez-le avec getValue() |
confirmPayment rejette avec missing_component | Aucun composant transmis ou composant jamais monté | Conservez l'instance de onInstance ou de l'événement ready |
confirmation_timeout | Aucun résultat reçu à temps. Le paiement continue et peut encore réussir | Augmentez timeout ou transmettez 0 pour attendre indéfiniment. Avant de proposer de payer à nouveau, écoutez success ou attendez le webhook |
onLoadError ou loaderror reçoit ready_timeout | Le champ n'était pas prêt avant la fin de readyTimeout : un cadre bloqué, une origine absente de Paramètres → Intégration de la page de paiement, ou une panne réseau | Vérifiez la console et les réglages d'intégration. Proposez un autre moyen de payer : retirez les champs, passez la commande, puis envoyez le payeur sur authorization_url. Quand le formulaire n'arrive pas |
| Un champ affiche, à la place de son formulaire, un message disant que le paiement a expiré, a été annulé ou a échoué | Sa session est déjà terminée sans paiement : loaderror avec session_terminal_expired, session_terminal_cancelled ou session_terminal_failed | Créez un nouveau paiement et montez le champ sur son jeton. Un champ dont la session est déjà payée envoie ready à la place |
loaderror reçoit component_origin_missing | Le composant n'a pas été monté avec Wajub.js ou un paquet framework | Montez-le avec l'un d'eux |
confirmPayment rejette avec component_unmounted | Le champ a quitté la page pendant que vous l'attendiez : démonté, détruit, ou son conteneur retiré par votre framework | Gardez le champ sur la page jusqu'à la fin du paiement, et verrouillez plutôt vos propres contrôles |
confirmPayment rejette avec checkout_validation | onBeforeSubmit a renvoyé false, a levé une exception ou a dépassé 30 secondes | Répondez en moins de 30 secondes, et ne renvoyez false que si la commande a été refusée |
confirmPayment rejette avec payment_error, « Validate the order before confirming this payment. » | Le paiement a été préparé avec deferred: true et jamais activé | Activez-le depuis votre endpoint de commande avant que onBeforeSubmit renvoie true |
getValue() renvoie null | Aucune valeur encore saisie | La valeur arrive au premier change. Lisez-la dans le handler |
submission_failed, depuis confirmPayment() ou sur error | Le formulaire n'était pas prêt lors de votre envoi : un champ vide, incomplet ou invalide, un formulaire encore en chargement, un numéro de carte refusé, ou une fenêtre bloquée par le navigateur | Affichez message, rédigé pour le payeur dans sa langue, et réactivez votre bouton |
| Votre bouton, pressé de nouveau pendant le paiement, ne fait rien | Un paiement est en cours. Un nouvel envoi, quel que soit le moyen, ne débite pas deux fois et ne rappelle pas onBeforeSubmit | Comportement attendu. C'est le paiement en cours qui vous donne son résultat |
| Le payeur a appuyé sur Entrée dans un champ et vous n'avez rien reçu | Le formulaire n'était pas prêt : le refus d'un appui sur Entrée reste dans le composant | Comportement attendu. Le composant montre ce qui manque, et vous n'êtes informé que des envois que vous demandez |
| Votre page part vers un autre site après l'envoi du payeur | Le paiement se poursuit sur la page sécurisée du prestataire, pour un wallet ou un paiement en crypto par exemple, juste après l'événement redirect | Comportement attendu. Définissez callback sur le paiement : le payeur y revient, et vous lisez le résultat sur votre serveur. Quand le paiement se poursuit sur la page du prestataire |
Style sans effet
Une clé appearance que le SDK ne reconnaît pas reste sans effet et ne lève aucune erreur. Une
faute de frappe ressemble donc exactement à un bug du checkout.
| Symptôme | Cause |
|---|---|
Bloc rules ignoré | Le sélecteur n'est pas .Input ou .Label avec au maximum un pseudo-élément autorisé |
| Propriété d'une règle ignorée | Elle ne figure pas parmi les 31 propriétés autorisées |
| Image d'arrière-plan ou importation de police ignorée | Certaines valeurs sont refusées par sécurité, dont toute valeur qui chargerait une ressource externe |
Clé variables ignorée | Seules les seize clés documentées sont acceptées |
Les trois listes figurent dans Apparence. Le comportement propre à chaque framework est présenté dans React, Vue, Svelte et Angular.
Mise en page et dimensions
| Symptôme | Cause | Correction |
|---|---|---|
| L'intégration est coupée | Parent avec overflow: hidden ou hauteur fixe | Laissez le parent grandir |
| La page se décale au chargement du checkout | Aucun espace réservé autour de votre contenu | Réservez la place de votre récapitulatif, pas celle de l'intégration |
| Un composant de champ reste minuscule | Son parent utilise display: none lors du montage | Montez-le une fois visible ou après l'ouverture de l'onglet |
| Un écran OTP, PIN ou 3D Secure est coupé dans un composant de champ | Parent avec une hauteur fixe ou overflow: hidden. Un tel écran est plus haut que le formulaire | Laissez le parent grandir |
Avec loader: 'skeleton', le formulaire s'affiche tard, sans erreur | Le champ a mis longtemps à être prêt, et aucun readyTimeout n'était défini | Définissez readyTimeout : onLoadError reçoit alors ready_timeout, et vous pouvez proposer un autre moyen de payer |
Avec loader: 'skeleton', le bloc d'un champ reste plus haut que son formulaire | La hauteur réservée ne descend jamais sous le min-height propre du conteneur | Ne donnez pas de min-height au conteneur, ou remettez-le à 0 juste avant mount() |
Paiement
| Symptôme | Cause | Correction |
|---|---|---|
| Moyen de paiement absent du checkout | La session ne contient pas ce canal | Lisez fetchSession().payment_methods avant le montage |
| Commande livrée deux fois | onSuccess et le webhook déclenchent tous deux la livraison | Livrez avec le webhook et utilisez onSuccess pour l'interface |
| Commande jamais livrée | onSuccess s'est exécuté puis l'onglet s'est fermé | Même réponse. Le navigateur n'est pas une source fiable |
onError se déclenche avec un decline_code | Le prestataire a refusé le paiement | retryable indique si une nouvelle tentative doit être proposée |
onError se déclenche avec nothing_to_submit | submit() n'a trouvé aucun bouton de paiement sur lequel appuyer : aucun moyen choisi, une étape d'OTP, le bouton propre d'un wallet, ou un paiement déjà en cours | Appelez submit() une fois que le payeur a choisi un moyen, et pas pendant un paiement en cours |
Avant de passer en live
onErroretonLoadErrorsont tous deux configurés.- La session est créée sur votre serveur avec la clé secrète dans une variable d'environnement.
- Votre site et votre URL de
callbackutilisent HTTPS. - La commande est livrée à la réception du webhook, pas dans le navigateur.
- L'intégration utilise
mount(),open()ou un composant de framework, jamais une URL construite à la main. onBreakdownmaintient votre total à jour s'il apparaît à côté d'une intégration en ligne.- Un paiement réel d'un petit montant a été effectué en mode live.
Les cartes et numéros Mobile Money de test figurent dans Scénarios de test.
Le problème persiste ?
Écrivez à support@wajub.com avec la référence de transaction, le domaine de votre
marchand et le code de onLoadError ou onError. N'envoyez jamais de clé.