The dLocal Direct SDK allows easy integration with dLocal API to perform card tokenization and also includes card utility methods, like card detection, validation and formatting.
- Requirements
- Installation
- Getting Started
- Tokenize Card
- Brand Detection
- Fields Validation
- Input Formatting
- Card Data Sync
- Report Issues
- License
- Supports API versions from 23 (Android 6.0 Marshmallow) and higher.
- App permission
android.permission.INTERNET
New releases of the dLocal Direct SDK are published via Maven Repository.
The latest version is available via mavenCentral()
.
Add mavenCentral()
to the project level build.gradle file's repositories section, if you don't have it already:
repositories {
mavenCentral()
}
Add dLocal Direct SDK dependency to the application's build.gradle file:
dependencies {
implementation 'com.dlocal.android:dlocal-direct:1.0.1'
}
Create an instance of dLocal Direct passing your API key and the two letter ISO 3166 country code:
import com.dlocal.direct.DLCardTokenizer
val dlocal = DLCardTokenizer(apiKey = "API_KEY", countryCode = "US", testMode = true)
NOTE: To initialize the dLocal Direct instance you will need an API Key. Please contact your Technical Account Manager to obtain one.
We strongly recommend that you use the SANDBOX
environment when testing, and only use PRODUCTION
in production ready builds.
You can specify to use a different environment with the testMode
param, which is false by default, i.e:
import com.dlocal.direct.DLCardTokenizer
val dlocal = if (BuildConfig.DEBUG) {
DLCardTokenizer(apiKey = "SBX API KEY", country = "US", testMode = true)
} else {
DLCardTokenizer(apiKey = "PROD API KEY", country = "US", testMode = false)
}
Replacing the apiKey
with yours for each environment.
See the SampleApplication for a detailed example.
import com.dlocal.direct.DLCardData
import com.dlocal.direct.DLCardTokenizer
val cardData = DLCardData(
holderName = "HOLDER-NAME",
cardNumber = "CARD-NUMBER",
cvv = "CVV",
expirationMonth = 12,
expirationYear = 2025
)
dlocal.tokenizeCard(cardData, onSuccess = { token ->
println("Successfully tokenized card: ${token.value}")
}, onError = { error ->
println(error.debugMessage)
})
import com.dlocal.direct.DLCardTokenizer
dlocal.getBinInformation(
binNumber = "BIN-NUMBER",
onSuccess = { binInfo ->
println("Successfully obtained bin information: ${binInfo.bin}")
}, onError = { error ->
println("Failed to obtain bin information: ${error.debugMessage}")
}
)
import com.dlocal.direct.DLCardTokenizer
dlocal.getBinInformation(
binNumber = "BIN-NUMBER",
currencyCode = "CURRENCY-CODE",
amount = 500.0,
onSuccess = { binInfo ->
binInfo.installments?.let { plan ->
println("Successfully created installments plan: ${plan.id}")
}
},
onError = { error ->
println("Failed to create installments plan: ${error.debugMessage}")
}
)
All interactions are done through one instance of the DLCardExpert
class which you can create as follows:
import com.dlocal.direct.DLCardExpert
val cardExpert = DLCardExpert(countryCode = "UY")
Card data is specific for the country defined at the initialization step of the sdk instance. In below examples we will assume that the sdk was initialized with country code for Uruguay ("UY").
For those countries that we don't have data for, you can still use a global expert as follows:
import com.dlocal.direct.DLCardExpert
val cardExpert = DLCardExpert.global
val supportedBrands = cardExpert.allBrands.map { it.niceName }
println("Globally accepted brands: $supportedBrands")
This global expert contains globally accepted card brands like Visa and Mastercard among others.
cardExpert.allBrands // Use this to access the list of all card brands supported in the country
cardExpert.brand(withIdentifier = DLCardBrandIdentifier.VISA)?.let { visa ->
// Visa is supported in Uruguay
println(visa.identifier) // "visa"
println(visa.niceName) // "Visa"
println(visa.length) // [16]
println(visa.image) // https://static.dlocal.com/fields/input-icons/visa.svg
} ?: run {
println("Visa is not supported in Uruguay")
}
val cardExpert = DLCardExpert(countryCode = "UY")
// Detect card brand from a complete card number
cardExpert.detectBrand(binNumber = "4242 42") // returns [Visa]
// Supports both formatted and raw card numbers
cardExpert.detectBrand(binNumber = "424242") // returns [Visa]
// Detect card brand from an incomplete card number
// In this case multiple card brands start with number "4", in order to find out which card is being entered user will need to enter additional numbers
cardExpert.detectBrand(binNumber = "4") // returns [Visa, Visa DĂ©bito, Maestro]
// Cards that are not supported in Uruguay will return an empty collection
// For example, American Express cards are not supported in Uruguay and "377400111111115" is a valid Amex card number
cardExpert.detectBrand(binNumber = "377400") // returns []
// Invalid values will return an empty collection
cardExpert.detectBrand(binNumber = "HELLO") // returns []
// An empty card number will return all brands supported in this Uruguay
cardExpert.detectBrand(binNumber = "") // returns [Visa, Oca, Mastercard, Diners, Lider, Visa DĂ©bito, Mastercard DĂ©bito, Maestro]
You can access various image URLs for different sizes and resolutions using the cardImage
method in the detected brand:
val brand = cardExpert.detectBrand(binNumber = "4242 4242 4242 4242")
// Image URL of size Small, XHDPI resolution and background
val xhdpiSmall = brand.cardImage(density = DLImageDensity.XHDPI, size = DLImageSize.SMALL, background = true)
// Image URL of size Small, HDPI resolution and no background
val hdpiSmall = brand.cardImage(density = DLImageDensity.HDPI, size = DLImageSize.SMALL, background = false)
// Image URL of size Medium, LDPI resolution and background
val ldpiMedium = brand.cardImage(density = DLImageDensity.LDPI, size = DLImageSize.MEDIUM, background = true)
cardExpert.validateName(holderName = "John Doe") // true
cardExpert.validateName(holderName = "") // false
cardExpert.validateName(holderName = " ") // false
cardExpert.validateName(holderName = "John Doe 2") // false
cardExpert.validateName(holderName = "John Doe!") // false
cardExpert.validateCardNumber(cardNumber = "") // false
cardExpert.validateCardNumber(cardNumber = "4") // false
cardExpert.validateCardNumber(cardNumber = "4242 4242 4242 4242") // true
cardExpert.validateCardNumber(cardNumber = "4242424242424242") // true
cardExpert.validateCardNumber(cardNumber = "4242424242424241") // false (fails luhn check)
cardExpert.validateCardNumber(cardNumber = "4A") // false
cardExpert.validateExpirationDate(expirationDate = "") // false
cardExpert.validateExpirationDate(expirationDate = "0") // false
cardExpert.validateExpirationDate(expirationDate = "02") // false
cardExpert.validateExpirationDate(expirationDate = "02/") // false
cardExpert.validateExpirationDate(expirationDate = "02/2") // false
cardExpert.validateExpirationDate(expirationDate = "02/26") // true
cardExpert.validateExpirationDate(expirationDate = "2/26") // true
cardExpert.validateExpirationDate(expirationDate = "13") // false
cardExpert.validateExpirationDate(expirationDate = "7/22") // false (expired)
cardExpert.validateExpirationDate(expirationDate = "8/2022") // false
The following will validate the security code using standard security code rules:
val brands = cardExpert.detectBrand(binNumber = "4242 42") // returns [Visa]
cardExpert.validateSecurityCode(securityCode = "", brands = brands) // false
cardExpert.validateSecurityCode(securityCode = "1", brands = brands) // false
cardExpert.validateSecurityCode(securityCode = "12", brands = brands) // false
cardExpert.validateSecurityCode(securityCode = "123", brands = brands) // true
cardExpert.validateSecurityCode(securityCode = "1234", brands = brands) // false, security code for Visa cards are always of length three
cardExpert.validateSecurityCode(securityCode = "12A", brands = brands) // false, security codes for Visa cards can only contain numbers
If you want for instance to allow input of security codes for any card that is valid in Uruguay:
val uruguayBrands = cardExpert.allBrands
cardExpert.validateSecurityCode(securityCode = "123", brands = uruguayBrands) // true
cardExpert.validateSecurityCode(securityCode = "1234", brands = uruguayBrands) // false as there is no supported card in Uruguay that allows security code length of four digits
If your brands list is empty or null, the validation will use the default Brand which has a security code of only digits with minimum length of 3 and maximum of 4.
cardExpert.validateSecurityCode(securityCode = "123", brands = null) // true
cardExpert.validateSecurityCode(securityCode = "12", brands = null) // false, minimum length is 3
cardExpert.validateSecurityCode(securityCode = "1234", brands = emptyList()) // true
cardExpert.validateSecurityCode(securityCode = "12345", brands = emptyList()) // false, maximum length is 4
Use this when users are typing in to the card field to facilitate the input of the card.
cardExpert.formatCardNumber("") // returns ""
cardExpert.formatCardNumber("4") // returns "4"
cardExpert.formatCardNumber("42") // returns "42"
cardExpert.formatCardNumber("4242") // returns "4242" (Visa detected at this point)
cardExpert.formatCardNumber("42424") // returns "4242 4"
cardExpert.formatCardNumber("424242") // returns "4242 42"
cardExpert.formatCardNumber("4242424242424242") // returns "4242 4242 4242 4242"
cardExpert.formatCardNumber("42424242424242425") // returns "42424242424242425" (notice that extra "5" entered which makes input match no card brand)
cardExpert.formatCardNumber("42 42424242424242") // returns "4242 4242 4242 4242"
cardExpert.formatCardNumber("42 4242 42 42 4 2424 2") // returns "4242 4242 4242 4242"
If you want to use a specific brand to format the number, you can pass that brand to the function as follows:
val diners = cardExpert.brand(withIdentifier = DLCardBrandIdentifier.DINERS_CLUB)
cardExpert.format(cardNumber = "12345678901234", brand = diners) // returns "1234 567890 1234"
If you want to display a card identified by last numbers, you can use this option as follows:
val diners = cardExpert.brand(withIdentifier = DLCardBrandIdentifier.DINERS_CLUB)
cardExpert.formatCardEndingWith(ending = "1234", brand = diners) // returns "**** ****** 1234"
cardExpert.formatCardEndingWith(ending = "234", brand = diners) // returns "**** ****** *234"
cardExpert.formatCardEndingWith(ending = "34", brand = diners) // returns "**** ****** **34"
cardExpert.formatCardEndingWith(ending = "4", brand = diners) // returns "**** ****** ***4"
cardExpert.formatCardEndingWith(ending = "", brand = diners) // returns "**** ****** ****"
If your form contains a single field for expiration date then this is the function you want to use to format the input. Formatting includes normalization to help users with input of the expiration date, like automatically inserting a "/" to separate month and year.
cardExpert.formatExpirationDate(date = "") // returns ""
cardExpert.formatExpirationDate(date = "1") // returns "1" (expecting a second number to understand whether this is month "01" or "11" or "12")
cardExpert.formatExpirationDate(date = "11") // returns "11/" (automatically inserts "/" character so user only has to enter numbers)
cardExpert.formatExpirationDate(date = "02") // returns "02/" (automatically formats into month "02" and inserts "/")
cardExpert.formatExpirationDate(date = "45") // returns "04/5" (formats "4" into "04")
cardExpert.formatExpirationDate(date = "2/A") // returns "2" (disallows entering any non numeric character)
cardExpert.formatExpirationDate(date = "22") // returns "02/2"
cardExpert.formatExpirationDate(date = "220") // returns "02/20"
cardExpert.formatExpirationDate(date = "2205") // returns "02/20"
cardExpert.formatExpirationDate(date = "1222") // returns "12/22"
If your form has separated fields for month and year, you will want to use these methods for formatting the input. Formatting includes normalization of values to match card formatting rules (e.g. turning "2" into "02") and some validation (e.g. disallow input of non numeric characters, disallow input of more than two characters for months, etc).
cardExpert.formatExpirationMonth(month = "") // returns ""
cardExpert.formatExpirationMonth(month = "0") // returns "0"
cardExpert.formatExpirationMonth(month = "1") // returns "1" (as this could be "01", "11" or "12")
cardExpert.formatExpirationMonth(month = "2") // returns "02"
cardExpert.formatExpirationMonth(month = "3") // returns "03"
cardExpert.formatExpirationMonth(month = "4") // returns "04"
cardExpert.formatExpirationMonth(month = "5") // returns "05"
cardExpert.formatExpirationMonth(month = "6") // returns "06"
cardExpert.formatExpirationMonth(month = "7") // returns "07"
cardExpert.formatExpirationMonth(month = "8") // returns "08"
cardExpert.formatExpirationMonth(month = "9") // returns "09"
cardExpert.formatExpirationMonth(month = "10") // returns "10"
cardExpert.formatExpirationMonth(month = "11") // returns "11"
cardExpert.formatExpirationMonth(month = "12") // returns "12"
cardExpert.formatExpirationMonth(month = "121") // returns "12" (disallows entering a third character)
cardExpert.formatExpirationMonth(month = "13") // returns "01" (this is an invalid month so we take only the first digit)
cardExpert.formatExpirationMonth(month = "1A") // returns "1" (does not allow entering non numeric characters)
cardExpert.formatExpirationMonth(month = "1 ") // returns "1" (does not allow spaces)
cardExpert.formatExpirationYear(year = "") // returns ""
cardExpert.formatExpirationYear(year = "0") // returns "" (does not allow first character to be a zero)
cardExpert.formatExpirationYear(year = "1") // returns "" (does not allow first character to be a one)
cardExpert.formatExpirationYear(year = "2") // returns "2"
cardExpert.formatExpirationYear(year = "20") // returns "20"
cardExpert.formatExpirationYear(year = "202") // returns "202" (allowed as user seems to be constructing four character year)
cardExpert.formatExpirationYear(year = "2022") // returns "22" (shortens length to match standard card expiration year formatting)
cardExpert.formatExpirationYear(year = "20225") // returns "22" (ignores everything coming after the fifth character)
cardExpert.formatExpirationYear(year = "202A") // returns "202" (does not allow entering non numeric characters)
Different card brands have different rules for security code validation. This is why we ask you to input the brand for which you are formatting the code.
val diners = cardExpert.brand(withIdentifier = DLCardBrandIdentifier.DINERS_CLUB)
cardExpert.formatSecurityCode(code = "", brand = diners) // returns ""
cardExpert.formatSecurityCode(code = "1", brand = diners) // returns "1"
cardExpert.formatSecurityCode(code = "12", brand = diners) // returns "12"
cardExpert.formatSecurityCode(code = "123", brand = diners) // returns "123"
cardExpert.formatSecurityCode(code = "1234", brand = diners) // returns "123" (security code limited to 3 characters for this brand)
cardExpert.formatSecurityCode(code = "12 ", brand = diners) // returns "12" (spaces are removed)
cardExpert.formatSecurityCode(code = "12A", brand = diners) // returns "12" (non numeric characters are removed)
If you pass null
brand, the formatter will use the default Brand which has a security code of only digits with minimum length of 3 and maximum of 4.
cardExpert.formatSecurityCode(code = "1A234B56", brand = null) // returns "1234"
cardExpert.formatSecurityCode(code = "00 1 3", brand = null) // returns "0013"
This library comes bundled with a list of supported cards by country. When this list is updated (e.g. we add support for Amex in Uruguay) you'll have to pull the latest version of the library (which comes bundled with this new card) and deploy a new version of your app.
You can use our Sync feature to get OTA updates and bypass this limitation.
// Create an instance of DLCardSync
val cardSync = DLCardSync(apiKey = "API_KEY", countryCode = "US", testMode = true)
// Initiate the syncing process
cardSync.sync { message, finished ->
println(message)
if (finished) {
println("Sync process has been finished")
}
}
We recommend you attach this code previous to using any of the card expert functionalities.
This feature is OFF by default. You'll have to manually initiate the sync process.
If you have a problem or find an issue with the SDK please contact us at mobile-dev@dlocal.com.
MIT License
Copyright (c) 2022 DLOCAL
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.