Skip to content

SDK reference

Every export, option, method, type, state and error code of @wajub/js.

The complete surface of @wajub/js@1.8.1. The guide pages explain when to use each piece. This one lists all of them.

Two ways to reach the same API

The runtime is one object. You either import helpers that load it for you, or you hold it yourself.

Helpers, or the runtime
import { mount, open, checkout, components, confirmPayment } from '@wajub/js';

import { loadWajub } from '@wajub/js/pure';
const rt = await loadWajub();
rt?.wajub.mount('#checkout', { sessionId });
Export from @wajub/jsReturnsNotes
mount(container, config)Promise<CheckoutInstance | null>null on the server
open(config)Promise<PopupInstance | null>null on the server
checkout(config)Promise<void | null>Redirects the page
components(sessionId, options?)Promise<ComponentsFactory | null>null on the server
confirmPayment(options)Promise<{ status, transaction? }>Rejects with WajubError
fetchSession(sessionId)Promise<SessionPreview>Works before anything is mounted
signals()Promise<ClientSignals>For a server-side charge: send the result to your server and pass it as data._client. Never rejects
preload(sessionId, options?)Promise<void | null>{ iframe?: boolean }
appearanceFrom(container, source?)Promise<AppearanceConfig | null>From 1.8.0. What appearance.inherit would read for a component in container. null on the server, {} with a Wajub.js older than 1.8.0
componentReserveHeight(type, variant?, fields?)numberFrom 1.8.0. The room loader: 'skeleton' holds, in px. Synchronous: the estimate until the runtime is loaded, then the runtime's own answer, which may be a height learnt on the page
componentEstimatedHeight(type, variant?, fields?)numberFrom 1.8.0. The estimate alone, the same on the server and in the browser, never a height learnt on the page: what a server render and its hydration both hold
getVersion()Promise<string | null>The loaded runtime version
getCheckoutOrigin()Promise<string | null>Where the checkout is served from
loadWajub(options?)Promise<WajubRuntime | null>{ jsOrigin?, jsUrl? }
WajubErrorDeclared in the types onlyNot exported at runtime, read it from loadWajub()

loadWajub.setLoadParameters({ jsOrigin }) sets the origin once, before the first call.

WajubRuntime

What loadWajub() resolves to, and what the CDN script puts on window.

MemberWhat it is
wajubThe SDK object, the table below
WajubThe client factory
WajubErrorThe error class

wajub

MemberSignature
mount(container, config) => CheckoutInstance
open(config) => PopupInstance
checkout(config) => void
components(sessionId, options?) => ComponentsFactory
confirmPayment(options) => Promise<{ status }>
fetchSession(sessionId) => Promise<SessionPreview>
signals() => Promise<ClientSignals>
preload(sessionId, options?) => void
appearanceFrom(container, source?) => AppearanceConfig, from 1.8.0. {} when there is nothing to read, never throws
componentReserveHeight(type, variant?, fields?) => number, from 1.8.0. The height a component of that kind last took at rest on the page, else its estimate
createThe Wajub factory, same object
versionThe runtime version string
sdkMajorThe major version
checkoutOriginThe origin the iframes come from
createHeadlessRemoved, throws when called

These are synchronous, because the runtime is already loaded by the time you hold it.

Wajub() client

A client holds a publishable key, a session, or both, so you stop repeating them.

CallWhat you get
Wajub('pk_test.…')A client that can createPayment()
Wajub.session(token)A client bound to an existing session
Wajub({ publishableKey, sessionId })Both at once
OptionWhat it does
sessionIdThe authorization_token from your server
publishableKeyYour pk_test. key. A live pk. key is not recognised in 1.8.1
apiKeyDeprecated alias for publishableKey
apiBaseAPI origin override, defaults to https://api.wajub.com
componentOriginCanonical origin for payment fields

Client members

MemberWhat it does
publishableKey, sessionId, environmentWhat the client currently holds
apiBase, checkoutOriginWhere it talks
useSession(sessionId)Binds another session, returns the client
createPayment(params)POST /payments with the publishable key
initCheckout(options)Creates the payment and shows it in one call
fetchSession(sessionId?)Session preview
signals()What a server-side charge needs from the payer's browser, to pass as data._client
preload(sessionId?, options?)Warms the runtime
mount, open, checkoutThe three hosted modes, session already bound
components(sessionIdOrConfig?, config?)The fields factory
appearanceFrom(container, source?)What appearance.inherit would read, from 1.8.0
confirmPayment(options?)Submits a mounted component

