search

UPI Smart Intent for Android Platform

UPI Intent flow offers flexibility to the users to make payments from any of the UPI PSP Apps available on their mobile device. Despite giving an optimized payment experience to the users, there are some challenges associated with it. The UPI intent has a low success rate due to non-visibility of UPI payment ready apps and the solution to this problem is UPI Smart Intent flow.

 

UPI Smart Intent logic optimises the customer experience on the merchant checkout page by displaying the apps which are UPI payment ready (Apps from which user can make a payment at that point of time). We will try to understand the definition by understanding the following cases:

  • Case 1: User has Paytm and BHIM UPI App install on their mobile device. Both of these apps are UPI payment ready as the user has successfully logged into these apps and UPI onboarding is also completed). In this case with UPI Smart Intent, both Paytm and BHIM UPI Apps will be available on the checkout page to make payment.
  • Case 2: User is UPI payment ready only on Paytm at this point in time. Either BHIM UPI is not there in their phone or their BHIM UPI App account is not UPI payment ready (either BHIM UPI account is not logged in or UPI onboarding is not completed on BHIM UPI App). In this case with UPI Smart Intent, only Paytm will be available on the check out page to make payment. 

Benefits of UPI Smart Intent

  1. Higher Success Rates
  2. Visibility of UPI payment ready Apps 

Smart Intent workflow and its limitation

Smart Intent only works on the Android platform. Whenever a user opens the Paytm or any other PSP App, PSP App sends the latest information to the OS whether this account is UPI payment ready or not. If the user is UPI payment ready, OS will store this information. Mercahnt App call the OS service to know the UPI ready installed apps and display only UPI payment ready Apps on the payment page.

Impact of implementing Smart Intent

Merchant 1 and Merchant 2 both operate in a food delivery business. We can assume that these two platforms have similar segments of user. The only difference between these two platforms are as follows:

  • Merchant 1 Implemented normal intent. It displays all UPI Apps such as Paytm, BHIM UPI, PhonePe, Google Pay, WhatsApp, etc. to all users on the checkout page irrespective of whether the user is UPI payment ready on that platform or not. 
  • Merchant 2 implemented Smart Intent. It displays only the UPI payment ready Apps to a user.

if we compare the data for both of the merchants then we found that Merchant 1 can improve the Success Rate by ~15% on implementing the UPI Smart Intent Flow.

How to implement Smart Intent on any merchant's checkout page?

The implementation of smart intent is very easy. A merchant needs to incorporate the following codes on their checkout page:

class MainActivity : AppCompatActivity() {
    private val REQUEST_CODE = 123
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        /*1.1 Defining the variables with sample UPI Apps package names. The merchant can add more UPI apps with their package names according to their requirement */
        val BHIM_UPI = "in.org.npci.upiapp"
        val GOOGLE_PAY = "com.google.android.apps.nbu.paisa.user"
        val PHONE_PE = "com.phonepe.app"
        val PAYTM = "net.one97.paytm"
        
        /*1.2 Combining the UPI app package name variables in a list */
        val upiApps = listOf<String>(PAYTM, GOOGLE_PAY, PHONE_PE, BHIM_UPI)
 
        /*2.1 Defining button elements for generic UPI OS intent and specific UPI Apps */
        var upiButton = findViewById(R.id.upi) as Button
        var paytmButton = findViewById(R.id.paytm) as Button
        var gpayButton = findViewById(R.id.gpay) as Button
        var phonepeButton = findViewById(R.id.phonepe) as Button
        var bhimButton = findViewById(R.id.bhim) as Button
        
        /*2.2 Combining button elements of specific UPI Apps in a list in the same order as the above upiApps list of UPI app package names */
        val upiAppButtons = listOf<Button>(paytmButton, gpayButton, phonepeButton, bhimButton)
 
        /*3. Defining a UPI intent with a Paytm merchant UPI spec deeplink */
        val uri = "upi://pay?pa=paytmqr2810050501011ooqggb29a01@paytm&pn=Paytm%20Merchant&mc=5499&mode=02&orgid=000000&paytmqr=2810050501011OOQGGB29A01&am=11&sign=MEYCIQDq96qhUnqvyLsdgxtfdZ11SQP//6F7f7VGJ0qr//lF/gIhAPgTMsopbn4Y9DiE7AwkQEPPnb2Obx5Fcr0HJghd4gzo"
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(uri))
        intent.data = Uri.parse(uri)
 
        /*4.1 Defining an on click action for the UPI generic OS intent chooser. This is just for reference, not needed in case of UPI Smart Intent.
            - This will display a list of all apps available to respond to the UPI intent
            in a chooser tray by the Android OS */
        upiButton.setOnClickListener{
            val chooser = Intent.createChooser(intent, "Pay with...")
            startActivityForResult(chooser, REQUEST_CODE)
        }
 
        /*4.2 Defining an on click action for the UPI intent to be carried out by specific apps
            - Clicking on the respective buttons will invoke those specific UPI apps (whenever available)
            - The buttons for specific UPI apps will be displayed when following conditions are met:
                1. App is installed
                2. App is in the list of apps ready to respond to a UPI intent
                    -> This is how the SMART INTENT will work
                    -> The button will only be visible when the app has a UPI ready user
 
            This will also log the results of the above two check in debug logs */
        for(i in upiApps.indices){
            val b = upiAppButtons[i]
            val p = upiApps[i]
            Log.d("UpiAppVisibility", p + " | " + isAppInstalled(p).toString() + " | " + isAppUpiReady(p))
            if(isAppInstalled(p)&&isAppUpiReady(p)) {
                b.visibility = View.VISIBLE
                b.setOnClickListener{
                    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(uri))
                    intent.data = Uri.parse(uri)
                    intent.setPackage(p)
                    startActivityForResult(intent, REQUEST_CODE)
                }
            }
            else{
                b.visibility = View.INVISIBLE
            }
        }
    }
 
    /*This function is to log the returned results of the transaction.
        - One can replace this with the standard UPI intent result handler code. */
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE) {
            // Process based on the data in response.
            Log.d("result", data.toString())
            data?.getStringExtra("Status")?.let { Log.d("result", it) };
            data?.getStringExtra("Status")?.let { Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() };
        }
    }

        /*
	    This function checks if the app with this package name is installed on the phone
	*/
	fun isAppInstalled(packageName: String): Boolean {
	    val pm = getPackageManager()
	    try {
	        pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
	        return true;
	    } catch (e: PackageManager.NameNotFoundException) {
	        e.printStackTrace()
	    }
	    return false;
	}
	/*
	    This function checks if the app with this package name is responding to UPI intent
	    - i.e. the app has a ready UPI user (as per the NPCI recommended implementation)
	    - Circular: https://www.npci.org.in/sites/default/files/circular/Circular-73-Payer_App_behaviour_for_Intent_based_transaction_on_UPI.pdf
	*/
	fun isAppUpiReady(packageName: String): Boolean {
	    var appUpiReady = false
	    val upiIntent = Intent(Intent.ACTION_VIEW, Uri.parse("upi://pay"))
	    val pm = getPackageManager()
	    val upiActivities: List<ResolveInfo> = pm.queryIntentActivities(upiIntent, 0)
	    for (a in upiActivities){
	        if (a.activityInfo.packageName == packageName) appUpiReady = true
	    }
	    return appUpiReady
	}