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 · GAnpm + 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.
| Directive | Host | Why |
|---|---|---|
script-src | https://js.wajub.com | Wajub.js, from npm or the script tag |
frame-src | https://pay.wajub.com | The hosted checkout, the overlay and every payment field |
connect-src | https://pay.wajub.com | fetchSession(), signals() and the checkout preload |
connect-src | https://api.wajub.com | createPayment() and initCheckout() with a publishable key |
img-src | https://pay.wajub.com | The Wajub mark in the overlay footer |
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 point | Loads Wajub.js | Use it when |
|---|---|---|
@wajub/js | On import | You have a bundler and the page always shows a checkout |
@wajub/js/pure | Only when you call loadWajub() | You render on the server, or the checkout is behind a click |
https://js.wajub.com | On script load | No 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/jsWithout a bundler, the script tag puts wajub, Wajub and WajubError on window. The .mjs
module gives you the same as named exports.
<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.
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.
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.
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.
const client = Wajub('pk_test.xxxxxxxxxxxxxxxx…');
const client = Wajub.session(sessionId);
const client = Wajub({ publishableKey: 'pk_test.xxxxxxxxxxxxxxxx…', sessionId });| Method | What 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.
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),
});A secret key in a browser is refused and reported
A secret key must never be used from a browser: Wajub.js refuses it, and the API refuses it with
403 and emails the key's owner. Use your publishable key here, or create the session on your
server and send the browser only the token.
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().
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 to | Read |
|---|---|
| Mount, open or redirect, with every option and callback | Display the checkout |
| Build your own layout around the fields | Payment fields |
| Create the session, and what the token may do | Sessions and security |
| Change colours, theme, locale and layout | Appearance |
| Read every method, type and checkout state | SDK reference |
| Type it correctly, with or without npm | TypeScript |
| Work out why nothing renders | Troubleshooting |
Use it through a framework