/

to search

Introducing Setu Changelog Check it out ↗

#Cross platform implementation

In case you are using a cross platform solution—like Flutter, React Native, Ionic—for your Android and iOS apps, you can refer to the code snippets provided below. The integration process remains the same, but changes as per the language you use.

The code snippets provided below deal specifically with how the web view is loaded and dismissed within your Android/iOS app. Usage of the Create Link API will remain unaffected.


#Flutter

Switch control from your app to Setu

The link returned by the Create link API should be loaded within a webview. Our sample below uses the flutter_inappwebview plugin for webviews. Feel free to use a plugin as per your requirements.

The webview controller will need to implement one JavaScript handler -

  • unload - Used by the parent app to dismiss the webview

Special use case: This unload function can also be used for dismissing the webview and redirecting a user back to your native app once a bill payment journey is completed (i.e. payment is successful) via a CTA from the Setu webview. Please let our team know if you would like to enable this use case for your app.


Make sure you are handling these functionalities in your webview for a seamless user experience:

  • If you are integrating with our default UPI payment option, the webview needs to handle UPI app specific intents using a shouldOverrideUrlLoading (or a similar function as per your webview plugin). This function is used to handle UPI app specific schemes and open the UPI app.

  • For downloading and saving transaction receipts from the webview, the webview will need to handle a onDownloadStartRequest (or a similar function as per your webview plugin). Our sample below uses the flutter_downloader and path_provider plugins for downloading and saving transaction receipts. Please follow the plugin specific documentation for implementation. Make sure you have added necessary permissions in your AndroidManifest.xml and Info.plist files for downloading and saving files.

import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize(
debug: true, // optional: set false to disable printing logs to console
);
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: RpWebPage(
link: 'https://billpay.setu.co/1234',
), // Replace with the actual URL
);
}
}
class RpWebPage extends StatefulWidget {
const RpWebPage({super.key, required this.link});
final String link;
@override
State<RpWebPage> createState() => _RpWebPageState();
}
class _RpWebPageState extends State<RpWebPage> {
late InAppWebViewController webView;
bool isButtonClicked = false;
@override
void initState() {
super.initState();
}
void _loadUrl() {
setState(() {
isButtonClicked = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Bill Payments'),
),
body: Column(
children: [
if (!isButtonClicked)
ElevatedButton(
onPressed: _loadUrl,
child: Text('Load webview'),
),
Expanded(
child: isButtonClicked
? InAppWebView(
initialSettings: InAppWebViewSettings(
allowFileAccess: true,
allowFileAccessFromFileURLs: true
),
initialUrlRequest: URLRequest(url: WebUri.uri(Uri.parse(widget.link))),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStop: (controller, url) async {
controller.addJavaScriptHandler(
handlerName: 'unload',
callback: (args) async {
// logic to close webview and return to app
},
);
},
// Handle opening UPI apps from UPI app specific intents
shouldOverrideUrlLoading: (controller, navigationAction) async {
final uri = navigationAction.request.url!;
if (["phonepe", "upi"].contains(uri.scheme) ||
uri.toString().contains("upi")) {
if (await canLaunchUrl(uri)) {
// Launch the App
await launchUrl(
uri,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
// Handle downloading the receipt
onDownloadStartRequest: (controller, request) async {
print("onDownloadStart $request");
final dir = Platform.isAndroid
? (await getExternalStorageDirectory())?.path
: (await getApplicationDocumentsDirectory()).uri.path;
print("saving in $dir");
final taskId = await FlutterDownloader.enqueue(
url: request.url.toString(),
savedDir: dir!,
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
saveInPublicStorage: true,
allowCellular: true
);
// Open the downloaded file
await FlutterDownloader.open(taskId: taskId!);
},
)
: Container(),
),
],
),
);
}
}

#React Native

Below are the steps to embed the pre-built screens into React Native app,

  • Implement a web view. We recommend to use the WebView component from react-native-webview library. You’re free to choose any other library for implementing this.

  • Pass the webview URL into the uri parameter on the WebView component. Ideally, this URL can be sent as a parameter from the previous screen to the WebView screen.

  • WebView component exposes a function onNavigationStateChange which is called every time there is a change in the navigation of web view.

import React, { useRef, useEffect } from "react";
import { SafeAreaView, ActivityIndicator, StyleSheet } from "react-native";
import WebView from "react-native-webview"; // Load WebView from "react-native-webview"
export default function WebView({ navigation }) {
const webviewRef = useRef(null);
// Check if webview URI contain .setu.co
useEffect(() => {
if(!webViewUri.includes('.setu.co')){
console.log('Blocking navigation');
navigation.navigate("Start");
}
});
// Function to check the navigation state inside webview
const onNavigation = (navState) => {
let url = navState.url;
};
return (
<SafeAreaView style={styles.flexContainer}>
<WebView
source={{ uri: 'https://billpay.setu.co/1234' }} // Replace with the actual URL
startInLoadingState={true}
renderLoading={() => (
<ActivityIndicator color="black" size="large" style={styles.flexContainer} />
)}
ref={webviewRef}
onNavigationStateChange={onNavigation}
style={styles.margin}
// Whitelist UPI app specific schemes for handling app specific intents
originWhitelist={['*.setu.co','*://upi/*','phonepe://*']}
mixedContentMode="always"
allowFileAccess
allowFileAccessFromFileURLs
allowUniversalAccessFromFileURLs
javaScriptEnabled
nestedScrollEnabled // optional: in case you are loading WebView inside a ScrollView
scrollEnabled
onFileDownload={(event) => {
// Handle downloading the receipt (iOS only)
}}
/>
</SafeAreaView>
);
}
// Styles
const styles = StyleSheet.create({
flexContainer: {
flex: 1,
},
margin: {
marginTop: 50,
},
});

Was this page helpful?