Skip to content

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-webview

react-native-webview is a peer dependency. If you're using Expo:

bash
npx expo install react-native-webview

Basic 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

PropTypeDefaultDescription
sessionTokenstringrequiredSession token (bp_sess_xxxx) from POST /v2/sdk/sessions
visiblebooleanrequiredControls whether the WebView is shown
displayMode"fullscreen" | "modal""fullscreen"Presentation style
onResult(result: BillSelectionResult) => voidrequiredCalled when user confirms selection, cancels, or an error occurs
onClose() => voidCalled 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)}
/>

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):

  1. onResult fires with { status: "cancelled" }
  2. onClose fires 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:

  1. If the user is on a sub-screen (e.g., bill details), it navigates back within the WebView
  2. 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 WKWebView under the hood
  • Ensure App Transport Security allows bills.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-webview is correctly linked (run npx pod-install for 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 visible is true when the component is rendered
  • Check that sessionToken is not null or undefined
  • Verify the component is not unmounted before the user completes the flow

IIMMPACT API Documentation