Android Best Practices for Visa Checkout SDK Integration

Pradeep_Visa
Visa Employee

This document provides best practices for VCO integration in Android. The scenarios included in this document extracted from real world integration issues which merchants had faced. For any iOS Developers, we've got a guide for you as well, check it out here

 

Android Best Practices

 

Here are the recommended devices and environment requirements:

  • Android 4.4 KitKat (API Level 19) or later
  • Android Studio 2.3 or later
  • Gradle Plugin 2.2.3 or later

 

Let's start with the steps to configure and set up the Visa Checkout mobile SDK for your Android  Studio project

 

  1.  Add `visacheckout-android-sdk–7.1.1.aar` file to your application's `libs` folder: [apps/libs] folder.
  2. In your app module's `build.gradle` file, Add the location of the SDK to the `repositories` block:
       ```
       android {
           ...
           repositories {
               ...
               flatDir { dirs 'libs' }
           }
       }
       ```
    Add the Visa Checkout SDK to the `dependencies` block.
       ```
       dependencies {
             ...
             implementation(name:'visacheckout-android-sdk-7.1.1', ext:'aar')
       }
       ```
    ​*Ensure a minimum SDK version of 19 in the `defaultConfig` block.
       ```
        defaultConfig {
             ...
             minSdkVersion 19
        }
        ```
  1. As a final step, in your `AndroidManifest.xml` file, add the following permissions:
     ```
    
      <uses-permission android:name="android.permission.INTERNET"/>
    
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
      <uses-permission android:name="android.permission.USE_FINGERPRINT"/>
    
      ```
    
      * (Optional) Override `allowBackup` if it's set to true by adding the `replace` attribute.
    
      ```
    
      <application
    
            android:allowBackup="true"
    
            tools:replace="allowBackup"
    
            ...
    
      </application>
    
      ```​

 

Now, let's review how to add the Native VCO standard button to the application.

 

The recommended way to use the `CheckoutButton` is to add the Visa Checkout Button in your layout and prepare the button with profile and purchase information using the `init` method. This method will provide the button with the needed information to configure Visa Checkout and prepare for a button tap.

 

In your layout add the Visa Checkout Button in the following way:

 

 

 

 

 

 

 

 

```xml
 <com.visa.checkout.CheckoutButton
    android:id="@+id/btn_visa_checkout"
    android:layout_width="@dimen/button_width"
    android:layout_height="@dimen/button_height"/>
```

 

 

 

 

 

 

 

 

  • The following code snippet sets the profile and purchase information for the button
    • Replace 'MERCHANT-API-KEY' with Api Key obtained from Visa Developer portal
    • Replace 'PROFILE' with Profile Name obtained from Visa Developer portal
      ```Java
      public class CheckoutButtonActivity extends AppCompatActivity {
          
          @Override
          protected void onCreate(@Nullable Bundle savedInstanceState) {
      Profile profile = new Profile.ProfileBuilder("MERCHANT-API-KEY", // TODO: Replace 'MERCHANT-API-KEY' with Api    Key obtained from Visa Developer portal.
      Environment.SANDBOX).setProfileName("PROFILE") // TODO: Replace 'PROFILE' with Profile Name obtained    from Visa Developer portal.
                      .build();
      
              PurchaseInfo purchaseInfo = new PurchaseInfo.PurchaseInfoBuilder(new BigDecimal("10.23"),
                      PurchaseInfo.Currency.USD) .build();
      
              visaCheckoutButton = findViewById(R.id.btn_visa_checkout);
              setUpCheckoutButton(profile, purchaseInfo) {
          }
      
          // Visa Checkout functionality is supported for devices running on KITKAT or above.
          // Below version check needs to be added for client apps supporting minSdkVersion = 16
          void setUpCheckoutButton(Profile profile, PurchaseInfo purchaseInfo) { 
              if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                  visaCheckoutButton.init(this, profile, purchaseInfo, new VisaCheckoutSdk.VisaCheckoutResultListener() {
                      @Override
                      public void onButtonClick(LaunchReadyHandler launchReadyHandler) {
                          launchReadyHandler.launch();
                      }
          
                  @Override
                  public void onResult(VisaPaymentSummary visaPaymentSummary) {
                      if (VisaPaymentSummary.PAYMENT_SUCCESS.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                          Log.d("AppTag", "Success");
                      } else if (VisaPaymentSummary.PAYMENT_CANCEL.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                          Log.d("AppTag", "Canceled");
                      } else if (VisaPaymentSummary.PAYMENT_ERROR.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                          Log.d("AppTag", "Error");
                      } else if (VisaPaymentSummary.PAYMENT_FAILURE.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                          Log.d("AppTag", "Generic Unknown failure");
                      }                
                  }
              });
          }
      }
      ```
      

 

 

