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.
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> | 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?) | number | From 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?) | number | From 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? } |
WajubError | Declared in the types only | Not 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.
| 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, 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 |
create | The Wajub factory, same object |
version | The runtime version string |
sdkMajor | The major version |
checkoutOrigin | The origin the iframes come from |
createHeadless | Removed, 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.
| 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 pk_test. key. A live pk. key is not recognised in 1.8.1 |
apiKey | Deprecated alias for publishableKey |
apiBase | API origin override, defaults to https://api.wajub.com |
componentOrigin | Canonical origin for payment fields |
Client members
| Member | What it does |
|---|---|
publishableKey, sessionId, environment | What the client currently holds |
apiBase, checkoutOrigin | Where 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, checkout | The 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
| 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() | 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.
| Option | Type | Default |
|---|---|---|
sessionId | string | Required |
locale | string | Session default |
layout | CheckoutLayout | Session default |
appearance | AppearanceConfig | Your Dashboard branding. From 1.8.0, inherit is read around the container for mount(), over the whole page for open() |
embedOrigin | string | The current page origin |
loadingText | string | Empty |
showLoading | boolean | true |
theme | EmbedTheme | Derived from appearance, deprecated |
| 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?, method_id? } |
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, 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.
| Option | Type | Applies to |
|---|---|---|
sessionId | string | Required |
locale | string | All |
appearance | AppearanceConfig | All |
layout | CheckoutLayout | payment |
componentOrigin | string | All, defaults to the page origin |
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. Through the Vue and Svelte wrappers from 1.5.1. From 1.7.2, fields sets no 120px floor on the iframe |
frameTitle | string | All. The iframe's name for screen readers, Wajub <type> component by default. From 1.7.0, wrappers included |
loader | none or skeleton | All, from 1.8.0. none by default, skeleton in the framework wrappers. Loading without a layout shift |
readyTimeout | number, milliseconds | All, 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 |
| Callback | Receives |
|---|---|
onBeforeSubmit | Nothing. Return false, or throw, to stop the charge. 30 seconds at most. 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? }, from 1.8.0 |
onResize | { height }, from 1.8.0. The hosted checkout's onResize receives the number itself |
onRedirect | ComponentRedirectEvent, { 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
| 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. 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
| 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 }. 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
| Option | Type | Default |
|---|---|---|
sessionId | string | Required |
components | ComponentInstance | Required, one mounted component |
timeout | number | 60000 milliseconds, 0 waits forever. From 1.8.0, paused while the payment waits on the payer |
callback | string | Deprecated, 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.
| 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. 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.
| 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 |
features | Record<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.
| Group | Keys |
|---|---|
| Preset | theme: stripe, night, flat, none. From 1.7.1, light, dark, system or auto is read as 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, 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.
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 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.
| 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, from the provider |
retryable | boolean |
param | string 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.
| Code | Cause |
|---|---|
secret_key_in_browser | A sk. 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 | From 1.8.0, a payment field sent no ready within readyTimeout. Reaches onLoadError and loaderror, and is retryable |
component_unmounted | The 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_unavailable | The components runtime did not load |
load_error | The 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.
| 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, 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_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. 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.
| Code | Cause |
|---|---|
payment_declined | The provider declined the payment without a more precise reason |
blocked | Refused before reaching the provider: a Shield block or 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 |
server_error | Wajub or the provider failed. 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, 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.
| Export | What it is |
|---|---|
WajubProvider | Loads the runtime once for a subtree |
CheckoutEmbed | The hosted checkout, inline |
CardComponent, MobileMoneyComponent, WalletComponent, PaymentComponent, AddressComponent, and from 1.8.0 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, from 1.8.1, exports the Angular forms of the same set.
| Export | What it is |
|---|---|
provideWajub(options?) | Configures the runtime's load for the application or a route, loadOptions and defer, in place of WajubProvider |
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 |
WajubComponentBase | The 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 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 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 |
loader | Field components | skeleton from 1.8.0, 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.
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.