Skip to content

Bill Presentment SDK Quickstart

This guide gets you from zero to a working bill selection flow as fast as possible.

Prerequisites

  1. B2B API credentials — Existing X-Api-Key + HMAC secret (same credentials used for other B2B API key endpoints)
  2. A backend server — Session creation is server-to-server only (secrets must never be in client code)
  3. A frontend app — Web (any framework) or React Native

DANGER

Never put API keys or HMAC secrets in frontend bundles, mobile apps, or source control.

Step 1: Create a Session (Backend)

When a user wants to view bills, your backend creates a session with POST /v2/sdk/sessions using API Key + HMAC headers.

bash
API_KEY="your_api_key"
HMAC_SECRET_BASE64="your_hmac_secret_base64"
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen | tr '[:upper:]' '[:lower:]')

BODY='{"ic_number":"901234567890"}'
BODY_HASH=$(printf "%s" "$BODY" | openssl dgst -sha256 -binary | base64)
CANONICAL="v1:${TIMESTAMP}:${NONCE}:POST::${BODY_HASH}"
SIGNATURE=$(printf "%s" "$CANONICAL" | openssl dgst -sha256 -mac HMAC \
  -macopt "hexkey:$(printf "%s" "$HMAC_SECRET_BASE64" | base64 -d | xxd -p -c 256)" \
  -binary | base64)

curl -X POST https://api.iimmpact.com/v2/sdk/sessions \
  -H "Content-Type: application/json" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "X-Timestamp: ${TIMESTAMP}" \
  -H "X-Nonce: ${NONCE}" \
  -H "X-Signature: v1=${SIGNATURE}" \
  -d "$BODY"

Response:

json
{
  "session_token": "bp_sess_7nF1oQ4n6vR2xZ9a",
  "expires_at": "2026-03-11T10:15:00Z"
}

Pass session_token to your frontend. See POST /v2/sdk/sessions for full request/response details.

INFO

session_token is the only SDK token. There is no bootstrap-token exchange step.

Step 2: Install the SDK (Frontend)

bash
npm install @iimmpact-sdn-bhd/billpresentment-sdk
bash
npm install @iimmpact-sdn-bhd/billpresentment-sdk-react-native react-native-webview

Step 3: Open the SDK

Use the session_token returned by your backend.

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

const sdk = new BillPresentmentSDK({
  sessionToken: sessionTokenFromBackend,
  displayMode: "modal", // "modal" | "fullscreen" | "inline"
});

const result = await sdk.open();
tsx
import { BillPresentment } from "@iimmpact-sdn-bhd/billpresentment-sdk-react-native";

<BillPresentment
  sessionToken={sessionTokenFromBackend}
  visible={showBills}
  displayMode="fullscreen"
  onResult={(result) => handleResult(result)}
  onClose={() => setShowBills(false)}
/>

Internally, the SDK opens bills.iimmpact.com/#token=..., the hosted app reads and clears the fragment, then uses Authorization: Bearer {session_token} for API calls.

Step 4: Handle the Result

When the user confirms bill selection, the SDK returns a structured result object:

typescript
if (result.status === "success") {
  console.log(result.user);          // { fullName, icNumber }
  console.log(result.selectedBills); // Array of { billerCode, billerName, ref1, ref2, amount }
  console.log(result.totalAmount);   // Sum of selected bill amounts

  navigateToPayment({
    user: result.user,
    bills: result.selectedBills,
    total: result.totalAmount,
  });
}

if (result.status === "cancelled") {
  // The SDK resolves with: { status: "cancelled" }
}

if (result.status === "error") {
  console.error("SDK error:", result.error);
}

Result structure:

typescript
interface User {
  fullName: string;
  icNumber: string;
}

interface SelectedBill {
  billerCode: string;
  billerName: string;
  ref1: string;
  ref2: string;
  amount: number;
}

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

Example result:

json
{
  "user": { "fullName": "AHMAD BIN ALI", "icNumber": "901234567890" },
  "status": "success",
  "totalAmount": 156.80,
  "selectedBills": [
    {
      "billerCode": "5454",
      "billerName": "Tenaga Nasional Berhad",
      "ref1": "220108271001",
      "ref2": "IIMMPACT",
      "amount": 143.50
    },
    {
      "billerCode": "8888",
      "billerName": "Telekom Malaysia",
      "ref1": "TM12345678",
      "ref2": "IIMMPACT",
      "amount": 13.30
    }
  ]
}

Complete Example (Web)

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

async function showBills(sessionToken: string) {
  const sdk = new BillPresentmentSDK({
    sessionToken,
    displayMode: "modal",
  });

  const result = await sdk.open();

  if (result.status === "success") {
    sessionStorage.setItem("user", JSON.stringify(result.user));
    sessionStorage.setItem("selectedBills", JSON.stringify(result.selectedBills));
    sessionStorage.setItem("totalAmount", String(result.totalAmount));
    window.location.href = "/pay";
    return;
  }

  if (result.status === "cancelled") {
    return;
  }

  console.error("Failed to collect bill selection:", result.error);
}

showBills(sessionTokenFromBackend);

Backend Example (Node.js)

typescript
import { createHash, createHmac, randomUUID } from "node:crypto";

const API_BASE_URL = "https://api.iimmpact.com";

type CreateSessionInput = {
  ic_number: string;
};

function sha256Base64(value: string): string {
  return createHash("sha256").update(value).digest("base64");
}

export async function createSdkSession(input: CreateSessionInput) {
  const apiKey = process.env.IIMMPACT_API_KEY;
  const hmacSecretBase64 = process.env.IIMMPACT_HMAC_SECRET_BASE64;

  if (!apiKey || !hmacSecretBase64) {
    throw new Error("Missing IIMMPACT API credentials");
  }

  const method = "POST";
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce = randomUUID().toLowerCase();
  const query = "";
  const body = JSON.stringify(input);
  const bodyHash = sha256Base64(body);
  const canonical = `v1:${timestamp}:${nonce}:${method}:${query}:${bodyHash}`;
  const hmacKey = Buffer.from(hmacSecretBase64, "base64");
  const signature = createHmac("sha256", hmacKey).update(canonical).digest("base64");

  const response = await fetch(`${API_BASE_URL}/v2/sdk/sessions`, {
    method,
    headers: {
      "Content-Type": "application/json",
      "X-Api-Key": apiKey,
      "X-Timestamp": timestamp,
      "X-Nonce": nonce,
      "X-Signature": `v1=${signature}`,
    },
    body,
  });

  if (!response.ok) {
    throw new Error(`Session creation failed (${response.status})`);
  }

  return (await response.json()) as {
    session_token: string;
    expires_at: string;
  };
}

Next Steps

  • Authentication — Full auth flow, token lifecycle, and bill set validation
  • Web SDK — Full configuration options and display modes
  • React Native SDK — Component props and platform-specific handling

IIMMPACT API Documentation