Appearance
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:
| Status | Meaning | Action Required |
|---|---|---|
success | User selected bills and confirmed | Process payment with selectedBills data |
cancelled | User dismissed the SDK without selecting | No action — allow user to re-open if needed |
error | Something went wrong | Show 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-Policyblocksframe-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
| Symptom | Likely Cause | Fix |
|---|---|---|
| Blank iframe / white screen | Session token expired or invalid | Create a fresh session token |
| SDK never opens | CSP blocking frame-src | Add https://bills.iimmpact.com to CSP |
| "Session expired" error | User idle > 15 min or session > 1 hour | Create a new session and retry |
| Result not received | iframe sandbox or postMessage blocked | Check CSP and sandbox policies |
onResult not called (React Native) | Component unmounted or visible is false | Ensure component stays mounted during flow |
| CORS error in console | Usually a browser extension | Test in incognito mode |
