Mobile WebView
Overview
When integrating the Nimbbl Web checkout in mobile webviews, UPI apps need to be opened natively from the mobile app. This guide shows how to intercept UPI app URL schemes and handle redirects for Android, iOS, React Native, Flutter platforms.
We strongly recommend using platform-specific SDKs (Android SDK, iOS SDK, React Native SDK, Flutter SDK) for better performance, seamless UPI handling, and optimized user experience. If you still prefer to use the Nimbbl Web checkout in a mobile WebView, this guide will help you handle UPI intent redirects properly.
Platform Integration
- Android
- iOS
- React Native
- Flutter
Override shouldOverrideUrlLoading
Override the shouldOverrideUrlLoading method in your WebView to intercept UPI app URLs and handle the redirection.
- Java
- Kotlin
WebViewClient Implementation
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import java.util.List;
public class PaymentWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url != null) {
// Check if the URL is a UPI app scheme
String[] upiSchemes = {
"upi://pay", "amazonpay://pay", "credpay://pay",
"gpay://pay", "jupiter://pay", "kiwi://pay", "mobikwik://pay",
"navipay://pay", "popclubapp://pay", "paytmmp://pay",
"phonepe://pay", "super.money://pay", "whatsapp://pay",
"freecharge://pay"
};
boolean isUpiUrl = false;
for (String scheme : upiSchemes) {
if (url.startsWith(scheme)) {
isUpiUrl = true;
break;
}
}
if (isUpiUrl) {
// Create intent to open UPI app
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
// Check if the UPI app is installed
PackageManager pm = getPackageManager();
List<ResolveInfo> resInfo = pm.queryIntentActivities(intent, 0);
if (!resInfo.isEmpty()) {
// UPI app is installed, open it
startActivity(intent);
} else {
// Handle case when UPI app is not installed
// You can show a message to the user or redirect to app store
showMessage("UPI app not installed. Please install the app to continue.");
}
return true; // URL handled, don't load in WebView
}
}
return false; // Let WebView handle other URLs
}
}
Usage in Activity
import android.webkit.WebView;
import android.webkit.WebSettings;
public class PaymentActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_payment);
webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
// Set custom WebViewClient to handle UPI redirects
webView.setWebViewClient(new PaymentWebViewClient());
// Load Nimbbl checkout URL
String checkoutUrl = "https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN";
webView.loadUrl(checkoutUrl);
}
}
WebViewClient Implementation
import android.content.Intent
import android.net.Uri
import android.webkit.WebView
import android.webkit.WebViewClient
import android.content.pm.PackageManager
class PaymentWebViewClient(private val activity: Activity) : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
url?.let {
val upiSchemes = arrayOf(
"upi://pay", "amazonpay://pay", "credpay://pay",
"gpay://pay", "jupiter://pay", "kiwi://pay", "mobikwik://pay",
"navipay://pay", "popclubapp://pay", "paytmmp://pay",
"phonepe://pay", "super.money://pay", "whatsapp://pay",
"freecharge://pay"
)
val isUpiUrl = upiSchemes.any { scheme -> it.startsWith(scheme) }
if (isUpiUrl) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(it))
val pm = activity.packageManager
val resolveInfo = pm.queryIntentActivities(intent, 0)
if (resolveInfo.isNotEmpty()) {
activity.startActivity(intent)
} else {
// Handle no app scenario
// Show message or redirect to app store
}
return true
}
}
return false
}
}
Usage in Activity
import android.webkit.WebView
class PaymentActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_payment)
webView = findViewById(R.id.webview)
webView.settings.javaScriptEnabled = true
webView.webViewClient = PaymentWebViewClient(this)
val checkoutUrl = "https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN"
webView.loadUrl(checkoutUrl)
}
}
Implement WKNavigationDelegate
Implement the WKNavigationDelegate protocol to intercept UPI app URLs and handle the redirection.
import UIKit
import WebKit
class PaymentViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
// Configure WebView
let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.navigationDelegate = self
view = webView
// Load Nimbbl checkout URL
let checkoutURL = URL(string: "https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN")!
let request = URLRequest(url: checkoutURL)
webView.load(request)
}
// MARK: - WKNavigationDelegate
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else {
decisionHandler(.allow)
return
}
let urlString = url.absoluteString
// Check if the URL is a UPI app scheme
let upiSchemes = [
"upi://pay", "amazonpay://pay", "cred://pay",
"gpay://pay", "jupiter://pay", "kiwi://pay", "mobikwik://pay",
"navipay://pay", "popclubapp://pay", "paytm://pay",
"phonepe://pay", "supermoney://pay", "whatsapp://pay",
"freecharge://pay"
]
let isUpiUrl = upiSchemes.contains { scheme in
urlString.hasPrefix(scheme)
}
if isUpiUrl {
// Check if the UPI app can be opened
if UIApplication.shared.canOpenURL(url) {
// Open UPI app
UIApplication.shared.open(url, options: [:]) { success in
if !success {
// Handle failure
print("Failed to open UPI app")
}
}
decisionHandler(.cancel) // Don't load in WebView
} else {
// Handle case when UPI app is not installed
showAlert(message: "UPI app not installed. Please install the app to continue.")
decisionHandler(.cancel)
}
} else {
decisionHandler(.allow) // Let WebView handle other URLs
}
}
func showAlert(message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
Add URL Schemes to Info.plist
Add the UPI app URL schemes to your Info.plist file to allow your app to query and open these apps:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>upi</string>
<string>amazonpay</string>
<string>cred</string>
<string>gpay</string>
<string>jupiter</string>
<string>kiwi</string>
<string>mobikwik</string>
<string>navipay</string>
<string>popclubapp</string>
<string>paytm</string>
<string>phonepe</string>
<string>supermoney</string>
<string>whatsapp</string>
<string>freecharge</string>
</array>
Using react-native-webview
If you're using react-native-webview, override the onShouldStartLoadWithRequest prop to handle UPI redirects.
JavaScript Implementation
import React from 'react';
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import { Linking } from 'react-native';
const PaymentScreen = () => {
const handleShouldStartLoadWithRequest = (event) => {
const { url } = event;
// Check if the URL is a UPI app scheme
const upiSchemes = [
'upi://pay', 'amazonpay://pay', 'credpay://pay',
'gpay://pay', 'jupiter://pay', 'kiwi://pay', 'mobikwik://pay',
'navipay://pay', 'popclubapp://pay', 'paytmmp://pay',
'phonepe://pay', 'super.money://pay', 'whatsapp://pay',
'freecharge://pay'
];
const isUpiUrl = upiSchemes.some(scheme => url.startsWith(scheme));
if (isUpiUrl) {
// Check if the UPI app can be opened
Linking.canOpenURL(url).then(supported => {
if (supported) {
// Open UPI app
Linking.openURL(url);
} else {
// Handle case when UPI app is not installed
console.log("UPI app not installed");
// Show alert to user
}
});
return false; // Don't load in WebView
}
return true; // Let WebView handle other URLs
};
return (
<View style={{ flex: 1 }}>
<WebView
source={{ uri: 'https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN' }}
onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</View>
);
};
export default PaymentScreen;
TypeScript Implementation
import React from 'react';
import { View } from 'react-native';
import { WebView, WebViewNavigation } from 'react-native-webview';
import { Linking, Alert } from 'react-native';
const PaymentScreen: React.FC = () => {
const handleShouldStartLoadWithRequest = (event: WebViewNavigation): boolean => {
const { url } = event;
const upiSchemes = [
'upi://pay', 'amazonpay://pay', 'credpay://pay',
'gpay://pay', 'jupiter://pay', 'kiwi://pay', 'mobikwik://pay',
'navipay://pay', 'popclubapp://pay', 'paytmmp://pay',
'phonepe://pay', 'super.money://pay', 'whatsapp://pay',
'freecharge://pay'
];
const isUpiUrl = upiSchemes.some(scheme => url.startsWith(scheme));
if (isUpiUrl) {
Linking.canOpenURL(url)
.then((supported: boolean) => {
if (supported) {
Linking.openURL(url);
} else {
Alert.alert('Error', 'UPI app not installed. Please install the app to continue.');
}
})
.catch((err: Error) => {
console.error('Error opening UPI app:', err);
});
return false;
}
return true;
};
return (
<View style={{ flex: 1 }}>
<WebView
source={{ uri: 'https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN' }}
onShouldStartLoadWithRequest={handleShouldStartLoadWithRequest}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
</View>
);
};
export default PaymentScreen;
Using flutter_inappwebview
If you're using flutter_inappwebview, implement the shouldOverrideUrlLoading callback to handle UPI redirects.
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';
class PaymentScreen extends StatefulWidget {
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
InAppWebViewController? webViewController;
final List<String> _upiSchemes = [
'upi://pay', 'amazonpay://pay', 'credpay://pay',
'gpay://pay', 'jupiter://pay', 'kiwi://pay', 'mobikwik://pay',
'navipay://pay', 'popclubapp://pay', 'paytmmp://pay',
'phonepe://pay', 'super.money://pay', 'whatsapp://pay',
'freecharge://pay',
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Payment'),
),
body: InAppWebView(
initialUrlRequest: URLRequest(
url: WebUri('https://sonic.nimbbl.tech/?token=YOUR_ORDER_TOKEN'),
),
initialSettings: InAppWebViewSettings(
javaScriptEnabled: true,
),
shouldOverrideUrlLoading: (controller, navigationAction) async {
final uri = navigationAction.request.url;
final urlString = uri?.toString() ?? '';
// Check if the URL is a UPI app scheme
if (_upiSchemes.any((scheme) => urlString.startsWith(scheme))) {
// Open UPI app
await _launchURL(urlString);
return NavigationActionPolicy.CANCEL; // Don't load in WebView
}
return NavigationActionPolicy.ALLOW; // Let WebView handle other URLs
},
onWebViewCreated: (controller) {
webViewController = controller;
},
),
);
}
Future<void> _launchURL(String url) async {
final uri = Uri.parse(url);
if (await canLaunchUrl(uri)) {
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
// Handle case when UPI app is not installed
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('UPI app not installed. Please install the app to continue.'),
),
);
}
}
}
}
Add Dependencies
Add the required packages to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
flutter_inappwebview: ^6.0.0
url_launcher: ^6.2.0
Supported UPI App Schemes
The following UPI app URL schemes are supported for handling UPI intent redirects:
| UPI App | Android Base URL | iOS App URL |
|---|---|---|
| Amazon Pay | amazonpay://pay | amazonpay://pay |
| CRED | credpay://pay | cred://pay |
| Google Pay | gpay://pay | gpay://pay |
| Jupiter | jupiter://pay | jupiter://pay |
| Kiwi | kiwi://pay | kiwi://pay |
| MobiKwik | mobikwik://pay | mobikwik://pay |
| NaviPay | navipay://pay | navipay://pay |
| PopClub | popclubapp://pay | popclubapp://pay |
| Paytm | paytmmp://pay | paytm://pay |
| PhonePe | phonepe://pay | phonepe://pay |
| Super Money | super.money://pay | supermoney://pay |
| WhatsApp Pay | whatsapp://pay | whatsapp://pay |
| Freecharge | freecharge://pay | freecharge://pay |
| Generic UPI | upi://pay | upi://pay |
Best Practices
-
Error Handling: Always handle the case when a UPI app is not installed. Provide clear feedback to users and optionally redirect them to the app store.
-
User Experience: Show loading indicators while the UPI app is opening to provide better user feedback.
-
Testing: Test with multiple UPI apps to ensure all redirects work correctly.
-
Fallback: Consider implementing a fallback mechanism if the UPI app fails to open.
-
Security: Validate URLs before opening them to prevent security vulnerabilities.
Troubleshooting
UPI App Not Opening
- Ensure the URL scheme is correctly added to your app's configuration
- Check that the UPI app is installed on the device
- Verify that the URL format matches the expected scheme
WebView Not Loading Checkout
- Ensure JavaScript is enabled in your WebView
- Check that the checkout URL is correct and accessible
- Verify network permissions are granted
Payment Not Completing
- Ensure the WebView can navigate back to your app after payment
- Check that callback URLs are properly configured
- Verify that the order token is valid
- Always test UPI redirects on real devices, as simulators may not have UPI apps installed
- Ensure proper error handling for cases when UPI apps are not available
- Keep your WebView implementation updated to handle new UPI app schemes