Appearance
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-sdkBasic Usage
typescript
import { BillPresentmentSDK } from "@iimmpact/billpresentment-sdk";
const sdk = new BillPresentmentSDK({
sessionToken: "bp_sess_xxxx",
displayMode: "modal",
});
const result = await sdk.open();Configuration
| Option | Type | Default | Description |
|---|---|---|---|
sessionToken | string | required | Session token from POST /v2/sdk/sessions |
displayMode | "modal" | "fullscreen" | "inline" | "modal" | How the UI is presented |
container | HTMLElement | — | Target element for inline mode |
Display Modes
Modal
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 cancelsBest 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.comis 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-Policyallowsframe-src https://bills.iimmpact.com
Modal not closing
- For
modalandfullscreenmodes, 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
