SDK reference
Every export, option, method, type, state and error code of @wajub/js.
Every export, option, method, type, state and error code of @wajub/js. The guide pages explain
when to use each piece.
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.
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/js | Returns | Notes |
|---|---|---|
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> | Device signals for a charge from your server, see Shield |
preload(sessionId, options?) | Promise<void | null> | Gets the checkout ready before the payer reaches it. { iframe: true } also loads that session's checkout |
appearanceFrom(container, source?) | Promise<AppearanceConfig | null> | What appearance.inherit would read for a component in container. null on the server |
componentReserveHeight(type, variant?, fields?) | number | The room loader: 'skeleton' holds, in px |
componentEstimatedHeight(type, variant?, fields?) | number | The same room as an estimate, identical on the server and in the browser: hold it in a server render and its hydration |
getVersion() | Promise<string | null> | The loaded runtime version |
loadWajub() | Promise<WajubRuntime | null> | null on the server |
WajubError | Declared in the types only | Not exported at runtime, read it from loadWajub() |
WajubRuntime
What loadWajub() resolves to, and what the script tag puts on window.
| Member | What it is |
|---|---|
wajub | The SDK object, the table below |
Wajub | The client factory |
WajubError | The error class |
wajub
| Member | Signature |
|---|---|
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. {} when there is nothing to read, never throws |
componentReserveHeight | (type, variant?, fields?) => number. The room loader: 'skeleton' holds, in px |
create | The Wajub factory, same object |
version | The runtime version string |
These are synchronous once you hold the runtime.
Wajub() client
A client holds a publishable key, a session, or both, so you stop repeating them.
| Call | What 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 |
| Option | What it does |
|---|---|
sessionId | The authorization_token from your server |
publishableKey | Your publishable key, pk. or pk_test. |
Client members
| Member | What it does |
|---|---|
publishableKey, sessionId, environment | What the client currently holds |
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() | Device signals for a charge from your server, see Shield |
preload(sessionId?, options?) | Gets the checkout ready ahead of time |
mount, open, checkout | The three hosted modes, session already bound |
components(sessionIdOrConfig?, config?) | The fields factory |
appearanceFrom(container, source?) | What appearance.inherit would read |
confirmPayment(options?) | Submits a mounted component |
Factory statics
| Member | What 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() | Device signals for a charge from your server, see Shield |
EmbeddedConfig
Used by mount(), by CheckoutEmbed in every framework, and as the base of PopupConfig.
| Option | Type | Default |
|---|---|---|
sessionId | string | Required |
locale | string | Session default |
layout | CheckoutLayout | Session default |
appearance | AppearanceConfig | Your Dashboard branding. inherit is read around the container for mount(), over the whole page for open() |
loadingText | string | Empty |
showLoading | boolean | true |
| Callback | Receives |
|---|---|
onReady | CheckoutInstance |
onSuccess | PaymentSuccess |
onError | WajubError, a payment that failed, or nothing_to_submit when submit() had no button to press |
onLoadError | WajubError, 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 |
onCancel | Nothing |
onExpired | Nothing |
onStateChange | { state?: CheckoutState, method?: string } |
onMethodChange | { methodId? } |
onBreakdown | EmbedBreakdown, { subtotal, discount, tax, total, currency } in major units |
onResize | height: number, inline only |
onClose | Nothing. 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.
| Option | Type | Default |
|---|---|---|
width | number | 920 |
height | number | 680 |
closeOnOverlay | boolean | false |
closeOnEscape | boolean | true |
closeOnSuccess | boolean | true |
closeOnCancel | boolean | true |
closeOnExpired | boolean | true |
The three closeOn outcome flags run after your callback, never before it.
CheckoutInstance and PopupInstance
| Method | What 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. 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 |
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. When a checkout comes back,
onReady fires once more, and the payment resumes where the session stands.
Driving the instance says when
submit() has no button to press.
ComponentConfig
Passed to wajub.components(sessionId, options) as defaults, and to factory.create(type, config)
per component.
| Option | Type | Applies to |
|---|---|---|
sessionId | string | Required |
locale | string | All |
appearance | AppearanceConfig | All |
layout | CheckoutLayout | payment |
collectAddress | shipping or billing | payment |
addressMode | shipping or billing | address |
collectName | boolean | payment, address |
fields.phone | always, auto, never | payment, address |
fields.customer | auto or external | All but address |
fields.country | ISO 3166 alpha-2 | mobileMoney, payment |
fields.operator | choose or detect | mobileMoney, payment |
variant | default or fields | All but address |
frameTitle | string | All. The frame's name for screen readers, Wajub <type> component by default |
loader | none or skeleton | All. none by default, skeleton in the framework packages. Loading without a layout shift |
readyTimeout | number, milliseconds | All. Without ready in time, onLoadError and loaderror receive ready_timeout. 0 or absent: no limit, and no error |
| Callback | Receives |
|---|---|
onBeforeSubmit | Nothing. Return false, or throw, to stop the charge. Answer within 30 seconds. All but address |
onReady | ComponentInstance |
onChange | ComponentChangeEvent |
onFocus, onBlur | Nothing |
onLoadError | WajubError: a code a checkout sends when it cannot start, or ready_timeout with readyTimeout |
onSuccess | The transaction |
onError | WajubError |
onMethodChange | { methodId? } |
onStateChange | ComponentStateChangeEvent, { state, method? } |
onResize | { height }. The hosted checkout's onResize receives the number itself |
onRedirect | ComponentRedirectEvent, { url, method }. 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
| Member | What it is |
|---|---|
sessionId | The 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.
| Method | Returns |
|---|---|
mount(container) | The instance |
unmount(), destroy() | Nothing |
on(event, fn), off(event, fn) | The instance |
update({ locale, appearance, layout, frameTitle }) | The instance. appearance merges as on the hosted checkout |
focus(), blur(), submit() | The instance. 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
| Event | Payload |
|---|---|
ready | ComponentInstance |
change | ComponentChangeEvent |
focus, blur | Nothing |
loaderror | WajubError: a code a checkout sends when it cannot start, or ready_timeout with readyTimeout |
resize | { height } |
success | PaymentSuccess |
error | WajubError |
statechange | ComponentStateChangeEvent, { state, method? } |
methodchange | { methodId } |
redirect | ComponentRedirectEvent, { url, method }. Right before your page leaves for the provider's own page |
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 frame for screen readers.
error also carries submission_failed when a submission you asked for, with submit() or
confirmPayment(), was refused before any charge.
ConfirmPaymentOptions
| Option | Type | Default |
|---|---|---|
sessionId | string | Required |
components | ComponentInstance | Required, one mounted component |
timeout | number | 60000, in milliseconds. 0 waits forever. Paused while the payment waits on the payer |
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 submitted.
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, a decline code, or the component's load error.
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 your page has left for the provider's
(redirect). A component removed from the page while you wait rejects with component_unmounted,
as unmount() and destroy() do.
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.
| Field | Type | What it is |
|---|---|---|
id | string | Wajub's id of the payment, what GET /payments/{id} takes |
reference | string | Your reference, the one you sent on POST /payments |
amount | number | What the payer paid, fee included when the payer bears it, in major units |
currency | string | ISO 4217 code |
method | string | The method family: mobile_money, card…, unknown when the session was already paid |
status | "success" | Always success |
timestamp | string | When the payment completed, ISO 8601 |
receipt_url | string | The 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.
CreatePaymentParams
What client.createPayment() sends. The same fields as
POST /payments, narrowed to what a browser may set.
| Field | Type |
|---|---|
amount | number, required |
currency | string, required |
reference | string |
description | string |
customer | { email?, name?, phone?, country? }, required with an email or a phone, or the API answers 422 |
bearer | merchant or customer, who pays the fee |
callback | HTTPS URL, redirect flow only |
metadata | Record<string, string> |
CreatePaymentResult
| Field | What it is |
|---|---|
sessionId | Pass this to mount, open or components |
authorizationToken | The same value, under the API's name |
authorizationUrl | The hosted page, or null |
transaction | The transaction object, or null |
raw | The untouched API response |
InitCheckoutOptions
Creates the payment and shows it in one call. Everything in EmbeddedConfig applies too.
| Option | Type | What it does |
|---|---|---|
mode | inline, overlay, redirect | How the checkout appears |
container | string or HTMLElement | Required for inline |
sessionId | string | Skips createPayment() |
payment | CreatePaymentParams | Used when there is no sessionId |
checkout | Partial<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.
| Field | Type |
|---|---|
session_id | string |
status | string |
environment | sandbox or live |
amount | number, in major units like the API |
currency | string |
merchant_name | string |
payment_methods | Array<{ id, type, label }> |
saved_methods | Array<{ id, type, label }>, returning customers your server named |
saved_methods lists the methods a customer paid with before, and only on a payment whose customer
your server named: one created with POST /payments and your secret key (or a restricted key) with
a customer, one activated with POST /payments/{id}/activate, or the payment of an invoice linked
to one of your customers. A payment link, a payment created with your publishable key and an invoice
addressed to a name only never list any, because the e-mail typed on those pages may be someone
else's. Paying with a saved method on such a payment is refused before any charge, with HTTP 422,
the code saved_method_not_allowed and the message "Saved payment methods cannot be used for this
payment."
ClientSignals
What signals() resolves to: an opaque object holding the payer's device signals, for a charge you
make from your own server. Do not build, edit or store it. How to send it is on
Shield.
AppearanceConfig
The full key list, the presets and the filtering rules are on Appearance.
| Group | Keys |
|---|---|
| Preset | theme: stripe, night, flat, none. light, dark, system or auto sets colorScheme when none is given |
| Colour | primaryColor, secondaryColor, backgroundColor, buttonTextColor, inputBackgroundColor, inputBorderColor, textMutedColor, successColor, errorColor |
| Shape | fontFamily, borderRadius, shadow |
| Behaviour | colorScheme, labels, disableAnimations |
| Tokens | variables, sixteen design token keys |
| Selectors | rules, .Input and .Label only |
| Your page | inherit: 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.
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, EXPIREDA new instance reports INITIATED until the payment moves. PIN_REQUIRED, PHONE_REQUIRED,
BIRTHDAY_REQUIRED and ADDRESS_REQUIRED are the steps some payments 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.
| Field | Type |
|---|---|
message | string, written for a human |
type | api_error, authentication_error, invalid_request_error, payment_error, rate_limit_error |
code | string, the machine name |
decline_code | string or null, why the payment was declined, when it was |
retryable | boolean |
param | string or null, the field at fault |
Codes raised by the SDK itself
They are integration mistakes.
| Code | Cause |
|---|---|
secret_key_in_browser | A secret key passed to Wajub() |
invalid_publishable_key | A key that is not a publishable key |
invalid_init | Wajub() called with neither a publishable key nor a session token |
missing_publishable_key | createPayment() without a key |
missing_session_token | createPayment() got an answer without a session token |
missing_session | No sessionId on the client |
missing_container | initCheckout in inline mode with nowhere to mount |
missing_component | confirmPayment without a submittable component |
wallet_not_supported | confirmPayment on a wallet component |
address_not_payment | confirmPayment on an address component |
confirmation_timeout | No outcome within timeout. The payment goes on and can still succeed |
ready_timeout | A payment field sent no ready within readyTimeout. Reaches onLoadError and loaderror, and is retryable |
component_unmounted | The component was unmounted, destroyed or removed from the page while confirmPayment waited on it |
components_unavailable | The components runtime did not load |
load_error | The checkout 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.
| Code | Cause |
|---|---|
session_not_found | The token is unknown, or the session could not be read |
session_terminal_expired | The payment ended and its token has stopped. On a payment field, the session expired unpaid, and the field shows its notice |
session_terminal_cancelled | Payment fields only, the session was already cancelled. The field shows its notice |
session_terminal_failed | Payment fields only, the session already ended on a failed payment. The field shows its notice |
component_origin_missing | Payment fields only, a field not mounted with Wajub.js or a framework package. Mount it with one of them |
invalid_component | Payment fields only, a component type that does not exist |
method_unavailable | Payment 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.
Codes a payment can end with
onError, the error event and a confirmPayment() rejection carry the decline code when there is
one, insufficient_funds for example. Otherwise they carry one of these.
| Code | Cause |
|---|---|
payment_declined | The payment was declined without a more precise reason |
blocked | Refused by Shield or by a restriction on your account |
validation_error | A field the payer filled was refused, such as a phone number that is not valid |
rate_limited | Too many payment attempts on this session. Wait a short while before offering another attempt |
saved_method_not_allowed | A saved method was used on a payment that lists none, see SessionPreview. Nothing was charged, and the payer can still pay with a method they enter |
server_error | A temporary failure. retryable says whether another attempt is worth offering |
submission_failed | Payment fields only. A submission you asked for with submit() or confirmPayment() was refused before any charge: a form not ready, such as a Mobile Money number that is not valid, a card left incomplete, fields still loading, a bank transfer with no IBAN or account number or a crypto payment with no asset, a card number refused, or a payment window that was closed or could not open. message says why, in the payer's language |
Framework packages
@wajub/react, @wajub/vue and @wajub/svelte export these, each in its framework's idiom.
| Export | What it is |
|---|---|
WajubProvider | Loads the runtime once for a subtree |
CheckoutEmbed | The hosted checkout, inline |
CardComponent, MobileMoneyComponent, WalletComponent, PaymentComponent, AddressComponent, BankTransferComponent, CryptoComponent | One field group each |
ComponentEmbed | The same, with type as a prop |
useWajub, useWajubOptional, useLoadWajub | Reach the runtime |
useConfirmPayment | Submit a field component |
WajubContext in React, WAJUB_INJECTION_KEY in Vue, getWajubContext and setWajubContext in Svelte, and useWajubContext in all three | The raw context, for your own hook, composable or helper |
@wajub/angular exports these.
| Export | What it is |
|---|---|
provideWajub(options?) | Provides Wajub to the application or a route, in place of WajubProvider. With defer: true, nothing loads until you call WajubService.load() |
WajubService | Loads the runtime once: the runtime, loading and error signals, runtime$, load() and getRuntime(), in place of useWajub, useWajubOptional and useLoadWajub |
WAJUB_OPTIONS | The 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 |
injectConfirmPayment(sessionId?) | Submits a field component, in place of useConfirmPayment. isConfirming is a signal |
| Component prop | Applies to | Default |
|---|---|---|
className in React, class in Vue and Svelte, and on the host element in Angular | Both | none |
style, in React and Vue, and on the host element in Angular | Both | none |
minHeight | CheckoutEmbed | 480 |
minHeight | Field components | Unset. With loader: 'none', pass 0 to fit the inputs. Inside your own payment method box |
loader | Field components | skeleton, where @wajub/js itself defaults to none |
onInstance, the (instance) output in Angular | Field components | none, 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.
Every option of ComponentConfig is a prop of the field components, the callbacks included, and a
new handler takes over without a reload. A change to appearance, locale, layout or
frameTitle applies to the mounted field without a reload. A new sessionId, type, variant,
fields, loader, readyTimeout, collectAddress, addressMode or collectName mounts a new
field in the same container. CheckoutEmbed applies appearance, locale and layout the same
way, and loads again for a new sessionId.
Each framework package 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. 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.