Integrate Turbo UPI Headless

Steps to integrate Razorpay Turbo UPI Headless with your app.


Use Razorpay Turbo UPI to make UPI payments faster. Follow these steps to integrate with the Razorpay Turbo UPI Headless SDK.

What's New

Users can now link their credit cards alongside bank accounts during onboarding. Merchants can seamlessly retrieve both credit and bank accounts for transactions, thereby simplifying payments, expanding options, and ensuring security.

Changes have been made to the

regarding credit card support on UPI.

Watch Out!

Charges will be levied for payments made using CC on UPI. Contact the

for further information.

Prerequisites

  1. Contact our

    to get your mobile number, app and GitHub account whitelisted to get access to the https://github.com/upi-turbo/android-turbo-sample-app - sample app repository. In this repository, you will find the AAR files (libraries for Turbo) and the sample app source code to help you do the entire integration. The AARs on the main branch are for the UAT environment, and the ones on the prod branch are for the production environment.
    These are the important files in the sample app repo:

    • app/src/turboUI: Sample app code for UI SDK
    • app/libs: All libraries (Bank, SecureComponent and Turbo) common for headless and UI SDK
    • app/uiLibrary: Library for UI SDK.
    • app/build.gradle: All transitive dependencies needed to integrate Turbo SDK.
  2. Integrate with

    .

  3. Import the following frameworks:

    • Razorpay Turbo Wrapper Plugin SDK (maven)
    • Razorpay Turbo Core SDK
    • Razorpay SecureComponent SDK
    • Bank SDK
  4. Add the following lines to your Android project's gradle.properties file:

    • android.enableJetifier=true
    • android.useAndroidX=true

Watch Out!

  • minSDKversion for using Turbo UPI is currently 19 and cannot be over written.
  • Use the rzp_test_0wFRWIZnH65uny API key id for testing on the UAT environment and the for prod testing.
  • As a compliance requirement, you need to get approval from Google for READ_SMS permission. Refer for more details.
  • If the user changes their mobile number during onboarding, you should store the updated number and pass it to the Turbo SDK.

  1. You need to link the customer's UPI account with your app. Use the code samples given below to fetch the UPI account.

    Watch Out!

    If the device binding is not completed and the getLinkedUpiAccounts is triggered, it will return an OnError with a DEVICE_BINDING_INCOMPLETE error message.

    • If your customer has already linked the UPI account, use the following code to fetch it. If there are no linked UPI accounts, an empty list is returned.
    razorpay.upiTurbo.getLinkedUpiAccounts("<customerMobile>", new UpiTurboResultListener(){
    @Override
    public void onError(@NonNull Error error) {
    //Display error message to user.
    }
    @Override
    public void onSuccess(@NonNull List<UpiAccount> accList) {
    if (accList.size()==0){
    //Display: no UpiAccounts onboarded yet. Please onboard an account.
    }else{
    //Display onboarded UpiAccounts.
    }
    }
    });

    Request Parameters

    customerMobile

    string The customer's mobile number.

    listener

    object The listener to be sent should be of type UpiTurboResultListener.

    Response Parameters

    onSuccess

    This function is triggered if the list is fetched successfully. accList can be empty to indicate that no accounts have been linked yet.

    onError

    This function is triggered in case an error is thrown during the retrieval process, either by the Razorpay SDK or the Bank SDK.

    • If the customer has not linked any UPI account, use the following code to link the newly created UPI account with your app. This function can be called from anywhere in the application, providing multiple entry points for customers to link their UPI account with your app.
    razorpay.upiTurbo.linkNewUpiAccount("<CUSTOMER_MOBILE>", new UpiTurboLinkAccountListener() {
    @Override
    public void onResponse(@NonNull UpiTurboLinkAction action) {
    switch (action.getCode()){
    case ASK_FOR_PERMISSION:
    action.requestPermission();
    break;
    case SHOW_PERMISSION_ERROR:
    //Show dialog to redirect the user to the settings page of the application to grant permissions
    break;
    case SELECT_SIM:
    if (action.getError()!=null){
    //Display error message
    return;
    }
    if (action.getData() != null && action.getData() instanceof List){
    try{
    List<?> simList = (List<?>) action.getData();
    Sim sim1 = (Sim) simList.get(0);
    Sim sim2 = (Sim) simList.get(1);
    //Show dialogue with a list of sims
    action.selectedSim(sim1);
    }catch (ClassCastException e){
    }
    }
    break;
    case SELECT_BANK:
    if (action.getError()!=null){
    return;
    }
    if (action.getData()!=null && action.getData() instanceof AllBanks){
    AllBanks allBanks = (AllBanks) action.getData();
    List<Bank> popularBanks = allBanks.getPopularBanks();
    List<Bank> allBanksList = allBanks.getBanks();
    //show dialog with bank list
    action.selectedBank(popularBanks.get(0));
    }
    break;
    case SELECT_BANK_ACCOUNT:
    if (action.getError()!=null){
    return;
    }
    if (action.getData()!=null && action.getData() instanceof List){
    List<?> bankAccountList = (List<?>) action.getData();
    if (bankAccountList.get(0) instanceof BankAccount){
    //Show dialog with bank account list
    action.selectedBankAccount((BankAccount)bankAccountList.get(0));
    }
    }
    break;
    case SETUP_UPI_PIN:
    Card card = new Card("01", "28", "234567");
    action.setupUpiPin(card);
    break;
    case STATUS:
    if (action.getError()!=null){
    //Show error message
    return;
    }
    if (action.getData()!=null && action.getData() instanceof List){
    List<?> onboardedUpiAccounts = (List<?>) action.getData();
    showUpiAccount((UpiAccount) onboardedUpiAccounts.get(0));
    }
    break;
    case LOADER_DATA:
    //Use this trigger to easily show background process happening in the SDK during onboarding
    showLoaderData((String) action.getData());
    break;
    }
    }
    });

    action

    The current state of customer registration with which you can call further functions. All values for this variable are exposed as an enum for ease of integration. Know more about the

    .

    Handy Tips

    Conditions for SELECT_SIM action:

    • Triggered:
      • CASE 1: The customer's phone has only one SIM, but the mobile number provided is not the same as the mobile number in the SIM object.
      • CASE 2: The customer's phone has multiple SIMs, but the mobile number provided is not the same as the mobile number in the SIM object in either of the SIMs.
    • Non-Triggered:
      • CASE 1: The customer's phone has one SIM, and the mobile number provided is the same as the mobile number in the SIM object received.
      • CASE 2: The customer's phone has multiple SIMs, and the mobile number provided is the same as the mobile number in one of the SIM objects received by the OS.
  2. To accept payments, call Custom Checkout’s submit method with the following payload:

    JSONObject payload = new JSONObject();
    payload.put("currency", "INR");
    payload.put("email", "gaurav.kumar@example.com");
    payload.put("contact", "9999999999");
    payload.put("amount", "10000");
    payload.put("method", "upi");
    JSONObject upiBlock = new JSONObject();
    upiBlock.put("flow", "in_app");
    payload.put("upi", upiBlock);
    payload.put("order_id", "order_L2MUBUOeFItcpU");//optional
    payload.put("customer_id", "cust_KQlMczYKcDIqmB");//optional
  3. Pass the vpa and payload objects as shown in the code below:

    HashMap<String, Object> turboPayload = new HashMap<>();
    turboPayload.put("upiAccount", upiAccount);
    turboPayload.put("payload", payload);
    razorpay.submit(turboPayload, new PaymentResultWithDataListener() {
    @Override
    public void onPaymentSuccess(String razorpayPaymentID, PaymentData paymentData) {
    //show success message
    }
    @Override
    public void onPaymentError(int code, String response, PaymentData paymentData) {
    //Show error message
    }
    });