Factory statics

MemberWhat it does
Wajub.session(sessionId, options?)A session bound client
Wajub.parsePublishableKey(key){ key, environment }, throws on a secret key
Wajub.createPayment(key, params, options?)One shot, no client to keep
Wajub.signals()What a charge made from your server needs from the payer's browser, to pass as data._client

EmbeddedConfig

Used by mount(), by CheckoutEmbed in every framework, and as the base of PopupConfig.

OptionTypeDefault
sessionIdstringRequired
localestringSession default
layoutCheckoutLayoutSession default
appearanceAppearanceConfigYour Dashboard branding. From 1.8.0, inherit is read around the container for mount(), over the whole page for open()
embedOriginstringThe current page origin
loadingTextstringEmpty
showLoadingbooleantrue
themeEmbedThemeDerived from appearance, deprecated
CallbackReceives
onReadyCheckoutInstance
onSuccessPaymentSuccess
onErrorWajubError, a payment that failed, or nothing_to_submit when submit() had no button to press
onLoadErrorWajubError, a checkout that never started: session_not_found, session_terminal_expired or load_error. Right after it, the overlay closes or the inline checkout leaves the page, without onClose
onCancelNothing
onExpiredNothing
onStateChange{ state?: CheckoutState, method?: string }
onMethodChange{ methodId?, method_id? }
onBreakdownEmbedBreakdown, { subtotal, discount, tax, total, currency } in major units
onResizeheight: number, inline only
onCloseNothing. The overlay was dismissed, or an inline checkout was ended by the payer's Done or by close()

PopupConfig

Everything in EmbeddedConfig, plus the modal.

OptionTypeDefault
widthnumber920
heightnumber680
closeOnOverlaybooleanfalse
closeOnEscapebooleantrue
closeOnSuccessbooleantrue
closeOnCancelbooleantrue
closeOnExpiredbooleantrue

The three closeOn outcome flags run after your callback, never before it.

CheckoutInstance and PopupInstance

MethodWhat it does
mount()Re-attaches after unmount(). Does nothing while mounted, or after destroy() or close()
unmount()Removes from the DOM, keeps the instance. On an overlay, onClose is not called
destroy()Full teardown. mount() no longer brings it back
update(partial)locale, appearance, currency, layout, deprecated theme. From 1.8.0, appearance merges into the current one: a key given as undefined or null is cleared, and appearance: undefined or null clears it all. A new theme preset replaces the previous one and what it brought
getState()The current CheckoutState
submit()Presses the pay button of the method the payer is on. With no button to press, nothing is pressed and onError receives nothing_to_submit
cancel()Cancels the payment in progress
retry()Retries after a failure
selectMethod(methodId)Switches payment method
close()Ends the checkout, inline or overlay, and calls onClose. destroy() ends it without a callback
isOpen()Whether the checkout is still open, inline and overlay: false once it ended (success, cancel, expiry, close)

mount() and unmount() behave the same inline and in the overlay. A checkout that comes back is loaded again from its token: onReady fires once more, and the payment resumes where the session stands. Driving the instance says when submit() has no button to press.

update({ layout }) reaches the checkout from 1.8.0

From Wajub.js 1.8.0, update({ layout }) lays the methods out again, inline and in the overlay. Before it, layout only updated the local config, so the methods kept the arrangement they were mounted with until unmount() then mount() loaded the checkout again.

ComponentConfig

Passed to wajub.components(sessionId, options) as defaults, and to factory.create(type, config) per component.