Override the onResult() method as shown above to receive a `VisaPaymentSummary` object from Visa Checkout, which contains the encrypted payload, if requested and available. You have now completed the steps in adding the VCO standard button.

 

Next, let's learn how to add the Native VCO Custom button to the application

 

You also have the option to launch the Visa Checkout SDK using your own custom button instead of the Visa Checkout button. For instance, you may want to create a custom button and launch the Visa Checkout SDK whenever the button is tapped. Here’s how you can do it:

 

  • Call the `VisaCheckoutSdk.initManualCheckoutSession` method in `onCreate` method of the Activity that initializes the Visa Checkout SDK.
  • The `onReady` callback function here returns an object of type `ManualCheckoutLaunchHandler` on which you can execute the `launch()` function later, when the button is tapped. You may create a variable in this Activity as follows to hold on to the launch handler object:
    ```Java
    private ManualCheckoutSession.ManualCheckoutLaunchHandler launchHandler;
    ```
    The following code snippet shows how to use the `VisaCheckoutSdk.initManualCheckoutSession` method in `onCreate` of the Activity you present Visa Checkout SDK from.
    Replace 'MERCHANT-API-KEY' with Api Key obtained from Visa Developer portal.
    Replace 'PROFILE' with Profile Name obtained from Visa Developer portal.
    
    ```Java
    public class CustomButtonActivity extends AppCompatActivity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            Profile profile = new Profile.ProfileBuilder("MERCHANT-API-KEY", // TODO: Replace 'MERCHANT-API-KEY' with Api Key obtained from Visa Developer portal.
                    Environment.SANDBOX).setProfileName("PROFILE") // TODO: Replace 'PROFILE' with Profile Name obtained from Visa Developer portal.
                    .build();
    
            PurchaseInfo purchaseInfo = new PurchaseInfo.PurchaseInfoBuilder(new BigDecimal("10.23"),
                    PurchaseInfo.Currency.USD)
                    .build();
    
            setUpCustomButton(profile, purchaseInfo);
        }
    
        void setUpCustomButton(Profile profile, PurchaseInfo purchaseInfo) {
            final ImageView customButton = findViewById(R.id.custom_button);
            customButton.setVisibility(View.VISIBLE);
            customButton.setBackground(VisaCheckoutSdk.getCardArt(this.getApplicationContext()));
    // Visa Checkout is supported for devices running Android KITKAT or above.
            // Below version check needs to be added for apps supporting minSdkVersion = 16
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                VisaCheckoutSdk.initManualCheckoutSession(this, profile, purchaseInfo, new ManualCheckoutSession() {
                    @Override
                    public void onReady(ManualCheckoutLaunchHandler manualCheckoutLaunchHandler) {
                        // store launch handler for later use on click event
                        launchHandler = manualCheckoutLaunchHandler;
                    }
    
                    @Override
                    public void onResult(VisaPaymentSummary visaPaymentSummary) {
    //this call invokes the function VisaCheckoutSdk.initManualCheckoutSession and makes sure that you have the right handler
                        if (VisaPaymentSummary.PAYMENT_SUCCESS.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                            Log.d("AppTag", "Success");
                        } else if (VisaPaymentSummary.PAYMENT_CANCEL.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                            Log.d("AppTag", "Canceled");
                        } else if (VisaPaymentSummary.PAYMENT_ERROR.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                            Log.d("AppTag", "Error");
                        } else if (VisaPaymentSummary.PAYMENT_FAILURE.equalsIgnoreCase(visaPaymentSummary.getStatusName())) {
                            Log.d("AppTag", "Generic Unknown failure");
                        }
                        setUpCustomButton(profile, purchaseInfo);
                    }
                });
            }
        }
    
      ```
    
  • Later in your custom button's click listener you execute the `launch()` function on the launchHandler to launch the Visa Checkout SDK:
    ```Java
    customButton.setOnClickListener(new View.OnClickListener() {
        @Override public void onClick(View v) {
            if (launchHandler != null) {
                launchHandler.launch();
            }
        }
    });
    ```
    

Great, you should now be able to add the custom button on your app. Let's move on.

 

Here's one more best practices for how to add the Hybrid functionality to the application

 

1. In your layout add a parent `RelativeLayout` to your `Activity's layout` file.

 

 

 

 

 

 

 

```xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/parentRelativeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </WebView>

</RelativeLayout>
```
​

 

 

 

 

 

 

 

 

2. Call `VisaCheckoutHybridPlugin's configure()` from the `Activity` that contains the `WebView` used to show Visa Checkout.

 

 

 

 

 

 

 