Handy Tips

In case of an error response, you will get a nested reason JSON object, which will contain the original error code and description from the bank/Secure component.

You can directly interact with the exposed methods of the Turbo Framework to perform the non-transactional flows listed below.

Fetch Balance

Fetch the customer's account balance. Call getBalance() on the bank account object received from upiAccount.

razorpay.upiTurbo.getBalance(upiAccount, new Callback<AccountBalance>() {
@Override
public void onSuccess(AccountBalance accountBalance) {
}
@Override
public void onFailure(@NonNull Error error) {
}
});

Change UPI PIN

Provide the customer the ability to change their UPI PIN. Call changeUpiPin() on the bank account object received from UpiAccount.

razorpay.upiTurbo.changeUpiPin(upiAccount, new Callback<UpiAccount>() {
@Override
public void onSuccess(UpiAccount upiAccount) {
}
@Override
public void onFailure(@NonNull Error error) {
}
});

Reset UPI

Let your customers reset the PIN for their account.

razorpay.upiTurbo.resetUpiPin(card, upiAccount, new Callback<Empty>() {
@Override
public void onSuccess(Empty empty) {
}
@Override
public void onFailure(@NonNull Error error) {
}
});

Let your customers delink, that is, remove a selected UPI account from your application.

razorpay.upiTurbo.delink(upiAccount, new Callback<Empty>() {
@Override
public void onSuccess(Empty empty) {
}
@Override
public void onFailure(@NonNull Error error) {
}
});

  1. The below function is triggered internally after integrating with the Razorpay Android Custom SDK.

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
    razorpay.upiTurbo.onPermissionsRequestResult()
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
  2. razorpay.onBackPressed() is triggered when a user tries to exit the app or return to the previous page. The razorpay.upiTurbo.destroy() function clears that particular session so that when the user comes back, the payment process starts from the beginning.

    @Override
    public void onBackPressed() {
    razorpay.onBackPressed();
    razorpay.upiTurbo.destroy();
    super.onBackPressed();
    }
  3. To get the device binding status, please use the variable razorpay.upiTurbo.deviceBindingDone of type boolean. It indicates whether the device binding, which is a prerequisite for adding UPI accounts, is done with the user's mobile number.

    if(razorpay.upiTurbo.deviceBindingDone){
    //show UI, which indicates device binding with the customer's mobile is pending
    }else{
    //proceed with getLinkedUpiAccounts
    }