OptionTypeApplies to
sessionIdstringRequired
localestringAll
appearanceAppearanceConfigAll
layoutCheckoutLayoutpayment
componentOriginstringAll, defaults to the page origin
collectAddressshipping or billingpayment
addressModeshipping or billingaddress
collectNamebooleanpayment, address
fields.phonealways, auto, neverpayment, address
fields.customerauto or externalAll but address
fields.countryISO 3166 alpha-2mobileMoney, payment
fields.operatorchoose or detectmobileMoney, payment
variantdefault or fieldsAll but address. Through the Vue and Svelte wrappers from 1.5.1. From 1.7.2, fields sets no 120px floor on the iframe
frameTitlestringAll. The iframe's name for screen readers, Wajub <type> component by default. From 1.7.0, wrappers included
loadernone or skeletonAll, from 1.8.0. none by default, skeleton in the framework wrappers. Loading without a layout shift
readyTimeoutnumber, millisecondsAll, from 1.8.0. Without ready in time, onLoadError and loaderror receive ready_timeout. 0 or absent: never, and a skeleton placeholder still gives way to the iframe after 15 seconds, without an error
CallbackReceives
onBeforeSubmitNothing. Return false, or throw, to stop the charge. 30 seconds at most. All but address
onReadyComponentInstance
onChangeComponentChangeEvent
onFocus, onBlurNothing
onLoadErrorWajubError: a code a checkout sends when it cannot start, or ready_timeout with readyTimeout
onSuccessThe transaction
onErrorWajubError
onMethodChange{ methodId? }
onStateChangeComponentStateChangeEvent, { state, method? }, from 1.8.0
onResize{ height }, from 1.8.0. The hosted checkout's onResize receives the number itself
onRedirectComponentRedirectEvent, { url, method }, from 1.8.0. The page leaves right after, even if the callback throws

Each callback is called just before the event of the same name. Passing one changes nothing else.

ComponentsFactory

MemberWhat it is
sessionIdThe session every component inherits
create(type, config?)A new ComponentInstance

Types. card, mobileMoney, bankTransfer, crypto, wallet, payment, address.

ComponentInstance

Every method except the readers returns the instance, so calls chain.

MethodReturns
mount(container)The instance
unmount(), destroy()Nothing
on(event, fn), off(event, fn)The instance
update({ locale, appearance, layout, frameTitle })The instance. From 1.8.0, appearance merges as on the hosted checkout
focus(), blur(), submit()The instance. From 1.8.0, focus() while a skeleton loader is up moves the focus in once the form is ready, unless the payer acted meanwhile
selectMethod(methodId)The instance
getState()CheckoutState
isComplete()boolean
getError()WajubError or null
getValue()ComponentAddressValue or null

Events

EventPayload
readyComponentInstance
changeComponentChangeEvent
focus, blurNothing
loaderrorWajubError: a code a checkout sends when it cannot start, or ready_timeout with readyTimeout
resize{ height }
successPaymentSuccess
errorWajubError
statechangeComponentStateChangeEvent, { state, method? }
methodchange{ methodId }
redirectComponentRedirectEvent, { url, method }. From 1.7.0, right before Wajub.js takes your page to the provider's own page

From 1.8.0, on() and off() type the payload of each event above through ComponentEventMap: card.on('statechange', (event) => event.state) needs no cast.

update({ frameTitle }) renames the iframe itself. The other three are sent to the component. error also carries submission_failed when a submission you asked for, with submit() or confirmPayment(), was refused before any charge.

ConfirmPaymentOptions

OptionTypeDefault
sessionIdstringRequired
componentsComponentInstanceRequired, one mounted component
timeoutnumber60000 milliseconds, 0 waits forever. From 1.8.0, paused while the payment waits on the payer
callbackstringDeprecated, never read

Resolves with { status, transaction }, transaction being a PaymentSuccess. Rejects with a WajubError carrying missing_component, wallet_not_supported or address_not_payment before anything is sent. After, it rejects with confirmation_timeout, or with the payment's own error: checkout_validation, submission_failed, payment_error, payment_cancelled, session_expired, component_unmounted, the provider's decline code, or the component's load error.

From Wajub.js 1.8.0, the time does not run while the payment waits on the payer (OTP_REQUIRED, PIN_REQUIRED, PHONE_REQUIRED, BIRTHDAY_REQUIRED, ADDRESS_REQUIRED, APPROVAL_REQUIRED, USSD_REQUIRED, 3DS_REQUIRED, PENDING), nor once the page leaves for the provider's (redirect). It starts over, whole, at the next step. Meanwhile, a component whose iframe has left the page without unmount() rejects with component_unmounted, as unmount() and destroy() do, and a component mounted again starts from INITIATED.

