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

and models 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. Access to the Sample App & Turbo Library Files

    Request Access

    Contact our

    to get the access to the Sample App Repository https://github.com/upi-turbo/android-turbo-sample-app.

    Allowlist Requirements

    Provide the following details for us to allowlist you:

    • Mobile numbers of anyone testing the SDK flows.
    • Your debug, staging, and production app IDs.

    Build and Test

    • Once you have access, build and run the Sample App on your local machine to try out the different SDK flows.
    • Explore the sample code in the app/src project directory to understand how to interact with the SDK.

    Environment-Specific AAR Files

    • The AAR files (Turbo libraries) on the main branch are for the UAT environment.
    • The AAR files 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, NPCI CL and Turbo) common for headless and UI SDK
    • app/build.gradle: Contains all transitive dependencies (in the dependencies section) needed to integrate Turbo SDK.
  2. Turbo SDK is an add-on to the Razorpay Checkout SDK, so you must integrate the

    before proceeding with the Turbo SDK integration.

  3. Import Required Frameworks

    Copy AAR Files:

    From the Sample App repository, copy the required AAR files to your project directories:

    • app/libs/{uat or prod}/common
    • app/libs/{uat or prod}/headless

    Suggestion: Create separate directories, such as app/turboUATLibs and app/turboProdLibs to organise AAR files for debug and production variants in your project.

    Add Dependencies in build.gradle

    Add the dependencies for the AAR files in your module's build.gradle file:

    debugImplementation fileTree(include: ['*.aar'], dir: 'turboUATLibs')
    releaseImplementation fileTree(include: ['*.aar'], dir: 'turboProdLibs')

    Add Transitive Dependencies

    Refer to the app/build.gradle file in the Sample App to add all required transitive dependencies to your module.

    For Multi-Module Architecture

    If you are using a library module that consumes Turbo SDK within another app module, follow these additional steps:

    • In the library module, use:
    debugCompileOnly fileTree(include: ['*.aar'], dir: 'turboUATLibs')
    releaseCompileOnly fileTree(include: ['*.aar'], dir: 'turboProdLibs')
    • In the app module, create turboUATLibs and turboProdLibs directories & copy the AAR files. Then, add the dependencies in the build.gradle file:
    debugImplementation fileTree(include: ['*.aar'], dir: 'turboUATLibs')
    releaseImplementation fileTree(include: ['*.aar'], dir: 'turboProdLibs')
  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 23 and cannot be over written.
  • Use the rzp_test_0wFRWIZnH65uny API key id for testing on the UAT environment and the for prod testing.

Follow these steps:

To enhance security, you must create a session token via a server-to-server (S2S) call between your backend and Razorpay's backend. This session token ensures secure communication between the Turbo SDK and Razorpay's systems.

  1. Trigger the S2S API from your Backend. Use the following API to generate a session token:

    Environment based URLs:

    • UAT: https://api-web-turbo-upi.ext.dev.razorpay.in

    • Production: https://api.razorpay.com

    Authorization Header Creation: Base64.encode(${public_key}:${secret})

    Request Parameters

    customer_reference

    mandatory

    string A unique identifier for the customer provided by the business. The recommended value is mobile number. For example, 9000090000.

    Response Parameters

    token

    string A session token to be used in subsequent session-protected APIs.

    expire_at

    long Expiry time (in seconds) for the session token, used to optimise session handling and reduce unnecessary reinitialisations.

    error

    object The request failure due to business or technical failure.

Initialise the TurboSessionDelegate object anonymously and pass it through the initialize method. The SDK will call fetchToken as needed and internally get the token using your implementation.

Inside the fetchToken function implementation, you must get a new token from your server, as mentioned in section

. Once the token is available, you can pass it to the completion object provided as a function parameter.

val turboSessionDelegate: TurboSessionDelegate = object : TurboSessionDelegate {
override fun fetchToken(completion: (Session) -> Unit) {
// fetch token here and once fetched,
// it can be passed back to Turbo SDK using completion lambda callback
completion(Session(token: <new-token>))
}
}
// pass the above created turboSessionDelegate object through initialize method
razorpay
.upiTurbo
.initialize(turboSessionDelegate)

After initialising the Turbo SDK, proceed to securely link UPI accounts and complete the payment flow.

  1. Get already linked accounts.

    If your customer has already linked accounts, use the following code to fetch them. If there are no linked UPI accounts, an empty list is returned.

    razorpay.upiTurbo.getLinkedUpiAccounts({10 digit mobile number / random customer id}, 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.
    }
    }
    });

    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 code.

    Request Parameters

    customerMobile

    mandatory

    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.

  2. New user onboarding / Linking additional accounts

    If you get DEVICE_BINDING_INCOMPLETE error code in the above section, you will need the user to complete the new onboarding. For already onboarded users, this function will help to link more accounts.

    razorpay.upiTurbo.linkNewUpiAccount({10 digit mobile number / random customer id}, 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;
    }
    }
    });

    Handy Tips

    Turbo SDK will auto-select the SIM card in few cases and the SELECT_SIM action will not be triggered in such cases:

    • Device has only one SIM card.

    • Device has multiple SIM cards and the mobile number provided by you matches the number on one of the SIM cards.

  3. Invoke the below function in your Activitys onRequestPermissionsResult function.

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
    if (requestCode == 101) {
    // make sure not to use the 101 requestCode for your other cases
    razorpay.upiTurbo.onPermissionsRequestResult()
    return
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
  4. Additionally razorpay.onBackPressed() is triggered when a user tries to exit the app or return to the previous page. The releaseActivityReference() function releases the allocated memory.

    @Override
    public void onBackPressed() {
    razorpay.onBackPressed();
    releaseActivityReference();
    super.onBackPressed();
    }
  5. To process the payment, first create a payload, which will be a JSONObject as shown in the code below.

    JSONObject payload = new JSONObject();
    payload.put("currency", "INR");
    payload.put("email", "gaurav.kumar@example.com");
    payload.put("contact", "9000090000");
    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");//mandatory
    payload.put("customer_id", "cust_KQlMczYKcDIqmB");//optional
  6. Pass the upiAccount 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) {
    }
    @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/NPCI.

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() and pass the UpiAccount instance you have received from Turbo SDK before.

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() and pass the upiAccount instance you have received from Turbo SDK before.

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. You will need to collect the below mentioned details for the account:

  • Bank accounts: last 6 digits, expiry month & year of the Debit Card.
  • Credit cards: last 6 digits, expiry month & year of the Credit Card.

Pass these details along with the card (Card data class is provided by Turbo SDK) and the upiAccount object you received from Turbo SDK.

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. To get the device binding status, please use the method isDeviceOnboarded() which returns a 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.isDeviceOnboarded(activity: Activity)) {
    // User Device Binded
    } else {
    // Call Link New Account for Device Binding
    }

Action Parameter Values

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

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

BankAccount

Bank

AccountBalance

Error

UpiAccount

SIM

Card

AllBanks

UpiTurbo.LinkAction

BankAccountState

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.

  • As a compliance requirement, you need to get approval from Google for READ_SMS permission. Refer

    for more details.

  • 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?