Action Parameter Values

Following are the constants passed in the action.code parameter in onResponse.

NameDescriptionNext Function
ASK_FOR_PERMISSIONNo UPI account found. That is, the customer has not registered. You should start customer registration.action.requestPermission()
SHOW_PERMISSION_RATIONALEYou can use this to show a dialogue directing customers to navigate and enable it from app settings.NA (You should show Go To Settings UI to enable permissions for the customers)
SELECT_SIMSIM details are fetched from the device to show the customer to begin the registration process. This will be skipped if the customer only has one SIM on the device.action.selectedSim(sim)
SELECT_BANKObject AllBanks returned by SDK for customer selection with:
  • popularBanks
  • allBanks
action.selectedBank(bank)
SELECT_BANK_ACCOUNTList of accounts related to the customer in the selected bank. (If no accounts are found on the mobile number, onError will not be null, indicating that you should request a different number/SIM based on the use case.)action.selectedBankAccount(bankAccount)
SET_UPI_PINTriggered if the UPI Pin is not set for the selected bank account.action.setUpiPin(Card)
LOADER_DATAReturns messages that can be used for the loader, informing the customer of the steps happening during the onboarding process.NA
STATUSThe onboarding process's final status returns the onboarded UpiAccount or error after a bank account was selected or SET_UPI_PIN was triggered.NA

The SDKs given below provide access to exposed models for seamless integration.

BankAccount

MethodReturn TypeDescription
getAccountNumber()StringMasked account number.
getBeneficiaryName()StringName of the account holder.
getBank()StringThe bank code.

Bank

MethodReturn TypeDescription
getIfsc()StringIFSC code of bank.
getName()StringName of bank.
getImageURL()StringImage URL of bank logo.
getBankPlaceholderUrl()StringImage URL for bank logo placeholder.

AccountBalance

MethodReturn TypeDescription
getBalance()longBalance amount in paise.
getCurrency()StringCurrency Type in INR.
getId()StringBank Account id.
getOutstanding()LongOutstanding balance for Credit accounts.

Error

ErrorDescription
getErrorCode()Types of error codes
  • BAD_REQUEST_ERROR: Failure from client's end (SDK).
  • GATEWAY_ERROR: Failure either from the Secure Component or the Bank.
  • SERVER_ERROR: Failure at PSP.
getErrorDescription()Brief description of the error.

UpiAccount

MethodReturn TypeDescription
getAccountNumber()StringReturns masked bank account number.
getType()StringThe account type. Possible values are bank_account and credit_card .
getIfsc()StringReturns IFSC for Bank.
getBankName()StringReturns name of bank.
getBankLogoUrl()StringReturns URL to the logo of the PNG image.
getBankPlaceholderUrl()StringImage URL for bank logo placeholder.

SIM

MethodReturn TypeDescription
getId()StringSIM id used to target SIM card for binding.
getProvider()StringNetwork provider name.
getSlotNumber()IntegerSIM slot number. Possible values are 0 and 1 .
getNumber()StringMobile number stored in SIM card.

Card

MethodReturn TypeDescription
getLastSixDigits()StringLast six digits of the debit card number.
getExpiryYear()StringExpiry year. Format: YY.
getExpiryMonth()StringExpiry month.

AllBanks

MethodReturn TypeDescription
getPopularBanks()List<Bank>Returns the list of the top 8 banks.
getAllBanks()List<Bank>Returns a list of all banks.

UpiTurbo.LinkAction

MethodReturn TypeDescription
additionalDetails()NASend additional required parameters.
requestPermissions()NARequest for required permissions and get SIM details.
selectedSim()NASend selected SIM object to SDK.
selectedBank()NASend selected bank object to SDK.
selectedBankAccount()NASend selected bankAccount object to SDK.
setUpiPin()NASend card object to SDK to create UPI PIN.

We recommend the following:

  • Complete the integration on UAT before using the prod builds.
  • Perform the UAT using the Razorpay-provided API keys.

Complete these steps to take your integration live:

  • You should get your app id whitelisted by Razorpay to test on prod.

    Handy Tips

    Contact our

    to get your mobile number and app whitelisted.

  • Import the prod library from the Github repository → https://github.com/upi-turbo/android-turbo-sample-app/tree/prod/app/libs prod branch.

  • Add Proguard rules:

    • keepclassmembers,allowobfuscation class * { @com.google.gson.annotations.SerializedName <fields>; }
    • keepclassmembers enum * { *; }
    • keepclassmembers class * { @android.webkit.JavascriptInterface <methods>; }
    • dontwarn com.razorpay.**
    • keep class com.razorpay.** {*;}
    • keep class com.olivelib.** {*;}
    • keep class com.olive.** {*;}
    • keep class org.apache.xml.security.** {*;}
    • keep interface org.apache.xml.security.** {*;}
    • keep class org.npci.** {*;}
    • keep interface org.npci.** {*;}
    • keep class retrofit2.** { *; }
    • keep class okhttp3.** { *; }
  • Replace the UAT credential with the

    for prod testing.


Is this integration guide useful?