confirmation_timeout only stops the wait. It cancels nothing: the payment goes on and can still succeed. Before offering to pay again, listen for success on the component or wait for the payment.succeeded webhook.

PaymentSuccess

What onSuccess, the success event and confirmPayment() hand over. It is the checkout's own summary, not the API's payment object, and it comes from the browser: confirm the payment on your server, with GET /payments/{id} or the payment.succeeded webhook, before you deliver anything.

FieldTypeWhat it is
idstringWajub's id of the payment, what GET /payments/{id} takes
referencestringYour reference, the one you sent on POST /payments
amountnumberWhat the payer paid, fee included when the payer bears it, in major units
currencystringISO 4217 code
methodstringThe method family: mobile_money, card…, unknown when the session was already paid
status"success"Always success
timestampstringWhen the payment completed, ISO 8601
receipt_urlstringThe payer's hosted receipt, when you have receipts on

Amounts are in major units, like the API: 12.5 for 12.50 EUR, 1500 for 1 500 XAF. Before 1.6.0, onSuccess, onBreakdown and fetchSession() gave them in minor units, 1250 for 12.50 EUR, and onSuccess carried no id.

CreatePaymentParams

What client.createPayment() sends. The same fields as POST /payments, narrowed to what a browser may set.

FieldType
amountnumber, required
currencystring, required
referencestring
descriptionstring
customer{ email?, name?, phone?, country? }, required with an email or a phone, or the API answers 422
bearermerchant or customer, who pays the fee
callbackHTTPS URL, redirect flow only
metadataRecord<string, string>

CreatePaymentResult

FieldWhat it is
sessionIdPass this to mount, open or components
authorizationTokenThe same value, under the API's name
authorizationUrlThe hosted page, or null
transactionThe transaction object, or null
rawThe untouched API response

InitCheckoutOptions

Creates the payment and shows it in one call. Everything in EmbeddedConfig applies too.

OptionTypeWhat it does
modeinline, overlay, redirectHow the checkout appears
containerstring or HTMLElementRequired for inline
sessionIdstringSkips createPayment()
paymentCreatePaymentParamsUsed when there is no sessionId
checkoutPartial<EmbeddedConfig>Options for the embed itself

It resolves with { session, instance }, and throws missing_container when inline has nowhere to go.

SessionPreview

What fetchSession(sessionId) resolves to. Read it before mounting to show an amount, a merchant name, or a sandbox banner.

FieldType
session_idstring
statusstring
environmentsandbox or live
amountnumber, in major units like the API
currencystring
merchant_namestring
payment_methodsArray<{ id, type, label }>
saved_methodsArray<{ id, type, label }>, returning customers
featuresRecord<string, boolean>

ClientSignals

What signals() resolves to, from 1.6.0: an opaque object. For a charge you make from your own server with a secret key, call signals() in the payer's browser, send the result to your server unchanged and pass it as data._client, so that Shield scores the payer rather than your server. Do not build, edit or store it. signals() never rejects.

AppearanceConfig

The full key list, the presets and the filtering rules are on Appearance.

GroupKeys
Presettheme: stripe, night, flat, none. From 1.7.1, light, dark, system or auto is read as colorScheme when none is given
ColourprimaryColor, secondaryColor, backgroundColor, buttonTextColor, inputBackgroundColor, inputBorderColor, textMutedColor, successColor, errorColor
ShapefontFamily, borderRadius, shadow
BehaviourcolorScheme, labels, disableAnimations
Tokensvariables, sixteen design token keys
Selectorsrules, .Input and .Label only
Your pageinherit, from 1.8.0: true, or an InheritSource, { input?, label?, button?, background? }, each a CSS selector or an element. Taking your page's look

States

getState() and onStateChange both speak this vocabulary.

CheckoutState
INITIATED → COLLECTING_DETAILS → PROCESSING → SUCCESS
                                ↘ OTP_REQUIRED, PIN_REQUIRED, PHONE_REQUIRED
                                ↘ BIRTHDAY_REQUIRED, ADDRESS_REQUIRED
                                ↘ 3DS_REQUIRED, USSD_REQUIRED, APPROVAL_REQUIRED
                                ↘ VERIFYING, PENDING
                                ↘ FAILED, CANCELLED, EXPIRED

