Android 3DS 2.0 SDK
Power native experience on the new 3DS 2.0 protocol for card transactions. Less latent, highly customisable, highest uptime with option to fallback in case of failures. Going forward from October 2023, only through a certified 3DS SDK can a merchant power native experience on app.
Benefits & features
- Power native experiences on cards through our native SDK
- Offers bin eligibility api to route transactions through 3DS1 or 3DS2
- Loosely coupled. Offers two flows
- Everything through payu – (Device Collection + Authentication + Complete Challenge + Authorization
- Only Device Collection + Challenge and use any other aggregator for authentication/ authorization
- Device Collection + Authentication + Complete Challenge
- Fallback to 3DS 1 available in case of failures in device collection.
- Highest uptime through multiple 3DS Server in future.
- Compliant EMVCO certified 3DS SDK with more control across the whole customer journey.

Integration
PayU SDK offers the following methods to integrate with 3DS 2.0:
- SDK Integration: Min SDK Version is v21 Compile SDK Version is v31 or later
- Maven Dependency URL Use the following code snippet in your app’s build.gradle file:
implementation 'in.payu:threeds-sdk:1.1.2'
- Use our SDK for a complete transaction:
- Collecting device details
- Invoking an authentication request through our 3DS Server
- Invoking challenge
- Completing authorization through PayU
- Use our SDK for collecting device details and to render challenge screens.
Using PayU implementation
Call the method to initiate payment through us and we will return a success or failure callback post-transaction completion.
fun initiatePayment(
activity: AppCompatActivity,
config: PayU3DS2Config,
paymentParams: PaymentParams,
callback: PayU3DS2PaymentCallback
)
You have to pass the following parameters:
Parameter | Description |
---|---|
activity | This parameter contains the |
config | This parameter contains the following properties:
|
paymentParams | Merchants have to create the payment param object and pass it which will contain info such as SDK Integration > Build the payment parameters . |
callback | This parameter contains the following methods: fun onPaymentSuccess(successResponse: Any): It will contain a success response. This will be a JSON Object, parse response as per your need. fun onPaymentFailure(failureResponse: Any): It will contain a failure response. This will be a JSON Object, parse response as per your need fun onPaymentCancel(isTxnInitiated: Boolean): It will tell if payment was canceled. fun onError(errorCode: Int, errorMessage: String): It will contain failure reason code and reason. fun generateHash(map: HashMap<String, String>, hashGenerationListener: PayUHashGeneratedListener): Merchant will get a map with the type of hash and hash string as the value of the map. Refer to the Sample code for callback - generateHash . |
Sample code for callback - generateHash
if (map.containsKey("hashString") && map.containsKey("hashName")) {
val hashData = map["hashString"]
val hashName = map["hashName"]
val postSalt = map["postSalt"]
var newsalt = salt
if (!postSalt.isNullOrEmpty()) {
newsalt += postSalt
}
Log.d("TAG", "generateHash: " + hashData)
Log.d("TAG", "generateHash: " + hashName)
Log.d("TAG", "generateHash: " + newsalt)
var hash: String?
// Do not generate hash from local, it needs to be calculated from server side only.
// Here, hashString contains hash created from your server side.
if (!TextUtils.isEmpty(hash)) {
val dataMap = HashMap<String, String>()
dataMap[hashName!!] = hash!!
hashGenerationListener.onHashGenerated(dataMap)
}
}
Decoupled Flow
Step 1:Initialise SDK
Initialization of SDK is required if the merchant is utilizing PayU 3DS 2.0 for Decoupled functionality. For more information on properties, refer to <<Description of Properties in Initialization>>.
PayU3DS2.initialise(
key: String,
requestId: String,
activity: AppCompatActivity,
config: PayU3DS2Config): PayU3DSResponse
CalloutIf auto-read is false, auto-submit will not work whereas auto-read will work in case of auto-submit is false.
Parameter | Description |
---|---|
Key | The key provided to merchant by PayU. |
RequestId | Unique request ID for the transaction. |
AppCompatActivity | Required to initialise SDK. |
GUI customisation
The following components can be customized:
- Button
- Label
- Toolbar
- Text box
- Font
- GUI
- The sample code blocks for the above:
var buttonCustomisation = ButtonCustomisation.Builder()
.setBackgroundColor("colorCode") //HEX CODE
.setCornerRadius(5).build() //Integer
var labelCustomisation = LabelCustomisation.Builder()
.setHeadingTextColor("colorCode") //HEX CODE
.setHeadingTextFontName(FontName.ROBOTO_REGULAR)
.setHeadingTextFontSize(10) //Integer
.setTextColor("colorCode") //HEX CODE
.setTextFontName(FontName.ROBOTO_REGULAR)
.setTextFontSize(10) //Integer
.build()
var toolbarCustomisation = ToolbarCustomisation.Builder()
.setBackgroundColor("colorCode") //HEXCODE
.setButtonText("ButtonText") //String
.setHeaderText("HeaderText) //String
.setTextColor("colorCode") //HEXCODE
.setTextFontSize(18) //Integer
.setTextFontName(FontName.ROBOTO_REGULAR)
.build()
var textBoxCustomisation = TextBoxCustomisation.Builder()
.setTextColor("colorCode") //HEXCODE
.setBorderColor("colorCode) //HEXCODE
.setCornerRadius(5) //Integer
.setTextFontSize(5) //Integer
.setBorderWidth(5) //Integer
.setTextFontName(FontName.ROBOTO_REGULAR)
.build()
val fontFamilyCustomisation = FontFamilyCustomisation.Builder()
.setHeaderFontFamily("Header Font family path")
.setSubTextFontFamily("Sub text font family path")
.build()
var uiCustomisation = UICustomisation.Builder()
.setButtonCustomisation(buttonCustomisation)
.setToolbarCustomisation(toolbarCustomisation)
.setTextBoxCustomisation(textBoxCustomisation)
.setLabelCustomisation(labelCustomisation)
.setFontFamilyCustomisation(fontFamilyCustomisation)
.build()
Supported Font Type Details
enum class FontName {
ROBOTO_REGULAR,
ROBOTO_MEDIUM
}
PayU3DS2Response:
The response includes the following parameters:
Parameter | Description |
---|---|
status |
This parameter returns the status of the web service call. The status can be any of the following: 0 - If the web service call succeeded 1 - If the web service call failed. |
errorMessage |
The error message with details of what went wrong. |
result |
Success response with details. Refer to the following class (below the table) for the response structure. |
The following items are in the response:
data class PayU3DS2DeviceWarning(
val id: String? = null,
val message: String? = null,
val severity: DeviceSeverity? = null
)
enum class DeviceSeverity {
LOW,
MEDIUM,
HIGH
}
Step 2: Device details(PArq)
To obtain device information to initiate an authentication request:
PayU3DS2.extractDeviceDetails(cardScheme: CardScheme): PayU3DS2Response
cardScheme expected values:
- VISA
- MASTERCARD
PayU3DS2Response: Three items are in the response:
Parameter | Description |
---|---|
status |
This parameter returns the status of the web service call. The status can be any of the following: 0 - If the web service call succeeded 1 - If the web service call failed. |
errorMessage |
The error message with details of what went wrong. |
result |
Success response with details. Refer to the following class (below the table) for the response structure. |
data class PArqResponse(
val sdkAppID: String,
val sdkEncData: String,
val crv: String,
val kty: String,
val x: String,
val y: String,
val sdkTransID: String,
val sdkReferenceNumber: String
)
Now, these device details can be used to initiate an authentication request with us or any other aggregator.
After the authentication request has been initiated and a response has been received, the same is used to initiate a challenge which basically means opening a UI screen to do user authentication.
Step 3: 3DS 2.0 Challenge Initiation
Call the following function to start the challenge:
PayU3DS2.initiateChallenge(activity: Activity, challengeParameter: ChallengeParameter, listener: PayU3DS2BaseCallback)
Parameter | Description |
---|---|
activity |
This parameter contains the |
challengeParameter |
Create an object of ChallengeParameter class with the following parameters : ChallengeParameter("acsSignedContent", "acsRefNumber", "acsTransactionID", "threeDSServerTransactionID") acsSignedContent= Send ACS Signed Content received in ARes acsRefNumber= Send ACS Ref Number Content received in ARes acsTransactionID= Send ACS Transaction ID received in ARes threeDSServerTransactionID= Send ThreeDS Server Transaction ID received in ARes |
Before invoking this method, generate the authentication request through any aggregator and pass the above-defined challenge parameters to initiate challenges.
PayU3DS2BaseCallback: Callback consists of two methods:
fun onSuccess(response: Any) //It will contain success response.
fun onError(errorCode: Int, errorMessage: String) //It will contain failure reason code and reason.
//Cast response to String. If value is "Y" that means challenge is successfully executed else it is failed.
PaymentParams Parameter Example
Basic Payment Parameters
The PaymentParams object contains key fields required for initiating a payment request with PayU. These parameters are critical for identifying the transaction, the customer, and the product.
var mPaymentParams = PaymentParams()
mPaymentParams.key = "<Your Key issued by PayU>" // Merchant key provided by PayU
mPaymentParams.amount = "<Transaction Amount>" // The total amount of the transaction
mPaymentParams.productInfo = "<Product Description>" // Description of the product being purchased
mPaymentParams.firstName = "<Customer First Name>" // Customer's first name
mPaymentParams.email = "<Customer Email>" // Customer's email address
mPaymentParams.txnId = "<Transaction Id>" // Unique transaction ID for this payment
mPaymentParams.surl = "<Success URL>" // URL to redirect on successful payment
mPaymentParams.furl = "<Failure URL>" // URL to redirect on failed payment
mPaymentParams.udf1 = "<User Defined Fields>" // User-defined field 1
mPaymentParams.udf2 = "<User Defined Fields>" // User-defined field 2
mPaymentParams.udf3 = "<User Defined Fields>" // User-defined field 3
mPaymentParams.udf4 = "<User Defined Fields>" // User-defined field 4
mPaymentParams.udf5 = "<User Defined Fields>" // User-defined field 5
Credit/Debit Card Payment
To process payments using a credit or debit card, the following parameters need to be included in the PaymentParams object..
mPaymentParams.cardNumber = "<cardNumber>" // Credit/Debit card number
mPaymentParams.cardName = "<cardName>" // Card type (e.g., Visa, MasterCard)
mPaymentParams.nameOnCard = "<cardholderName>" // Name of the cardholder
mPaymentParams.expiryMonth = "<expiryMonth>" // Card expiry month (MM)
mPaymentParams.expiryYear = "<expiryYear>" // Card expiry year (YYYY)
mPaymentParams.cvv = "<cvv>" // CVV code on the back of the card
Store Credit/Debit Card
To store the card for future transactions (such as recurring payments), the StoreCard option should be enabled. This allows the card to be saved securely for later use..
mPaymentParams.setCardNumber(cardNumber);
mPaymentParams.setCardName(cardName);
mPaymentParams.setNameOnCard(cardholderName);
mPaymentParams.setExpiryMonth(expiryMonth);// MM
mPaymentParams.setExpiryYear(expiryYear);// YYYY
mPaymentParams.setCvv(cvv);
mPaymentParam.setUserCredentials(userCredentials);
mPaymentParam.setStoreCard(1);
Recurring Payments via Card
For recurring payments, you need to configure SIParams (Subscription Information). This includes the billing cycle, amount, and other details regarding the recurring payment setup.:
fun getSIDetails(): SIParams {
var siParams = SIParams()
siParams.api_version = "7" // API version
siParams.si = "1" // Indicates recurring payment
siParams.isFree_trial = false // Free trial flag (if applicable)
var siParamDetails = SIParamsDetails()
siParamDetails.billingAmount = "1.0" // Recurring billing amount
siParamDetails.billingCurrency = "INR" // Currency (INR in this example)
siParamDetails.billingInterval = 1 // Interval between payments (e.g., monthly)
siParamDetails.billingCycle = BillingCycle.ADHOC // Recurring cycle type
siParamDetails.paymentStartDate = "2025-09-26" // Start date of the recurring payments
siParamDetails.paymentEndDate = "2025-10-26" // End date of the recurring payments
siParams.si_details = siParamDetails
return siParams
}
mPaymentParams.siParams = getSIDetails() // Add subscription details to the payment parameters
Card Tokenization
Tokenization is used to securely store card details without exposing sensitive information. There are two main types of card tokenization:
Card Tokenization with PayU
To make payments using a previously saved card, you need to pass both the network token and the card token..
cardDetails.networkToken = "<networkToken>"
cardDetails.cardToken = "<cardToken>"
Third-Party Card Tokenization
If the card has been tokenized outside of PayU’s platform (via a third-party service), you need to provide additional tokenization information.
private fun getTokenizedDetails(): TokenizedCardAdditionalParam? {
var token = TokenizedCardAdditionalParam()
token.last4Digits = "XXXX" // Last 4 digits of the card
token.tavv = "XXXXXXXXXXXXXX" // Transaction authorization verification value
token.tokenRefNo = "XXXXXXXXXXXXXX" // Reference number for tokenized card
token.trid = "XXXXXXXXXXXXXX" // Transaction ID for this payment
return token
}
mPaymentParams.expiryMonth = "XX" // Card expiry month (MM)
mPaymentParams.expiryYear = "XXXX" // Card expiry year (YYYY)
mPaymentParams.cardToken = "XXXXXXXXXXXXXXXXX" // The token representing the saved card
mPaymentParams.cardTokenType = 1 // Type of tokenization (e.g., 1 = PayU token, 2 = third-party token)
mPaymentParams.tokenizedCardAdditionalParam = getTokenizedDetails() // Add token details
EMI
To process payments using EMI (Equated Monthly Installments), you need to specify the card details along with the bank code for EMI and set the payment gateway (PG) to "EMI"..
mPaymentParams.setCardNumber("5123456789012346") // Card number used for EMI payment
mPaymentParams.setNameOnCard("test") // Name on the card
mPaymentParams.setExpiryMonth("06") // Expiry month (MM)
mPaymentParams.setExpiryYear("2023") // Expiry year (YYYY)
mPaymentParams.setCvv("123") // CVV of the card
mPaymentParams.setBankCode("EMI03") // Bank code for EMI (e.g., EMI03)
mPaymentParams.setPg("EMI") // Set payment gateway to EMI
Start Redirection Flow
To authenticate the transaction using PayU’s 3DS2 redirection flow, use the startRedirectionFlow function. This method handles the authentication process via the ACS (Access Control Server) template or post data and provides callbacks for success, failure, or errors..
fun startRedirectionFlow(
activity: Activity,
params: Map<String, Any>,
uiCustomisation: UICustomisation,
callback: PayU3DS2PaymentBaseCallback
)
Parameters
Parameter | Description |
---|---|
activity | Pass the current |
params | A map containing key-value pairs for configuration. Valid keys include: |
| |
| |
| |
| |
| |
uiCustomisation | Customize the bottom sheet UI for the redirection flow. Use the |
callback | Callback interface to receive the payment status: success, failure, or error. |
Sample Code
val params = mapOf(
APIConstants.ACS_TEMPLATE to "<pass acs_templete>",
APIConstants.AUTO_READ to true,
APIConstants.AUTO_SUBMIT to true,
APIConstants.SURL to "<success_url>",
APIConstants.FURL to "<failure_url>"
)
val uiCustomization = UICustomisation()
// Customize uiCustomization as needed
startRedirectionFlow(
activity = this,
params = params,
uiCustomisation = uiCustomization,
callback = object : PayU3DS2PaymentCallback {
override fun onPaymentSuccess() {
// Handle success
}
override fun onPaymentFailure() {
// Handle failure
}
override fun onError(errorCode: Int, errorMessage: String) {
// Handle error
}
override fun onPaymentCancel(isTxnInitiated: Boolean) {
// Handle erro
}
override fun onPaymentCancel(isTxnInitiated: Boolean) {
// Handle erro
}
override fun generateHash(map: HashMap<String, String>,hashGenerationListener: PayUHashGeneratedListener) {
//// Handle Hash
}
}
)
Hash Generation
You will receive a call on the generateHash method of PayU3DS2PaymentCallback.
In the method parameter, you will receive a dictionary or hashMap, and extract the value of hashString from that. Pass that value to the server, and now the server will append salt at the end and generate sha512 hash over it. The server will give that hash back to your app, and the app will provide that hash to PayU through a callback mechanism.
In the map, you have to check for the following keys to generate a hash:
- hashString
- hashName
- postSalt
At the end of that hashString, append your salt and use the SHA-512 algorithm on that final string to generate a hash.
Callout
- If you got postSalt also in the map, first use hash string append salt and then append postSalt value to that string and use SHA-512 algorithm on that final string to generate hash.
- There is no need to know the formula for dynamic hashes because PayU SDK gives you the string containing all the required parameters. Your server has to append salt at the end and generate sha512 hash over it.
Error codes
Code | Description |
---|---|
0 | Success |
1 | Fail |
3 | Challenge timeout |
4 | Challenge protocol error |
5 | Challenge cancelled |
101 | Card bin or card token was empty |
102 | Merchant key null |
103 | Amount not in correct format |
104 | Transaction ID null |
105 | Hash null |
106 | Card not supported on 3DS 2.0 |
107 | Card scheme not supported |
108 | Hash incorrect |
500 | Something went wrong |
504 | Gateway timeout |
Updated 1 day ago