Lesaka Online SDK#
Introduction#
The Lesaka Online SDK is a client-side JavaScript library for merchants want more fine-grained control over the appearance and behaviour of their checkout. Instead of redirecting users to an Adumo-hosted payment page, payment elements are rendered inline within your own site.
Your layout, branding, and surrounding UI remain entirely under your control. Lesaka Online handles PCI-compliant card capture and transaction processing behind the scenes.
Features#
- Render payment UI inline on your page — no redirect required
- Supports Card payments (
3DS,Non-3DS,Apple Pay,Google Pay) - Supports APMs (
Ozow,Zapper,OTT Voucher,Mobicred,Masterpass) - Separate
Payments,PaymentMethod, andPaymentMethodselements for flexible layout composition - Fully customisable appearance and themeing. See more at Appearance
- Element-level lifecycle callbacks
- Per-payment-method lifecycle callbacks
Requirements#
Merchant Account#
You must have a live merchant account with Lesaka Online. Contact support to apply or sign up via the merchant portal .
Once your account is approved you will be issued:
| Credential | Description |
|---|---|
Merchant ID (cuid) | Unique identifier for your merchant account |
Application ID (auid) | Identifies a specific application or payment flow. A merchant can have multiple Application IDs. |
| JWT Secret | Used to sign session requests from your backend. Retrieve from the portal under E-Commerce → JWT and API Authentication → Reveal |
Virtual Template Configuration#
Currently the SDK is only supported for Virtual template v2 configured applications. You can enable this through merchant portal as follows:
- Tap/Hover
E-commerceon the side menu and selectConfigure Hosted Payment Page - Select an application from the
Applicationdropdown - Select
Version 2from theTemplatedropdown. - Tap/Click
Save
You will need to enable template V2 for each environment in which you wish to use the SDK.
Staging - https://www.staging-portal.adumoonline.com
Production - https://www.portal.adumoonline.com
Technical Requirements#
- Backend: Any server-side language or runtime capable of signing HS256 JWTs and making HTTPS form POST requests (e.g. Node.js, PHP, Java, Python, .NET)
- Frontend: Any modern web browser
Installation#
Add the SDK script to your HTML page. It exposes a global window.adumo object once loaded.
Staging:
<script src="https://staging-gateway.adumoonline.com/sdk/v1/adumo-js-client.js"></script>
Production:
<script src="https://gateway.adumoonline.com/sdk/v1/adumo-js-client.js"></script>
Usage#
Integration has two parts: a server-side session creation step , and client-side SDK initialisation .
1. Create a Session (Server-side)#
Your backend generates a signed JWT and POSTs it along with the transaction details to the Adumo API. The response contains a token (the session token) which you return to your frontend.
Endpoint:
| Environment | URL |
|---|---|
| Staging | https://staging-apiv3.adumoonline.com/product/payment/v1/initialisevirtual |
| Production | https://apiv3.adumoonline.com/product/payment/v1/initialisevirtual |
Use initialiseverification instead of initialisevirtual for card verification flows.
Node.js / Express example:
const express = require("express");
const fetch = require("node-fetch");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const app = express();
app.use(express.json());
function generateJWT(cuid, auid, mref, amount, secretKey, notificationURL) {
const now = Math.floor(Date.now() / 1000);
const payload = {
iss: "My App",
cuid,
auid,
amount,
mref,
jti: crypto.randomBytes(32).toString("base64"),
iat: now - 60,
exp: now + 600,
notificationURL,
};
return jwt.sign(payload, secretKey, { algorithm: "HS256" });
}
app.post("/create-session", async (req, res) => {
const { Amount, CurrencyCode, Flow, MerchantReference, puid } = req.body;
const MerchantID = process.env.MERCHANT_ID;
const ApplicationID = process.env.APP_ID;
const jwtSecret = process.env.JWT_SECRET;
const Token = generateJWT(
MerchantID,
ApplicationID,
MerchantReference,
Amount,
jwtSecret,
);
const apiUrl =
Flow === "CARD_VERIFICATION"
? "https://staging-apiv3.adumoonline.com/product/payment/v1/initialiseverification"
: "https://staging-apiv3.adumoonline.com/product/payment/v1/initialisevirtual";
const formData = new URLSearchParams({
Amount,
ApplicationID,
CurrencyCode,
Flow,
Token,
MerchantID,
MerchantReference,
puid,
});
const response = await fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: formData,
});
const data = await response.json();
if (data.token) {
res.json({ success: true, sessionToken: data.token });
} else {
const error =
data.extendedErrors
?.map((e) => (e.field ? `${e.field}: ${e.message}` : e.message))
.join(", ") ?? "Unexpected response from payment API";
res.status(response.status).json({ success: false, error });
}
});
app.listen(3001);
2. Initialise the SDK (Client-side)#
Once your page has received a sessionToken from your backend, initialise the Adumo client and mount the payment elements.
<!-- Two containers: one for the payment method selector, one for the payment form -->
<div id="payment-methods-container"></div>
<div id="payments-element-container"></div>
<!-- Alternative: use this container with initPaymentMethod when mounting a single payment method button -->
<!-- <div id="payment-method-container"></div> -->
<!-- Button to restart the session when the transaction completes or is cancelled -->
<button class="button" id="start-new-session-btn">Cancel Session</button>
<script>
const adumoClient = window.adumo.initClient();
let transactionCompleted = false;
function startNewSession() {
window.location.href = "redirect to capture page";
}
// Mount the payments element
adumoClient.initPayments({
ref: document.getElementById("payments-element-container"),
sessionToken: "<token from your backend>",
onReady: (actions) => {
// Payment UI is mounted. `actions` exposes imperative actions i.e. cancelSession
console.log("Payments element ready");
document
.getElementById("start-new-session-btn")
.addEventListener("click", function () {
if (!transactionCompleted && actions) {
// Cancel current session but don't navigate away
actions.cancelSession();
return;
}
startNewSession();
});
},
onCancel: (response) => {
console.log("Payment cancelled", response);
startNewSession();
},
onSuccess: (response) => {
console.log("Payment successful", response);
transactionCompleted = true;
},
onFailure: (response) => {
console.error("Payment failed", response);
transactionCompleted = true;
},
onError: (response) => {
console.error("Payment error", response);
},
// Optional: per-payment-method callbacks
paymentMethods: {
Card: {
onSuccess: (response) => console.log("Card success", response),
onFailure: (response) => console.error("Card failure", response),
onError: (response) => console.error("Card error", response),
onCancel: (response) => console.log("Card cancelled", response),
},
MasterPass: {
onSuccess: (response) => console.log("MasterPass success", response),
onFailure: (response) => console.error("MasterPass failure", response),
onError: (response) => console.error("MasterPass error", response),
onCancel: (response) => console.log("MasterPass cancelled", response),
},
Mobicred: {
onSuccess: (response) => console.log("Mobicred success", response),
onFailure: (response) => console.error("Mobicred failure", response),
onError: (response) => console.error("Mobicred error", response),
onCancel: (response) => console.log("Mobicred cancelled", response),
},
OttVoucher: {
onSuccess: (response) => console.log("OTT Voucher success", response),
onFailure: (response) => console.error("OTT Voucher failure", response),
onError: (response) => console.error("OTT Voucher error", response),
onCancel: (response) => console.log("OTT Voucher cancelled", response),
},
Ozow: {
onSuccess: (response) => console.log("Ozow success", response),
onFailure: (response) => console.error("Ozow failure", response),
onError: (response) => console.error("Ozow error", response),
onCancel: (response) => console.log("Ozow cancelled", response),
},
Zapper: {
onSuccess: (response) => console.log("Zapper success", response),
onFailure: (response) => console.error("Zapper failure", response),
onError: (response) => console.error("Zapper error", response),
onCancel: (response) => console.log("Zapper cancelled", response),
},
},
});
// Mount the payment method selector
adumoClient.initPaymentMethods({
ref: document.getElementById("payment-methods-container"),
appearance: {
styles: {
PaymentMethodContainer: {
backgroundColor: "rgb(240, 240, 240)",
borderRadius: "var(--border-radius)",
paddingBlock: "calc(3 * var(--spacing-unit))",
paddingInline: "calc(3 * var(--spacing-unit))",
},
},
settings: {
order: {
ApplePay: 0,
GooglePay: 1,
Card: 2,
MasterPass: 3,
Mobicred: 4,
OttVoucher: 5,
Ozow: 6,
Zapper: 7,
},
},
},
onError: (error) => console.error("PaymentMethods error", error),
});
// Alternative: mount a single payment method button instead of the selector above.
// Use this block in place of initPaymentMethods when you want to show only one method.
// adumoClient.initPaymentMethod({
// ref: document.getElementById("payment-method-container"),
// type: "Card",
// appearance: {
// settings: {
// hideDetailedInfo: true,
// hideSummary: true,
// },
// },
// onError: (error) => console.error("PaymentMethod error", error),
// });
</script>
Mount PaymentsElement together with either PaymentMethodsElement or PaymentMethodElement. PaymentMethodsElement renders the payment method selector, PaymentMethodElement renders a single payment method button, and PaymentsElement renders the active payment form.
Test Cards#
For test card numbers see the Test Cards reference.
Appearance#
Every SDK element accepts an optional appearance property that lets you match the payment UI to your brand. You can apply a built-in theme, override global design tokens (colours, fonts, spacing), target individual components with CSS overrides, and adjust behavioural settings — all without touching any internal markup.
For the full list of variables, style selectors, and settings see the Appearance Customization reference. To experiment with the options interactively, try the Storybook Playground — a live environment where you can adjust controls and copy the resulting configuration directly into your code.
Lifecycle#
The SDK lifecycle is mostly about DOM ownership: once you pass a container through ref, the SDK owns the contents of that container. Do not mutate or append children to mounted SDK containers.
Element Lifecycle Flow#
- Create one
AdumoClientwithwindow.adumo.initClient(). - Create or fetch a session token from your backend.
- Mount
PaymentsElementwithinitPayments(). - Mount either
PaymentMethodsElementor one or morePaymentMethodElementinstances. - Let the SDK control those containers until the transaction completes, is cancelled, or you replace the elements.
- For a new transaction, create a new session token and re-initialise the relevant elements.
Re-initialising Elements#
initClient()- resets the SDK client and unmounts all tracked SDK elements.initPayments()- unmounts any existingPaymentsElement. This also resets shared payment state, including the selected method, opened payment window, and loaded session configuration. If the transaction was previously intiated, the SDK will display a transaction status modal and poll for a status until it reaches a timeout or the backend responds with a result.initPaymentMethods()- replaces the selector only when called again with the sameref. A differentrefmounts another selector. This does not reset shared payment state.initPaymentMethod()- replaces the button only when called again with the same paymenttypeand sameref. A differentrefmounts another button for that type. This does not reset shared payment state.
API Reference#
Full schema documentation for all configuration objects and response types is available in the SDK API Reference .