A new instance reports INITIATED until the checkout says otherwise. PIN_REQUIRED, PHONE_REQUIRED, BIRTHDAY_REQUIRED and ADDRESS_REQUIRED are the steps some providers add after the first submit: a wallet PIN, a phone number to confirm, a date of birth, an address.

On the hosted checkout the outcome does not arrive through onStateChange: it arrives through onSuccess, onError, onCancel or onExpired, and getState() keeps the last step before it.

WajubError

Every rejection and every error callback receives one.

FieldType
messagestring, written for a human
typeapi_error, authentication_error, invalid_request_error, payment_error, rate_limit_error
codestring, the machine name
decline_codestring or null, from the provider
retryableboolean
paramstring or null, the field at fault

WajubError.fromPayload(payload) builds one from a raw object, and toJSON() gives it back.

Codes raised by the SDK itself

These never reach the network. They are integration mistakes.

CodeCause
secret_key_in_browserA sk. key passed to Wajub()
invalid_publishable_keyA key that is not a publishable key
invalid_initWajub() called with neither a publishable key nor a session token
missing_publishable_keycreatePayment() without a key
missing_session_tokencreatePayment() got an answer without a session token
missing_sessionNo sessionId on the client
missing_containerinitCheckout in inline mode with nowhere to mount
missing_componentconfirmPayment without a submittable component
wallet_not_supportedconfirmPayment on a wallet component
address_not_paymentconfirmPayment on an address component
confirmation_timeoutNo outcome within timeout. The payment goes on and can still succeed
ready_timeoutFrom 1.8.0, a payment field sent no ready within readyTimeout. Reaches onLoadError and loaderror, and is retryable
component_unmountedThe component was unmounted or destroyed while confirmPayment waited on it. From 1.8.0, also when its iframe left the page during a step that waits on the payer
components_unavailableThe components runtime did not load
load_errorThe checkout iframe failed to load

Codes a checkout sends when it cannot start

onLoadError on the hosted checkout, and onLoadError or the loaderror event on a payment field, receive one of these. Nothing was paid.

CodeCause
session_not_foundThe token is unknown, or the session could not be read
session_terminal_expiredThe payment ended and its token has stopped. On a payment field, the session expired unpaid, and the field shows its notice
session_terminal_cancelledPayment fields only, the session was already cancelled. The field shows its notice
session_terminal_failedPayment fields only, the session already ended on a failed payment. The field shows its notice
component_origin_missingPayment fields only, the field cannot tell which page it is in: a URL built by hand, or a page that sends no referrer. Mount it with Wajub.js
invalid_componentPayment fields only, a component type that does not exist
method_unavailablePayment fields only, the session offers no method this component can take

With readyTimeout, a payment field's onLoadError and loaderror can also receive ready_timeout, which the SDK raises itself when the field is not ready in time.

For one of the three session_terminal_ codes, start a new payment and mount the field on its token. A field whose session is already paid shows its notice and sends ready, not an error. The field's notice, its three codes and component_origin_missing come with the checkout shipped with Wajub.js 1.8.0, whatever the version on your page: before, such a field sent nothing.

Codes a payment can end with

onError, the error event and a confirmPayment() rejection carry the provider's decline code when there is one, insufficient_funds for example. Otherwise they carry one of these.

CodeCause
payment_declinedThe provider declined the payment without a more precise reason
blockedRefused before reaching the provider: a Shield block or a restriction on your account
validation_errorA field the payer filled was refused, such as a phone number that is not valid
rate_limitedToo many payment attempts on this session. Wait a short while before offering another attempt
server_errorWajub or the provider failed. retryable says whether another attempt is worth offering
submission_failedPayment fields only. A submission you asked for with submit() or confirmPayment() was refused before any charge: a form not ready, whatever the method, such as a Mobile Money number that is not valid, a card left incomplete with any provider, card fields still loading, a bank transfer with no IBAN or account number or a crypto payment with no asset, a card number its provider rejected, a provider's window that failed, was closed or could not open. message says why, in the payer's language

