Android
Use Android WebView when you want to launch the Jodo-hosted flow inside your native Android app.
- Fetch the Jodo
redirect_urlfrom your backend. - Load the URL in a WebView.
- Enable JavaScript, DOM storage, and multiple windows.
- Handle UPI deep links using Android intents.
- Detect your
callback_urland return the user to your app flow. - Confirm final status from your backend.
Get the Redirect URL
Section titled “Get the Redirect URL”The app should not call Jodo directly with server credentials. Call your ERP or backend API, and let that backend create the Jodo flow and return the redirect_url.
Include a callback_url that your WebView can identify after the payment flow completes.
WebView Requirements
Section titled “WebView Requirements”Handle these cases explicitly:
- UPI intent: When a user selects a UPI app, Android should open the UPI intent instead of trying to load
upi://inside the WebView. - Bank authentication: Net banking and card flows may open popups or new windows.
- Callback redirect: Detect your
callback_urland close checkout or show a status screen. - Back navigation: Decide whether back should navigate WebView history, close a child WebView, or exit checkout.
Sample Activity
Section titled “Sample Activity”package com.example.jodocheckout;
import android.content.ActivityNotFoundException;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.os.Message;import android.view.View;import android.webkit.WebChromeClient;import android.webkit.WebView;import android.webkit.WebViewClient;import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity { private static final String CALLBACK_URL = "https://example.com/jodo/callback";
private WebView parentWebView; private WebView childWebView;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
parentWebView = findViewById(R.id.parentWebView); childWebView = findViewById(R.id.childWebView);
configureWebView(parentWebView); configureWebView(childWebView);
String redirectUrl = "<redirect_url_from_your_backend>"; parentWebView.loadUrl(redirectUrl); parentWebView.setWebChromeClient(new CheckoutChromeClient(childWebView)); }
private void configureWebView(WebView webView) { webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); webView.getSettings().setSupportMultipleWindows(true); webView.getSettings().setDomStorageEnabled(true);
webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(CALLBACK_URL)) { handleCheckoutComplete(url); return true; }
if (url.startsWith("http://") || url.startsWith("https://")) { return false; }
if (url.startsWith("upi://pay")) { openIntent(url); return true; }
openIntent(url); return true; } }); }
private void openIntent(String url) { try { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); } catch (ActivityNotFoundException error) { Toast.makeText(this, "No app found to complete this action.", Toast.LENGTH_SHORT).show(); } }
private void handleCheckoutComplete(String callbackUrl) { // Close checkout and ask your backend for final status. }}
class CheckoutChromeClient extends WebChromeClient { private final WebView childWebView;
CheckoutChromeClient(WebView childWebView) { this.childWebView = childWebView; }
@Override public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; transport.setWebView(childWebView); resultMsg.sendToTarget(); childWebView.setWebChromeClient(new CheckoutChromeClient(childWebView)); childWebView.setVisibility(View.VISIBLE); return true; }
@Override public void onCloseWindow(WebView window) { childWebView.setVisibility(View.INVISIBLE); }}Android Manifest
Section titled “Android Manifest”Add internet permission. If your app handles deep links or UPI return flows, configure the relevant intent filters for your application.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET" />
<application> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>Test Cases
Section titled “Test Cases”- UPI app selection and return to checkout
- Net banking popup creation and closure
- Card authentication redirects
- Callback URL detection
- User closes checkout before completion
- Backend status remains pending after callback