Skip to content

Web SDK

Coming Soon

This feature is under active development and not yet available. The API endpoints, SDK packages, and behavior described here are subject to change before release.

The @iimmpact/billpresentment-sdk package provides a JavaScript/TypeScript wrapper that opens the bill selection UI in an iframe. It handles session authentication, iframe lifecycle, and postMessage communication. Outstanding amounts load progressively in the hosted UI, and failed lookups show a retry action per bill.

Installation

bash
npm install @iimmpact/billpresentment-sdk

Basic Usage

typescript
import { BillPresentmentSDK } from "@iimmpact/billpresentment-sdk";

const sdk = new BillPresentmentSDK({
  sessionToken: "bp_sess_xxxx",
  displayMode: "modal",
});

const result = await sdk.open();

Configuration

OptionTypeDefaultDescription
sessionTokenstringrequiredSession token from POST /v2/sdk/sessions
displayMode"modal" | "fullscreen" | "inline""modal"How the UI is presented
containerHTMLElementTarget element for inline mode

Display Modes

Opens the bill UI as a centered overlay with a backdrop. The user can close it by clicking outside or pressing Escape.

typescript
const sdk = new BillPresentmentSDK({
  sessionToken,
  displayMode: "modal",
});

const result = await sdk.open();
// Promise resolves when user confirms or cancels

Best for: trigger buttons ("View Bills"), secondary flows.

Fullscreen

Opens the bill UI as a full-viewport overlay. Covers the entire screen.

typescript
const sdk = new BillPresentmentSDK({
  sessionToken,
  displayMode: "fullscreen",
});

const result = await sdk.open();

Best for: mobile web, dedicated bill management pages.

Inline

Embeds the bill UI inside a specific container element. Does not create any overlay.

typescript
const sdk = new BillPresentmentSDK({
  sessionToken,
  displayMode: "inline",
  container: document.getElementById("bill-container")!,
});

const result = await sdk.open();

Best for: embedding within an existing page layout.

WARNING

For inline mode, the container element must be in the DOM before calling sdk.open(). The iframe will fill the container's dimensions -- set appropriate width and height on the container.

Result Handling

sdk.open() returns a Promise<BillSelectionResult> that resolves when:

  • The user confirms their bill selection (status: "success")
  • The user closes the SDK without selecting (status: "cancelled", resolves as { status: "cancelled" })
  • An error occurs (status: "error")
typescript
const result = await sdk.open();

switch (result.status) {
  case "success":
    // User selected bills
    console.log(result.selectedBills); // Array of selected bills
    console.log(result.totalAmount);   // Sum of outstanding amounts
    break;

  case "cancelled":
    // User dismissed without selecting
    break;

  case "error":
    // Something went wrong
    console.error(result.error);
    break;
}

Result Types

typescript
type BillSelectionResult =
  | { status: "success"; selectedBills: SelectedBill[]; totalAmount: number }
  | { status: "cancelled" }
  | { status: "error"; error: string };

interface SelectedBill {
  accountNumber: string;
  billerCode: string;
  billerName: string;
  billerCategory: string;
  outstandingAmount: number;
  dueDate?: string;
  jompayBillerCode?: string;
  jompayRef2?: string;
}

Event Listeners

The SDK is Promise-first (await sdk.open()), and also supports optional event listeners for lifecycle hooks.

typescript
sdk.on("loaded", () => {
  /* WebView loaded */
});

sdk.on("error", (error) => {
  /* Error occurred */
});

sdk.on("close", () => {
  /* User closed */
});

Complete Example

html
<!DOCTYPE html>
<html>
<body>
  <button id="view-bills">View My Bills</button>
  <div id="result"></div>

  <script type="module">
    import { BillPresentmentSDK } from "@iimmpact/billpresentment-sdk";

    document.getElementById("view-bills").addEventListener("click", async () => {
      // 1. Get session token from your backend
      const res = await fetch("/api/create-bill-session", { method: "POST" });
      const { session_token } = await res.json();

      // 2. Open the SDK
      const sdk = new BillPresentmentSDK({
        sessionToken: session_token,
        displayMode: "modal",
      });

      const result = await sdk.open();

      // 3. Handle the result
      if (result.status === "success") {
        document.getElementById("result").textContent =
          `Selected ${result.selectedBills.length} bills, total: RM${result.totalAmount.toFixed(2)}`;

        // Store selection and navigate to payment
        sessionStorage.setItem("selectedBills", JSON.stringify(result.selectedBills));
        sessionStorage.setItem("totalAmount", String(result.totalAmount));
        window.location.href = "/payment";
      }

      if (result.status === "cancelled") {
        // User dismissed without selecting
      }

      if (result.status === "error") {
        console.error("SDK error:", result.error);
      }
    });
  </script>
</body>
</html>

Framework Examples

React

tsx
import { BillPresentmentSDK } from "@iimmpact/billpresentment-sdk";
import { useCallback } from "react";

function BillButton({ sessionToken }: { sessionToken: string }) {
  const handleClick = useCallback(async () => {
    const sdk = new BillPresentmentSDK({
      sessionToken,
      displayMode: "modal",
    });

    const result = await sdk.open();
    if (result.status === "success") {
      // Navigate to payment
    }
  }, [sessionToken]);

  return <button onClick={handleClick}>View Bills</button>;
}

Vue

vue
<script setup lang="ts">
import { BillPresentmentSDK } from "@iimmpact/billpresentment-sdk";

const props = defineProps<{ sessionToken: string }>();

async function openBills() {
  const sdk = new BillPresentmentSDK({
    sessionToken: props.sessionToken,
    displayMode: "modal",
  });

  const result = await sdk.open();
  if (result.status === "success") {
    // Navigate to payment
  }
}
</script>

<template>
  <button @click="openBills">View Bills</button>
</template>

Troubleshooting

SDK fails to load / blank iframe

  • Verify the session token is valid and not expired
  • Check browser console for CORS errors
  • Ensure bills.iimmpact.com is not blocked by CSP or ad blockers

"Session expired" error

  • Session tokens use a 15-minute sliding TTL. Each successful API call extends the expiry window.
  • Sessions have a 1-hour absolute lifetime cap. Create a new session token if the user returns after long inactivity.

postMessage not received

  • Ensure the SDK is not blocked by an iframe sandbox policy
  • Check that your page's Content-Security-Policy allows frame-src https://bills.iimmpact.com
  • For modal and fullscreen modes, the SDK cleans up the iframe when the Promise resolves
  • If using a SPA router, ensure the SDK is not unmounted before the Promise resolves

IIMMPACT API Documentation