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 expired, used or unknownWire onLoadError. It carries session_expired, session_used, session_not_found, invalid_session or session_terminal_expired
Embed misconfiguredA 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'
A thrown Error at call timeNo sessionId, or the selector matched nothingBoth throw synchronously, before any request
The iframe never appearsload_error on onLoadErrorThe checkout origin is unreachable. Check the network tab and any content 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

A Wajub key is a prefix, a dot, then 96 characters. pk. and sk. are live, pk_test. and sk_test. are sandbox.

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 backRaise timeout, or pass 0 to wait indefinitely
getValue() returns nullNothing has been typed yetIt fills on the first change. Read it in the handler
OTP never appearsPayment fields do not carry OTPUse hosted checkout

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 32 property whitelist
A url() background is ignoredValues containing url(, @import, javascript:, < or > are refused
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

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 support@wajub.com 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?