```Java
public class HybridPluginActivity extends AppCompatActivity {
    RelativeLayout relativeLayout;
    Webview webview;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        relativeLayout = findViewById(R.id.parentRelativeLayout);
        webview = findViewById(R.id.webview);
        setWebView();
    }

    // Visa Checkout functionality is supported for devices running on KITKAT or above.
    // Below version check needs to be added for client apps supporting minSdkVersion = 16
    void setWebView() {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebSettings webSettings = webView.getSettings();
            webSettings.setJavaScriptEnabled(true);
            webSettings.setDomStorageEnabled(true);
            mWebView.getSettings().setSupportMultipleWindows(true);
            mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
            CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, true);
webView.setWebChromeClient(new WebChromeClient() {
                @Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, final Message resultMsg) {
                    WebView.HitTestResult result = view.getHitTestResult();
            
                    // finish configure by sending the Message object if all conditions are true.
if(result.getType() == WebView.HitTestResult.UNKNOWN_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) {
                        try {
                            Message href = view.getHandler().obtainMessage();
                            view.requestFocusNodeHref(href);
                            Bundle data = href.getData();
                            if (data.get("url") == null && data.get("title") == null) {
                                if(visaCheckoutHybridPlugin != null) {
                                    visaCheckoutHybridPlugin.finishConfigure(resultMsg);
                                    return true;
                                }
                            }
                        } catch (Exception e) {
                        }
                    }
                    return false;
                }
            });
            initHybridPlugin();
        }
        webview.loadUrl(url);
    }
    
    void initHybridPlugin() {
        if(visaCheckoutHybridPlugin != null) {
            // call VisaCheckoutHybridPlugin.clean() when reloading webView and leaving this activity/fragment
            visaCheckoutHybridPlugin.clean();
        }
    
        // make visaCheckoutHybridPlugin an Activity gloabl variable
        visaCheckoutHybridPlugin = new VisaCheckoutHybridPlugin();
        visaCheckoutHybridPlugin.configure(webView, this, relativeLayout, 
            new HybridPluginViewListener() {
            @Override
            public void hideViews() {
                // Hide any views that will overlay the plugin
                // Will be called as soon as the user taps the button
                webView.setVisibility(View.INVISIBLE);
            }
            
            @Override
            public void showViews() {
                // Show any views that were hidden for the plugin
                // Will be called after user finishes a flow (Success, Error, Canceled, Generic Unknown failure)
                webView.setVisibility(View.VISIBLE);
            }
        });
    }
}
```

 

 

 

 

 

 

 

 

3. Call `VisaCheckoutHybridPlugin's overrideBackButton()` from the `Activity` to override its `onBackPressed()`.

 

 

 

 

 

 

 

```Java
@Override
public void onBackPressed() {
    if(visaCheckoutHybridPlugin != null) {
        if (visaCheckoutHybridPlugin.overrideBackButton()) {
            return;
        }
        visaCheckoutHybridPlugin = null;
    }
    super.onBackPressed();
}
```
​

 

 

 

 

 

 

 

 

 

What to Avoid 

 

Avoid launching the Custom initialization recursively inside onResult(..) callback method as follows

 

 

 

 

 

 

 

 

private void launchOnReady() {
    VisaCheckoutSdk.initManualCheckoutSession(this, profile, purchaseInfo
            , new ManualCheckoutSession() {

                @Override
                public void onResult(VisaPaymentSummary paymentSummary) {
                    Log.i(TAG, "onResult: " + paymentSummary.getStatusName());
                    Log.i(TAG, "callID: " + paymentSummary.getCallId());
                    if (this != null) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                launchOnReady();
                            }
                        });
                    }
                }

              @Override
              public void onReady(ManualCheckoutLaunchHandler  manualLaunchHandler) {
                    Log.i(TAG, "OnReady...");
                    manualHandler = manualCheckoutLaunchHandler;
                    if (manualHandler != null) {
                        manualHandler.launch();
                    }
                }
            });
}

 

 

 

 

 

 

When the user calls the launchOnReady() recursively in the onResult(...){...}, then once the  user tries to come to the calling application, VCO SDK will launch the lightbox continuously. So to end the cycle, the user has to terminate the application.

 

Avoid launching the SDK via Android dialog box, as you seen in the picture below:

AndroidBestPractices.png

 

When the VCO SDK get launched like this, and for some reason if the Dialog does not visible to the user, or set isCancelable to true, then the user accidently presses the screen just outside the dialog box, then the user will not be able to launch the VCO lightbox.

 

Avoid Initialize the SDK multiple times

- When the merchant initializes the VCO SDK multiple times, the VCO lightbox may launch several times and sometimes it will lead to errors.

 

Avoid using UI Automation tools to automate the lightbox

- When the merchant uses automation tools to automate the lightbox at front end, at the backend, it will detect and stop responding to the user.

 

We hope this helps as you integrate the Visa Checkout Android SDK. Any questions? Comment below, we're here to help.