Skip to content

Wajub.js

The core browser package, how to install it, allow it in your CSP and call it.

@wajub/js puts Wajub's checkout and payment fields on any page. This page covers installing it, the Content Security Policy it needs, and the three ways to call it.

@wajub/js

Stable · GA

npm + CDN

Version
1.8.1
Runtime
Any modern browser (ES2020+)

Covers

  • Payments
  • Payment Links

Not in this package: Billing, Transfers, Sync, Shield, Tax.

No offline build

Whether you install the npm package or use the script tag, Wajub.js loads from https://js.wajub.com. There is no offline or self-hosted build: a browser that cannot reach js.wajub.com has no checkout, so plan for that origin the way you plan for the API.

Content Security Policy

A page with a strict Content-Security-Policy must allow these hosts. Without them the browser blocks the checkout and reports the violation in the console.

DirectiveHostWhy
script-srchttps://js.wajub.comWajub.js, from npm or the script tag
frame-srchttps://pay.wajub.comThe hosted checkout, the overlay and every payment field
connect-srchttps://pay.wajub.comfetchSession(), signals() and the checkout preload
connect-srchttps://api.wajub.comcreatePayment() and initCheckout() with a publishable key
img-srchttps://pay.wajub.comThe Wajub mark in the overlay footer
The directives to add to your own policy
script-src https://js.wajub.com;
frame-src https://pay.wajub.com;
connect-src https://pay.wajub.com https://api.wajub.com;
img-src https://pay.wajub.com;

If you serve the checkout from your own verified domain, use that host wherever pay.wajub.com appears. The checkout's own page sets its policy itself; yours only governs the page around it.

A payment field whose payment continues on the provider's own page, a crypto payment for example, takes your whole page there, sometimes with a form post. If your policy sets form-action, it must allow that post.

A payment field with loader: 'skeleton' adds one <style> element for its pulse, carrying the nonce of your page's inline scripts or styles. A style-src with neither that nonce nor 'unsafe-inline' only stops the pulse: the placeholder still shows.

Three entry points

They give you the same API. Pick one by when Wajub.js should load and whether you have a bundler.

Entry pointLoads Wajub.jsUse it when
@wajub/jsOn importYou have a bundler and the page always shows a checkout
@wajub/js/pureOnly when you call loadWajub()You render on the server, or the checkout is behind a click
https://js.wajub.comOn script loadNo bundler, plain HTML

Install the package with your usual manager. It ships as an ES module only, so a CommonJS require() will not resolve it.

npm install @wajub/js

Without a bundler, the script tag puts wajub, Wajub and WajubError on window. The .mjs module gives you the same as named exports.

From the CDN
<script src="https://js.wajub.com"></script>

<!-- or, as a module -->
<script type="module">
  import { mount } from 'https://js.wajub.com/wajub.mjs';
</script>

Three ways to call the same API

Every method below exists in all three forms. Pick one per project and stay on it, because mixing them makes the loading order hard to read.

The named helpers are the shortest, with no loading step to write.

Named helpers
import { mount, open, checkout, fetchSession, confirmPayment } from '@wajub/js';

const instance = await mount('#checkout', { sessionId });

loadWajub() hands you the runtime instead, loaded when you decide. It returns null when there is no window, so a server render is safe.

Holding the runtime
import { loadWajub } from '@wajub/js/pure';

const rt = await loadWajub();
if (rt) {
  const instance = await rt.wajub.mount('#checkout', { sessionId });
}

From the CDN, the same objects sit on window and there is nothing to await.

On the global
const instance = await wajub.mount('#checkout', { sessionId });

What the package exports

Every export, with what it returns, is listed in the SDK reference.

preload() is the one worth knowing about: call it when the customer reaches the cart, and the checkout opens faster when they reach the pay button.

The Wajub() client

The runtime methods all need a sessionId on every call. The client holds it once instead, and it is the only way to create a payment from the browser. Wajub() takes a publishable key, sandbox or live, a session token, or both.

Three ways to build one
const client = Wajub('pk_test.xxxxxxxxxxxxxxxx…');

const client = Wajub.session(sessionId);

const client = Wajub({ publishableKey: 'pk_test.xxxxxxxxxxxxxxxx…', sessionId });
MethodWhat it does
createPayment(params)POST /payments with the publishable key, returns the session
useSession(sessionId)Binds an existing token to the client
fetchSession()Amount, currency, merchant name, methods, saved methods
preload()Prepares the checkout before it is needed
signals()Device signals for a charge from your server, see Shield
mount(el, config), open(config), checkout(config)The three hosted modes
components(config)The fields factory
confirmPayment(options)Submits a field component and waits for the outcome
initCheckout(options)Creates the payment and mounts it in one call

initCheckout is the whole flow without a backend. It needs a publishable key, because the amount is decided by the page.

Create and mount in one call
await Wajub('pk_test.xxxxxxxxxxxxxxxx…').initCheckout({
  mode: 'inline',
  container: '#checkout',
  payment: { amount: 35000, currency: 'XOF', customer: { email: 'buyer@example.com' } },
  onSuccess: (transaction) => console.log('paid', transaction),
});

On the server

loadWajub() and every helper return null when there is no window, so a server render never throws. The difference is the import itself: @wajub/js loads Wajub.js as soon as it is imported, @wajub/js/pure only when you call loadWajub().

No side effect on import
import { loadWajub } from '@wajub/js/pure';

payButton.addEventListener('click', async () => {
  const rt = await loadWajub();
  await rt?.wajub.open({ sessionId });
});

With a framework package, there is nothing to do: this matters only when you wire @wajub/js yourself.

Where each subject is documented

This page stops at the package. Each subject has its own page.

You want toRead
Mount, open or redirect, with every option and callbackDisplay the checkout
Build your own layout around the fieldsPayment fields
Create the session, and what the token may doSessions and security
Change colours, theme, locale and layoutAppearance
Read every method, type and checkout stateSDK reference
Type it correctly, with or without npmTypeScript
Work out why nothing rendersTroubleshooting

What did you think of this content?