WajubPay, deprecated

The script also exposes window.WajubPay.create(), an older way to mount an inline checkout. It is deprecated since 1.6.0 and will be removed in a future version: its first call writes a warning to the console. Use mount(), open() or a framework component instead.

Framework packages

The three wrappers export the same set, with the idioms of each framework.

ExportWhat it is
WajubProviderLoads the runtime once for a subtree
CheckoutEmbedThe hosted checkout, inline
CardComponent, MobileMoneyComponent, WalletComponent, PaymentComponent, AddressComponent, and from 1.8.0 BankTransferComponent, CryptoComponentOne field group each
ComponentEmbedThe same, with type as a prop
useWajub, useWajubOptional, useLoadWajubReach the runtime
useConfirmPaymentSubmit a field component
WajubContext in React, WAJUB_INJECTION_KEY in Vue, getWajubContext and setWajubContext in Svelte, and useWajubContext in all threeThe raw context, for your own hook, composable or helper

@wajub/angular, from 1.8.1, exports the Angular forms of the same set.

ExportWhat it is
provideWajub(options?)Configures the runtime's load for the application or a route, loadOptions and defer, in place of WajubProvider
WajubServiceLoads the runtime once: the runtime, loading and error signals, runtime$, load() and getRuntime(), in place of useWajub, useWajubOptional and useLoadWajub
WAJUB_OPTIONSThe injection token provideWajub() sets
WajubCheckoutEmbedComponent<wajub-checkout-embed>, the hosted checkout, inline
WajubCardComponent, WajubMobileMoneyComponent, WajubWalletComponent, WajubPaymentComponent, WajubAddressComponent, WajubBankTransferComponent, WajubCryptoComponent<wajub-card>, <wajub-mobile-money>, <wajub-wallet>, <wajub-payment>, <wajub-address>, <wajub-bank-transfer>, <wajub-crypto>: one field group each
WajubComponentEmbedComponent<wajub-component>, the same with a type input
WajubComponentBaseThe class the field components share: their inputs, their outputs and componentInstance
injectConfirmPayment(sessionId?)Submits a field component, in place of useConfirmPayment. isConfirming is a signal
Wrapper propApplies toDefault
className in React, class in Vue and Svelte, and on the host element in AngularBothnone
style, in React and Vue, and on the host element in AngularBothnone
minHeightCheckoutEmbed480
minHeightField componentsUnset from 1.8.0. With the skeleton loader, the container holds from the first render until the form mounts componentEstimatedHeight(type, variant, fields) on the server and at hydration, componentReserveHeight() on a later render, then the SDK holds the room. With loader: 'none', 200 for card, 240 for mobileMoney, 120 for wallet, 360 for payment, 280 for address, 200 for bankTransfer, 240 for crypto. A value you pass, or in React, Vue and Angular a min-height in style, is kept for the component's life
loaderField componentsskeleton from 1.8.0, where @wajub/js itself defaults to none
onInstance, the (instance) output in AngularField componentsnone, fires with null on unmount

In Angular, the props are inputs and outputs of the same names: every option is an input, every callback an output named without on, such as (loadError) for onLoadError, and onBeforeSubmit is the beforeSubmit input, since an output cannot answer.

From 1.8.0, every option of ComponentConfig is a prop of the field components, the callbacks included, and the handlers you pass are read when the event comes. appearance, locale, layout and frameTitle reach the mounted field through update(), only when their content changes. A new sessionId, type, variant, fields, loader, readyTimeout, collectAddress, addressMode or collectName mounts a new field in the same container. CheckoutEmbed sends appearance, theme, locale and layout through update(), and loads again for a new sessionId or embedOrigin. @wajub/angular does the same from its first version, 1.8.1.

Each wrapper, Angular's included, also re-exports every type of @wajub/js, so import type { PaymentSuccess } from '@wajub/react' works as well as from @wajub/js.

useWajubOptional() returns the runtime or null in React. From 1.6.0 it returns a ref in Vue and a store in Svelte, both null until the runtime has loaded. In Angular, the runtime signal of WajubService holds null until then.

Each package has its own page, because the differences are real: React, Vue, Svelte, Angular.

What did you think of this content?