Skip to content

Error Handling

This guide covers all error scenarios you may encounter when integrating the Bill Presentment SDK, with recommended recovery strategies.

Result Statuses

The SDK returns one of three statuses:

StatusMeaningAction Required
successUser selected bills and confirmedProcess payment with selectedBills data
cancelledUser dismissed the SDK without selectingNo action — allow user to re-open if needed
errorSomething went wrongShow error message, offer retry

Handling Cancellation

The user can dismiss the SDK at any time (close button, Escape key, back button on mobile). This is not an error — it resolves normally.

Web

typescript
const result = await sdk.open();

if (result.status === "cancelled") {
  // User chose not to select bills — no action needed
  // The SDK cleans up the iframe automatically
}

React Native

tsx
<BillPresentment
  sessionToken={token}
  visible={visible}
  onResult={(result) => {
    if (result.status === "cancelled") {
      // User dismissed — clean up state
      setVisible(false);
      setSessionToken(null);
    }
  }}
  onClose={() => setVisible(false)}
/>

INFO

On Android, the hardware back button triggers cancellation when the user is on the main bill list. If they're on a sub-screen (e.g., bill details), it navigates back within the SDK first.

Expired Session

Session tokens have a 15-minute sliding TTL and a 1-hour absolute maximum. If the session expires while the SDK is open, the SDK returns an error.

typescript
if (result.status === "error") {
  if (result.error.includes("expired") || result.error.includes("session")) {
    // Session expired — create a new one and retry
    const newToken = await createSession(icNumber);
    const retryResult = await openSdk(newToken);
  }
}

Prevention

  • Create the session token immediately before opening the SDK, not in advance
  • Don't cache session tokens — they are single-use and short-lived
  • If the user leaves the flow and comes back, always create a fresh session

WARNING

Session tokens cannot be refreshed or extended from the client side. If a session expires, you must create a new one from your backend via POST /v2/sdk/sessions.

Network Errors

Network failures can occur during SDK initialization (iframe loading) or during bill fetching.

SDK Fails to Load

If the hosted web app at bills.iimmpact.com cannot be reached:

typescript
if (result.status === "error") {
  // Could be network issue, CSP blocking, or ad blocker
  console.error("SDK error:", result.error);

  // Show user-friendly message
  showNotification("Unable to load bill service. Please check your connection and try again.");
}

Common causes:

  • No internet connectivity
  • Content-Security-Policy blocks frame-src https://bills.iimmpact.com
  • Ad blocker or browser extension blocking the iframe
  • Corporate firewall blocking the domain

Bill Fetching Fails

The SDK handles individual bill fetch failures internally — if a specific bill's outstanding amount fails to load, the SDK shows a retry button for that bill. Your app does not need to handle this.

Error Recovery Pattern

Here's a complete error handling pattern for both platforms:

Web

typescript
async function showBills(icNumber: string) {
  try {
    // 1. Create session from your backend
    const { session_token } = await fetch("/api/create-bill-session", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ic_number: icNumber }),
    }).then((res) => {
      if (!res.ok) throw new Error("Failed to create session");
      return res.json();
    });

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

    const result = await sdk.open();

    // 3. Handle result
    switch (result.status) {
      case "success":
        await processPayment(result.user, result.selectedBills, result.totalAmount);
        break;

      case "cancelled":
        // No action needed
        break;

      case "error":
        showNotification(`Something went wrong: ${result.error}. Please try again.`);
        break;
    }
  } catch (error) {
    // Session creation failed (network error, auth error, etc.)
    showNotification("Unable to connect. Please check your connection and try again.");
  }
}

React Native

tsx
const [error, setError] = useState<string | null>(null);

const openBills = useCallback(async () => {
  setError(null);

  try {
    const res = await fetch("https://your-api.com/create-bill-session", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ ic_number: icNumber }),
    });

    if (!res.ok) throw new Error("Failed to create session");

    const { session_token } = await res.json();
    setSessionToken(session_token);
    setVisible(true);
  } catch {
    setError("Unable to connect. Please try again.");
  }
}, [icNumber]);

const handleResult = useCallback((result: BillSelectionResult) => {
  setVisible(false);
  setSessionToken(null);

  switch (result.status) {
    case "success":
      navigation.navigate("Payment", {
        user: result.user,
        bills: result.selectedBills,
        total: result.totalAmount,
      });
      break;

    case "cancelled":
      break;

    case "error":
      Alert.alert("Error", result.error ?? "Something went wrong. Please try again.");
      break;
  }
}, [navigation]);

Troubleshooting Reference

SymptomLikely CauseFix
Blank iframe / white screenSession token expired or invalidCreate a fresh session token
SDK never opensCSP blocking frame-srcAdd https://bills.iimmpact.com to CSP
"Session expired" errorUser idle > 15 min or session > 1 hourCreate a new session and retry
Result not receivediframe sandbox or postMessage blockedCheck CSP and sandbox policies
onResult not called (React Native)Component unmounted or visible is falseEnsure component stays mounted during flow
CORS error in consoleUsually a browser extensionTest in incognito mode

IIMMPACT API Documentation