SDK

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.

If you do not host your own frontend and/or prefer a simpler integration requiring minimal development, consider Virtual instead.

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, and PaymentMethods elements 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:

CredentialDescription
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 SecretUsed to sign session requests from your backend. Retrieve from the portal under E-Commerce → JWT and API Authentication → Reveal
Never expose your JWT Secret in client-side code. All session creation must happen server-side.

Virtual Template Configuration#

Currently the SDK is only supported for Virtual template v2 configured applications. You can enable this through merchant portal as follows:

  1. Tap/Hover E-commerce on the side menu and select Configure Hosted Payment Page
  2. Select an application from the Application dropdown
  3. Select Version 2 from the Template dropdown.
  4. 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:

EnvironmentURL
Staginghttps://staging-apiv3.adumoonline.com/product/payment/v1/initialisevirtual
Productionhttps://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#

  1. Create one AdumoClient with window.adumo.initClient().
  2. Create or fetch a session token from your backend.
  3. Mount PaymentsElement with initPayments().
  4. Mount either PaymentMethodsElement or one or more PaymentMethodElement instances.
  5. Let the SDK control those containers until the transaction completes, is cancelled, or you replace the elements.
  6. 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 existing PaymentsElement. 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 same ref. A different ref mounts another selector. This does not reset shared payment state.

  • initPaymentMethod() - replaces the button only when called again with the same payment type and same ref. A different ref mounts 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 .