Skip to content

Components troubleshooting

The symptom, the cause underneath it, and the line that fixes it.

Almost every Wajub Components problem is one of four things: a token that is not valid any more, a page that loaded the SDK on the server, a callback nobody wired, or a config key the SDK quietly dropped. Start here before reading the code.

Nothing renders

SymptomCauseFix
An empty container, no errorThe token is unknown, or its payment ended and it has stoppedWire onLoadError. It carries session_not_found or session_terminal_expired. From Wajub.js 1.6.0 the checkout leaves the page right after it, without onClose
Embed configuration errorA checkout URL pasted into an iframe by handCall mount(), open() or CheckoutEmbed
Nothing happens, no network requestmount() ran on the serverImport from @wajub/js/pure, or mark the file 'use client'
An Error at call timeNo sessionId, or the selector matched nothingBefore any request. window.wajub throws it synchronously; the @wajub/js helpers (mount(), open()…) are async and return a rejected promise instead, so await them in a try
The iframe never appearsload_error on onLoadErrorThe checkout origin is unreachable. Check the network tab and any content policy
A blank frame on one site, fine on another, no callback at allSettings → Checkout embedding does not list this page's originAdd https:// plus the domain, port included if any. Where the checkout may be embedded
The console reports a Content Security Policy violationYour page's policy does not allow Wajub's hostsAllow the hosts listed under Content Security Policy

onLoadError is the callback that explains an empty embed

onError is for payments that fail. onLoadError is for checkouts that never start, which is the case in most of the rows above. Wire it first, on every integration.

The keys

Live keys start with pk. or sk., sandbox keys with pk_test. or sk_test..

SymptomCauseFix
secret_key_in_browserAn sk. key passed to Wajub()The browser gets a session token, never a secret key
invalid_publishable_keyThe value is not a publishable keyIt starts with pk, and it is not the session token
missing_publishable_keycreatePayment() with no keySet it on the client, or create the session on your server
403 from the API, and an emailA secret key sent from a browserThe API refuses it and alerts the key's owner. Rotate the key
CORS on createPaymentA direct API call from page codeCreate the session on your backend

To tell sandbox from live in the browser, read the session rather than the key.

The session knows
import { fetchSession } from '@wajub/js';

const preview = await fetchSession(sessionId);
if (preview.environment === 'sandbox') showSandboxBanner();

Payment fields

SymptomCauseFix
complete never becomes trueA required field is still empty or invalidLog event.error in the change handler, it names the field
confirmPayment rejects with wallet_not_supportedA wallet componentApple Pay and Google Pay pay through their own button
confirmPayment rejects with address_not_paymentAn address componentIt collects, it does not charge. Read it with getValue()
confirmPayment rejects with missing_componentNo component passed, or one that never mountedKeep the instance from onInstance or the ready event
confirmation_timeout after a minuteNo outcome came back in time. The payment goes on and can still succeedRaise timeout, or pass 0 to wait indefinitely. Before offering to pay again, listen for success or wait for the webhook
confirmPayment rejects with checkout_validationonBeforeSubmit returned false, threw or took more than 30 secondsAnswer within 30 seconds, and return false only when the order was refused
confirmPayment rejects with payment_error, "Validate the order before confirming this payment."The payment was prepared with deferred: true and never activatedActivate it from your order endpoint before onBeforeSubmit resolves true
getValue() returns nullNothing has been typed yetIt fills on the first change. Read it in the handler

Styling that does nothing

The SDK filters appearance before it leaves the browser. An unknown key is dropped in silence, so a typo looks exactly like a bug in the checkout.

SymptomCause
A rules block is ignoredThe selector is not .Input or .Label, with at most one allowed pseudo
One property inside a rule is ignoredIt is outside the 31 property whitelist
A background image or a font import is ignoredSome values are refused for security, among them any value that would load an external resource
A variables key is ignoredOnly the sixteen documented keys pass
update({ layout }) changes nothingOn the hosted checkout it is stored locally and never sent
appearance props change nothing after mountCheckoutEmbed never calls update() itself

The three lists are on Appearance, and the framework specific behaviour is on React, Vue and Svelte.

Layout and sizing

SymptomCauseFix
The embed is clippedA parent with overflow: hidden or a fixed heightThe SDK sets the iframe height itself. Let the parent grow
The page jumps when the checkout loadsNothing is reserved around your own contentThe SDK already reserves 480px. Reserve your own summary, not the embed
A field component stays tinyIts parent is display: none at mountMount it once it is visible, or after the tab opens

onResize is informational. The SDK has already applied the height by the time it fires, so use it to move something else on the page.

The payment itself

SymptomCauseFix
A method is missing from the checkoutThe session does not carry that channelRead fetchSession().payment_methods before mounting
The order shipped twiceonSuccess and the webhook both fulfilFulfil on the webhook, use onSuccess for the interface
The order never shippedonSuccess ran and the tab closedSame answer. The browser is not a reliable reporter
onError fires with a decline_codeThe provider refused the paymentretryable says whether another attempt is worth offering
onError fires with nothing_to_submitsubmit() found no pay button to press: no method chosen, an OTP step, a wallet's own button, or a payment already on its wayCall submit() once the payer has chosen a method, and not while a payment is under way

Before going live

  • onError and onLoadError are both wired.
  • The session is created on your server, with the secret key in an environment variable.
  • Your site and your callback URL are HTTPS.
  • Fulfilment happens on the webhook, not in the browser.
  • The integration goes through mount(), open() or a framework component, never a hand built URL.
  • onBreakdown keeps your own total in step, if you show one beside an inline embed.
  • One real payment for a small amount has gone through in live mode.

Test cards and Mobile Money numbers are on Test scenarios.

Still stuck

Write to [email protected] with the transaction reference, your merchant domain, and the code from onLoadError or onError. Never send a key.

What did you think of this content?