Appearance
React Native 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-react-native package provides a React Native component that opens the bill selection UI in a WebView. It handles session authentication, WebView lifecycle, and postMessage communication. Outstanding amounts load progressively, and failed lookups show a retry action per bill.
Installation
bash
npm install @iimmpact/billpresentment-sdk-react-native react-native-webviewreact-native-webview is a peer dependency. If you're using Expo:
bash
npx expo install react-native-webviewBasic Usage
tsx
import { useNavigation } from "@react-navigation/native";
import { useState } from "react";
import { Button, View } from "react-native";
import { BillPresentment } from "@iimmpact/billpresentment-sdk-react-native";
function BillScreen() {
const navigation = useNavigation();
const [visible, setVisible] = useState(false);
const [sessionToken, setSessionToken] = useState<string | null>(null);
async function openBills() {
// Get session token from your backend
const res = await fetch("https://your-api.com/create-bill-session", {
method: "POST",
});
const { session_token } = await res.json();
setSessionToken(session_token);
setVisible(true);
}
return (
<View style={{ flex: 1 }}>
<Button title="View Bills" onPress={openBills} />
{sessionToken && (
<BillPresentment
sessionToken={sessionToken}
visible={visible}
displayMode="fullscreen"
onResult={(result) => {
if (result.status === "success") {
navigation.navigate("Payment", {
bills: result.selectedBills,
total: result.totalAmount,
});
}
}}
onClose={() => {
setVisible(false);
setSessionToken(null);
}}
/>
)}
</View>
);
}Component Props
| Prop | Type | Default | Description |
|---|---|---|---|
sessionToken | string | required | Session token (bp_sess_xxxx) from POST /v2/sdk/sessions |
visible | boolean | required | Controls whether the WebView is shown |
displayMode | "fullscreen" | "modal" | "fullscreen" | Presentation style |
onResult | (result: BillSelectionResult) => void | required | Called when user confirms selection, cancels, or an error occurs |
onClose | () => void | — | Called after the WebView is dismissed (cleanup). Also fires after cancel/error via onResult. |
Display Modes
Fullscreen
Covers the entire screen. Recommended for most mobile flows.
tsx
<BillPresentment
sessionToken="bp_sess_xxxx"
visible={visible}
displayMode="fullscreen"
onResult={handleResult}
onClose={() => setVisible(false)}
/>Modal
Presents as a bottom sheet or overlay (platform-dependent).
tsx
<BillPresentment
sessionToken="bp_sess_xxxx"
visible={visible}
displayMode="modal"
onResult={handleResult}
onClose={() => setVisible(false)}
/>Result Handling
The onResult callback receives a BillSelectionResult:
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;
}tsx
function handleResult(result: BillSelectionResult) {
setVisible(false);
switch (result.status) {
case "success":
navigation.navigate("Payment", {
bills: result.selectedBills,
total: result.totalAmount,
});
break;
case "cancelled":
// User dismissed -- no action needed
break;
case "error":
Alert.alert("Error", result.error ?? "Something went wrong");
break;
}
}Cancellation
When the user dismisses the SDK (back button, swipe gesture, or close button):
onResultfires with{ status: "cancelled" }onClosefires after the WebView is dismissed
Handle cancellation in onResult. Use onClose for cleanup (resetting state, etc.).
Android Back Button
The SDK handles the Android hardware back button automatically. When pressed:
- If the user is on a sub-screen (e.g., bill details), it navigates back within the WebView
- If the user is on the main bill list, it triggers cancellation (see above)
No additional configuration is needed.
Complete Example
tsx
import { useNavigation } from "@react-navigation/native";
import { useCallback, useState } from "react";
import {
Alert,
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import {
BillPresentment,
type BillSelectionResult,
} from "@iimmpact/billpresentment-sdk-react-native";
export function HomeScreen() {
const navigation = useNavigation();
const [visible, setVisible] = useState(false);
const [sessionToken, setSessionToken] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const openBills = useCallback(async () => {
setLoading(true);
try {
const res = await fetch("https://your-api.com/create-bill-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
});
const { session_token } = await res.json();
setSessionToken(session_token);
setVisible(true);
} catch {
Alert.alert("Error", "Failed to create bill session");
} finally {
setLoading(false);
}
}, []);
const handleResult = useCallback(
(result: BillSelectionResult) => {
setVisible(false);
setSessionToken(null);
if (result.status === "success") {
navigation.navigate("Payment", {
bills: result.selectedBills,
total: result.totalAmount,
});
}
if (result.status === "error") {
Alert.alert("Error", result.error ?? "Something went wrong");
}
},
[navigation]
);
return (
<SafeAreaView style={styles.container}>
<View style={styles.content}>
<Text style={styles.title}>My Bills</Text>
<TouchableOpacity
style={styles.button}
onPress={openBills}
disabled={loading}
>
<Text style={styles.buttonText}>
{loading ? "Loading..." : "View & Pay Bills"}
</Text>
</TouchableOpacity>
</View>
{sessionToken && (
<BillPresentment
sessionToken={sessionToken}
visible={visible}
displayMode="fullscreen"
onResult={handleResult}
onClose={() => {
setVisible(false);
setSessionToken(null);
}}
/>
)}
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "#fff" },
content: { flex: 1, justifyContent: "center", alignItems: "center" },
title: { fontSize: 24, fontWeight: "bold", marginBottom: 20 },
button: {
backgroundColor: "#3451b2",
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: { color: "#fff", fontSize: 16, fontWeight: "600" },
});Platform Notes
iOS
- The WebView uses
WKWebViewunder the hood - Ensure
App Transport Securityallowsbills.iimmpact.com(HTTPS, no additional config needed)
Android
- The WebView uses Chromium-based
WebView - Hardware back button is handled automatically
- For devices with gesture navigation, the swipe-back gesture triggers the same behavior as the back button
Troubleshooting
WebView shows blank screen
- Verify the session token is valid and not expired
- Check that
react-native-webviewis correctly linked (runnpx pod-installfor iOS) - Ensure the device has internet connectivity
"Session expired" error in WebView
- Session tokens use a 15-minute sliding TTL. Each successful API call extends the expiry window.
- Sessions have a 1-hour absolute lifetime cap. If the user returns after long inactivity, create a new session.
onResult not called
- Ensure
visibleistruewhen the component is rendered - Check that
sessionTokenis notnullorundefined - Verify the component is not unmounted before the user completes the flow
