Android SDK
Installation
Add the following repository to the project settings.gradle
file inside dependencyResolutionManagement
repositories:
maven {
url "https://nexus.i.unit.co/repository/maven/"
}
Add the un-components dependency to your project:
dependencies {
implementation 'co.unit:un-components:0.14.0'
}
Usage
Initialize Unit SDK
Call UnitSdk.init
with your requested environment and your theme.
It is recommended to call it in onCreate
method of your main activity.
You may use the data class UNEnviorment
:
enum UNEnvironment {
sandbox,
production
}
function inputs:
Name | Type | Required | Description |
---|---|---|---|
env | UNEnvironment | YES | Unit environment. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
fonts | UNFonts | NO | UNFonts object that specifies your custom fonts. |
webVersioningStrategy | UNWebVersioningStrategy | NO | Web SDK version management strategy. |
securitySettings | UNSecuritySettings | NO | Security |
Example:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import co.unit.un_components.common.models.UNEnvironment
import co.unit.un_components.api.UnitSdk
const val THEME = "<your json styles url>"
const val LANGUAGE = "<your json language settings url>"
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
UnitSdk.init(UNEnvironment.Sandbox, THEME, LANGUAGE)
}
}
Fonts
Add the Font File to Your Project
- Locate the
.ttf
or.otf
font file you wish to add. - Add them to your
res/font
folder in your Android Studio project. Create the font folder if it doesn't exist. In order to create the assets resource folder: right click on 'res' -> new -> folder -> Assets Folder - Add them to your
assets/fonts
folder.
Note: Android supports only alphanumeric characters and underscores in file names. Therefore, your custom font file name should adhere to this convention and may look like arial_regular.ttf
.
Now, You can configure custom fonts in your application by utilizing the typealias UNFonts
as shown below:
typealias UNFonts = Map<UNFontFamilyName, Array<UNFontData>>
typealias UNFontFamilyName = String
In this map, the keys typically represent the font family names. Corresponding to each font family name, there is an array containing various fonts from that particular family.
UNFontData
Properties
Name | Type | Description |
---|---|---|
fontResId | Int | The resource id of the font |
fontWeight | FontWeight | Enum that defines the weight of the font. |
sources | [UNFontSource] | An array of UNFontSource objects. An array is used to provide fallback options. |
Note:
It is imperative to recognize that the fontResId
should be designated as the primary font to be utilized by our SDK. The array of font sources primarily serves to offer fallback options in scenarios involving WebView usage.
UNFontSource
Properties
Name | Type | Description |
---|---|---|
fileRelativePath | String | The custom font file relative path from the project assets directory. |
format | String? | (Optional) Font file format. Useful for specifying fallback behavior. |
Kotlin Code Example
private val fonts: UNFonts = mapOf(
"Lugrasimo" to arrayOf(
UNFontData(
fontResId = R.font.lugrasimo_regular,
sources = listOf(UNFontSource(fileRelativePath = "fonts/lugrasimo/lugrasimo_regular.ttf")),
fontWeight = FontWeight.Regular,
),
UNFontData(
fontResId = R.font.lugrasimo_bold,
sources = listOf(UNFontSource(fileRelativePath = "fonts/lugrasimo/lugrasimo_bold.ttf")),
fontWeight = FontWeight.Bold,
),
),
"Arial" to arrayOf(
UNFontData(
fontResId = R.font.arial_regular,
sources = listOf(UNFontSource(fileRelativePath = "fonts/arial/arial_regular.ttf")),
fontWeight = FontWeight.Regular,
)
)
)
Web SDK Versioning Strategy Guide
It's essential to understand that this SDK utilizes the web SDK views for certain components.
To give you optimal flexibility in managing the Web SDK versions, we've devised a strategy that allows you to either keep your SDK up-to-date or fixate on a particular version.
To set your preferred versioning approach, utilize the sealed class UNWebVersioningStrategy
. Below are the options you can select from:
Exact Versioning -
data class Exact(val major: Int, val minor: Int, val patch: Int)
- This method allows you to lock onto a specific version that suits your needs.
Up to Next Minor -
data class UpToNextMinor(val major: Int, val minor: Int)
- This is the default and recommended approach. While it keeps your SDK updated with minor patches, a manual update is needed for minor version changes.
Up to Next Major -
data class UpToNextMajor(val major: Int)
- With this strategy, your SDK will automatically update until there's a major version change.
Latest -
object Latest
- This strategy will always keep your SDK updated with the latest version.
For a comprehensive understanding, refer to our Web SDK - Versioning Guide.
val myWebVersioningStrategy = UNWebVersioningStrategy.UpToNextMinor(1, 2)
For the latest SDK, the latest webVersioningStrategy is upToNextMinor(major: 1, minor: 8)
.
Components
To use a component you may call it constructor with your current context: SomeUNComponent(context)
.
Then, configure the component parameters.
Card View
Component name: UNCardView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
cardId | String | YES | Unit card id. |
customerToken | String | YES | A Unit Customer token. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
menuItems | List<UNCardMenuItem> | NO | A list of actions, The menu dynamically adjusts based on the provided list [.Freeze, .AddToWallet, .ManagePin, .Replace, .Report, .Close] . |
hideActionsMenuButton | Boolean | NO | Hide menu button in case value is true. |
hideCardTitle | Boolean | NO | Hide card title in case value is true. |
hideSensitiveDataButton | Boolean | NO | Hide sensitive data button in case value is true. |
learnMoreUrl | String | NO | A “Learn more” URL on the report lost/close card info note. |
onLoad | (onLoadResult: Result<UNCardData>) => Void | NO | Callback Occurs after a component is loaded. |
onStatusChanged | (card: UNCardData) => Void | NO | Callback for card status changes. |
onCardActivated | (card: UNCardData) => Void | NO | Callback for card activated. |
enablePushProvisioning | Boolean | NO | Enables Push Provisioning in case value is true. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.components.UNCardView
import co.unit.un_components.common.models.UNCardData
class CardFragment : Fragment(){
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unCardView = this.context?.let { UNCardView(it) }
unCardView?.configure(
YOUR_CARD_ID,
YOUR_CUSTOMER_TOKEN,
THEME,
onStatusChanged = ::onStatusChanged,
onLoad = ::cardUnitOnLoad,
onCardActivated = ::onCardActivated
)
yourView.addView(unCardView)
// ...
}
private fun onStatusChanged(card: UNCardData) {
println("Card Status is changed. New card data: $card")
}
private fun cardUnitOnLoad(onLoadResult: Result<UNCardData>) {
if(onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
private fun onCardActivated(card: UNCardData) {
println("Card is activated. Card-data: $card")
}
}
Incoming Events:
In some cases, the default menu button won't fit into the design of an application.
By using the openActionsMenu()
method, it's possible to open the card actions bottom sheet from a custom button.
Important Note: one can use the openActionsMenu()
only after UNCardView is loaded. (can be verified by onLoad callback)
Example:
unCardView.openActionsMenu()
It's also possible to create your own menu and call card actions from it.
Use openAction(action: UNCardMenuAction)
method and send inside an action you want to perform.
Use API Docs in order to understand which actions could be applied for any particular card statuses.
Example:
import co.unit.un_components.common.models.UNCardMenuAction
// .......
unCardView.openAction(UNCardMenuAction.Freeze)
You can choose to implement your own show / hide card sensitive data button.
In this case use 'showSensitiveData()' method to show sensitive data and 'hideSensitiveData()' method to hide it.
#### Example:
```kotlin
unCardView.showSensitiveData()
unCardView.hideSensitiveData()
Adding a card to Google Wallet (Optional)
Start by following the Add card to wallet instructions to use this capability.
After that, Pass the enablePushProvisioning
parameter To the Card Component with the value of true
.
Then, you can add a card to Google Wallet using the "Manage Google Wallet" tab in the Card Components's native menu or using an UNCardMenuAction.AddToWallet
Incoming event.
Book Payment View
Component name: UNBookPaymentView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
counterPartyAccountId | String | NO | Unit account id. The account which will receive money. |
counterPartyName | String | NO | Name of the counterparty. This is the name that will be displayed in the Book Payment UI during the transfer. |
isSameCustomer | Boolean | NO | Stating whether both accounts belong to the same customer. Allows fetching additional information about the counterparty account. Default false |
isAutoFocus | Boolean | NO | Auto-focus the money input field once the component is mounted. Default false |
initialStageBackButton | Boolean | NO | An action button at the first stage of the payment flow. Default false |
finalStageDoneButton | Boolean | NO | An action button at the final stage of the payment flow. Default false |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
onInitialStageBackButtonClicked | () => Void | NO | Occurs when the initial stage back button is clicked. |
onFinalStageDoneButtonClicked | () => Void | NO | Occurs when the final stage done button is clicked. |
onPaymentCreated | (data: UNBookPaymentData) => Void | NO | Callback for the created bookPayment. |
onLoad | (onLoadResult: Result<List<UNAccountData>>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.components.UNBookPaymentView
class BookPaymentFragment : Fragment() {
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unBookPaymentView = this.context?.let { UNBookPaymentView(it) }
unBookPaymentView?.configure(
ACCOUNT_ID,
CUSTOMER_TOKEN,
COUNTER_PARTY_ACCOUNT_ID,
COUNTER_PARTY_NAME,
IS_SAME_CUSTOMER,
THEME,
onPaymentCreated = ::onPaymentCreated,
onLoad = ::bookPaymentUnitOnLoad
)
yourView.addView(unBookPaymentView)
// ...
}
private fun onPaymentCreated(data: UNBookPaymentData) {
println("[Book Payment Component] paymentCreatedResponse: $data")
}
private fun bookPaymentUnitOnLoad(onLoadResult: Result<[UNAccountData]>) {
if(onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
Activity View
Component name: UNActivityView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
queryFilter | String | NO | Query for filtering transactions and authorizations: Transactions, Authorizations |
paginationType | UNActivityComponentPaginationType | NO | Defines the method by which additional content is loaded. Possible values: .InfiniteScroll (default), .Pagination |
transactionsPerPage | Int | NO | Number of transactions to fetch on each page or scroll to bottom. Also acts as initial number of transactions to fetch. The default is 8 for pagination and 15 for infinite scroll |
hideFilterButton | Boolean | NO | Hide filter button in case value is true. |
hideBackToTop | Boolean | NO | Hide back to top button in case value is true . Possible values: true / false (default) |
hideTitle | Boolean | NO | Hide title in case value is true |
onLoad | (onLoadResult: Result<UNActivityOnLoadResponse>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.components.UNActivityView
class ActivityFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unActivityView = this.context?.let { UNActivityView(it) }
unActivityView?.configure(
ACCOUNT_ID,
CUSTOMER_TOKEN,
THEME,
onLoad = ::activityUnitOnLoad
)
yourView.addView(unActivityView)
// ...
}
private fun activityUnitOnLoad(onLoadResult: Result<UNActivityOnLoadResponse>) {
if (onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
Incoming Events:
By using the refresh()
method, it's possible to to refresh the component data from a custom button.
Important Note: one can use the refresh()
only after unActivityView
is loaded.
// .......
unActivityView?.refresh()
Account View
Component name: UNAccountView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account to show, when not provided, one of the customer's accounts will be shown. |
customerToken | String | YES | A Unit Customer token. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
menuItems | List<UNAccountMenuItem> | NO | A list of actions, The menu dynamically adjusts based on the provided list [.Details, .Statements, . BankVerification] . |
hideActionsMenuButton | Boolean | NO | Hide actions menu button in case value is true. |
hideSelectionMenuButton | Boolean | NO | Hide selection menu button in case value is true. |
showLeftToSpend | Boolean | NO | Show amount left to spend in case value is true , only relevant for credit accounts. |
onRequestLeftToSpendDetails | (account: UNAccountData) => Void | NO | Callback for left to spend details request. |
onLoad | (onLoadResult: Result<List<UNAccountData>>) => Void | NO | Callback Occurs after a component is loaded. |
onAccountChanged | (account: UNAccountData) => Void | No | Callback for account changes. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.components.UNAccountView
import co.unit.un_components.common.models.UNAccountData
class AccountFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unAccountView = this.context?.let { UNAccountView(it) }
unAccountView?.configure(
ACCOUNT_ID,
CUSTOMER_TOKEN,
THEME,
onLoad = ::accountUnitOnLoad,
onAccountChanged =:: handleAccountChange
)
yourView.addView(unAccountView)
// ...
}
private fun accountUnitOnLoad(onLoadResult: Result<List<UNAccountData>>) {
if(onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
private fun handleAccountChange(account: UNAccountData) {
print("Account Change: $account")
}
}
Incoming Events:
In some cases, the default menu button won't fit into the design of an application.
By using the openActionsMenu()
method, it's possible to open the account menu bottom-sheet from a custom button.
Important Note: one can use the openActionsMenu()
only after unAccountView
is loaded.
Example:
unAccountView?.openActionsMenu()
It's also possible to create your own menu and call account actions from it. Use openAction(action: UNAccountMenuAction) method and send inside an action you want to perform. Use Unit's API Docs in order to understand which actions could be applied.
Example:
import co.unit.un_components.common.models.Account.UNAccountMenuAction
// .......
unAccountView?.openAction(UNAccountMenuAction.OpenAccountDetails)
By using the refresh()
method, it's possible to to refresh the component data from a custom button.
Important Note: one can use the refresh()
only after UNAccountComponent
is loaded.
import co.unit.un_components.common.models.Account.UNAccountMenuAction
// .......
unAccountView?.refresh()
Check Deposit View
Component name: UNCheckDepositView
Prerequirements:
To enable check scanning with our SDK, please request the camera permissions in your application manifest.
<uses-permission android:name="android.permission.CAMERA" />
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | YES | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
fee | Double | NO | Fee changed for making the check deposit, will be presented to the user. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
initialStageBackButton | Boolean | NO | An action button at the first stage of the payment flow. Default false |
finalStageDoneButton | Boolean | NO | An action button at the final stage of the payment flow. Default false |
onInitialStageBackButtonClicked | () => Void | NO | Occurs when the initial stage back button is clicked. |
onFinalStageDoneButtonClicked | () => Void | NO | Occurs when the final stage done button is clicked. |
onDepositCreated | (checkDepositData: UNCheckDepositData) => Void | NO | Occurs when a check deposit is successfully created |
onRestartRequest | (checkDepositData: UNCheckDepositData) => Void | NO | Occurs when "Deposit another check" is clicked |
onLoad | (onLoadResult: Result<UNAccountData>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.components.UNCheckDepositView
class CheckDepositFragment : Fragment() {
private var unCheckDepositView: UNCheckDepositView? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
unCheckDepositView = this.context?.let { UNCheckDepositView(it) }
unCheckDepositView?.configure(
accountId = "424242",
fee = 1.5,
customerToken = "<Your customer token>",
onDepositCreated = { checkData: UNCheckDepositData -> println("Check Deposit Created. Check ID: ${checkData.id}") },
onRestartRequest = { checkData: UNCheckDepositData -> println("Check Deposit Restart Request. Check ID: ${checkData.id}") },
onLoad = { onLoadResult: Result<UNAccountData> -> handleUnitOnLoad(onLoadResult) }
)
val fragmentLayout = inflater.inflate(R.layout.fragment_check_deposit, container, false)
fragmentLayout.findViewById<FrameLayout>(R.id.check_deposit_container).addView(unCheckDepositView)
return fragmentLayout
}
}
ACH Debit Payment View
Prerequirements
This component is using the Plaid API to connect your users' financial accounts using the Plaid integration. You must have a Plaid account and provide your Plaid credentials in the Unit Sandbox Dashboard and Unit Production Dashboard.
Enable camera support (for apps using Identity Verification only)
If your app uses Identity Verification please follow these instructions
Register your app id in Plaid's dashboard
By doing this Plaid will be able to redirect the user back to your app.
- Sign in to the Plaid Dashboard and go to the Team Settings -> API page.
- Next to Allowed Android Package Names click Configure then Add New Android Package Name.
- Enter your package name, for example com.plaid.example.
- Click Save Changes.
Using plaid in your app
- declare a variable to act as your activity result launcher.
private var linkAccountToPlaid: ActivityResultLauncher<LinkTokenConfiguration>? = null
- Implement the configure parameter
openPlaid
, which is required. When you attempt to add a new account, it will be triggered, opening the Plaid Link Activity.
private fun openPlaid(linkTokenConfiguration: LinkTokenConfiguration) {
linkAccountToPlaid?.launch(linkTokenConfiguration)
}
- You need to sign up for the
OpenPlaidLink()
contract in youronCreate
method. Once you have a result, you should submit it to theonPlaidLink(result: LinkResult)
method located inside theUNACHDebitView
.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
linkAccountToPlaid = registerForActivityResult(OpenPlaidLink()) {
unACHDebitView?.onPlaidLink(it)
}
}
Component name: UNACHDebitView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
plaidLinkCustomizationName | String | NO | The name of the Link customization from the Plaid Dashboard to be applied to Link. If not specified, the default customization will be used. When using a Link customization, the language in the customization must match the language selected via the language parameter. |
plaidAccountFilters | List<UNPlaidAccount> | NO | Account subtypes to display in Link based on the provided list [.Checking, .Savings] . If not specified, only checking subtype will be shown. |
fee | Double | NO | Bill your counterparty for his activity. |
isAutoFocus | Boolean | NO | Auto-focus the 'add new recipient' button once the component is mounted. Default false |
sameDay | Boolean | NO | Enables Same Day ACH |
initialStageBackButton | Boolean | NO | An action button at the first stage of the payment flow. Default false |
finalStageDoneButton | Boolean | NO | An action button at the final stage of the payment flow. Default false |
onInitialStageBackButtonClicked | () => Void | NO | Occurs when the initial stage back button is clicked. |
onFinalStageDoneButtonClicked | () => Void | NO | Occurs when the final stage done button is clicked. |
onPaymentCreated | (data: UNACHData) => Void | NO | Callback for the created ACH payment. |
openPlaid | (linkTokenConfiguration: LinkTokenConfiguration) => Void | YES | Callback Occurs when the Plaid Activity should open |
onLoad | (onLoadResult: Result<UNACHDebitOnLoadResponse>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.common.models.UNACHData
import co.unit.un_components.components.UNACHDebitView
import constants.*
import com.plaid.link.OpenPlaidLink
import com.plaid.link.configuration.LinkTokenConfiguration
class ACHDebitFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unACHDebitView = this.context?.let { UNACHDebitView(it) }
private var linkAccountToPlaid: ActivityResultLauncher<LinkTokenConfiguration>? = null
unACHDebitView?.configure(
ACCOUNT_ID,
CUSTOMER_TOKEN,
FEE,
IS_AUTO_FOCUS,
onPaymentCreated = ::onPaymentCreated,
openPlaid = ::openPlaid,
onLoad = ::achDebitUnitOnLoad
)
yourView.addView(unACHDebitView)
// ...
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
linkAccountToPlaid =
registerForActivityResult(OpenPlaidLink()) {
unACHDebitView?.onPlaidLink(it)
}
}
private fun openPlaid(linkTokenConfiguration: LinkTokenConfiguration) {
linkAccountToPlaid?.launch(linkTokenConfiguration)
}
private fun onPaymentCreated(data: UNACHData) {
println("[ACH Debit Component] paymentCreatedResponse: $data")
}
private fun achDebitUnitOnLoad(onLoadResult: Result<UNACHDebitOnLoadResponse>) {
if(onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
}
ACH Credit Payment View
Prerequirements:
If you wish to use plaid in this component, please follow the pre-requisites of [ACHDebitComponent]{/android-sdk#ach-debit-component}.
Component name: UNACHCreditView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
theme | String | NO | A URL that specifies the UI configuration. |
language | String | NO | A URL that specifies the language configuration. |
plaidLinkCustomizationName | String | NO | The name of the Link customization from the Plaid Dashboard to be applied to Link. If not specified, the default customization will be used. When using a Link customization, the language in the customization must match the language selected via the language parameter. |
plaidAccountFilters | List<UNPlaidAccountFilter> | NO | Account subtypes to display in Link based on the provided list [.Checking, .Savings] . If not specified, only checking subtype will be shown. |
fee | Double | NO | Bill your counterparty for his activity. |
isAutoFocus | Boolean | NO | Auto-focus the 'add new recipient' button once the component is mounted. Default "false" |
sameDay | Boolean | NO | Enables Same Day ACH |
initialStageBackButton | Boolean | NO | An action button at the first stage of the payment flow. Default false |
finalStageDoneButton | Boolean | NO | An action button at the final stage of the payment flow. Default false |
onInitialStageBackButtonClicked | () => Void | NO | Occurs when the initial stage back button is clicked. |
onFinalStageDoneButtonClicked | () => Void | NO | Occurs when the final stage done button is clicked. |
onPaymentCreated | (data: UNACHData) => Void | NO | Callback for the created ACH payment. |
openPlaid | (linkTokenConfiguration: LinkTokenConfiguration) => Void | NO | Callback Occurs when the Plaid Activity should open |
onLoad | (onLoadResult: Result<UNACHCreditOnLoadResponse>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.common.models.UNACHData
import co.unit.un_components.components.UNACHCreditView
import constants.*
class ACHCreditFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val unACHCreditView = this.context?.let { UNACHCreditView(it) }
unACHCreditView?.configure(
ACCOUNT_ID,
CUSTOMER_TOKEN,
FEE,
IS_AUTO_FOCUS,
onPaymentCreated = ::onPaymentCreated,
onLoad = ::achCreditUnitOnLoad
)
yourView.addView(unACHCreditView)
// ...
}
private fun onPaymentCreated(data: UNACHData) {
println("[ACH Credit Component] paymentCreatedResponse: $data")
}
private fun achCreditUnitOnLoad(onLoadResult: Result<UNACHCreditOnLoadResponse>) {
if(onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
}
Multiple Cards View
Component name: UNMultipleCardsView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
customerToken | String | YES | A Unit Customer token. |
language | String | NO | A URL that specifies the language configuration. |
theme | String | NO | A URL that specifies the UI configuration. |
paginationType | UNMultipleCardsComponentPaginationType | NO | Defines how more content is loaded. Possible values: .InfiniteScroll (default), .Pagination |
cardsPerPage | Int | NO | Number of cards to fetch on each page or scroll to bottom. Also acts as initial number of cards to fetch. The default is 8 for pagination and 15 for infinite scroll |
disableCardClick | Boolean | NO | When true, will not publish a unitMultipleCardsCardClicked event when a row is clicked. Possible values: true / false (default) |
queryFilter | String | NO | Query for filtering cards: Cards |
hideTitle | Boolean | NO | Hide the component title in case value is 'true'. Possible values: true / false (default) |
hideBackToTop | Boolean | NO | Hide back to top button in case value is true . Possible values: true / false (default) |
onLoad | (onLoadResult: Result<List<UNCardData>>) => Void | NO | Callback Occurs after a component is loaded. |
onCardClicked | (card: UNCardData) => Void | NO | Callback Occurs when a card row is clicked |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.common.models.UNError
import co.unit.un_components.common.models.multipleCards.UNMultipleCardsComponentPaginationType
import co.unit.un_components.components.UNMultipleCardsView
class MultipleCardsFragment : Fragment() {
private var unMultipleCardsView: UNMultipleCardsView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
unMultipleCardsView = this.context?.let { UNMultipleCardsView(it) }
unMultipleCardsView?.configure(
customerToken = CUSTOMER_TOKEN,
onLoad = ::multipleCardsUnitOnLoad,
paginationType = UNMultipleCardsComponentPaginationType.Pagination,
)
yourView.addView(unMultipleCardsView)
// ...
}
private fun multipleCardsUnitOnLoad(onLoadResult: Result<List<UNCardData>>) {
if (onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
}
Program Details View
Component name: UNProgramDetailsView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | YES | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
language | String | NO | A URL that specifies the language configuration. |
theme | String | NO | A URL that specifies the UI configuration. |
hideTitle | Boolean | NO | Hide the component title in case value is 'true'. Possible values: true / false (default) |
onLoad | (onLoadResult: Result<UNAccountData>) => Void | NO | Callback Occurs after a component is loaded. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.common.models.UNError
import co.unit.un_components.components.UNProgramDetailsView
class ProgramDetailsFragment : Fragment() {
private var unProgramDetailsComponent: UNProgramDetailsView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
unProgramDetailsComponent = this.context?.let { UNProgramDetailsView(it) }
unProgramDetailsComponent?.configure(
accountId = ACCOUNT_ID,
customerToken = CUSTOMER_TOKEN,
hideTitle = false,
onLoad = ::programDetailsUnitOnLoad
)
yourView.addView(unProgramDetailsComponent)
// ....
}
private fun programDetailsUnitOnLoad(onLoadResult: Result<UNAccountData>) {
if (onLoadResult.isSuccess) {
println(onLoadResult.getOrNull())
} else {
when (val exception = onLoadResult.exceptionOrNull()) {
is UNError.OnLoad -> {
println(exception)
}
}
}
}
}
Wire Payment View
Wire payments are not enabled by default, and are subject to a minimum payment amount that is determined by the partner bank. See docs about Wires.
The component is performing MFA on every wire payment, the customer-token that is being used when using the component must have the upgradableScope
attribute with the wire-payments-write
scope.
An example of creating a token:
{
"data": {
"type": "customerToken",
"attributes": {
"scope": "payments accounts",
"upgradableScope": "wire-payments-write",
"verificationToken": "i8FWKLBjXEg3TdeK93G3K9PKLzhbT6CRhn/VKkTsm....",
"verificationCode": "203130"
}
}
}
Component name: UNWirePaymentView
configure parameters:
Name | Type | Required | Description |
---|---|---|---|
accountId | String | NO | Unit account id. The account from which money is being sent. |
customerToken | String | YES | A Unit Customer token. |
fee | Double | NO | Fee changed for making the wire payment, will be presented to the user. |
isAutoFocus | Boolean | NO | Auto-focus the money input field once the component is mounted. Default false |
initialStageBackButton | Boolean | NO | An action button at the first stage of the payment flow. Default false |
finalStageDoneButton | Boolean | NO | An action button at the final stage of the payment flow. Default false |
language | String | NO | A URL that specifies the language configuration. |
theme | String | NO | A URL that specifies the UI configuration. |
onLoad | (onLoadResult: Result<List<UNAccountData>>) => Void | NO | Callback Occurs after a component is loaded. |
onPaymentCreated | (data: UNWirePaymentData) => Void | NO | Callback for the created wirePayment. |
onInitialStageBackButtonClicked | () => Void | NO | Occurs when the initial stage back button is clicked. |
onFinalStageDoneButtonClicked | () => Void | NO | Occurs when the final stage done button is clicked. |
Example:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.fragment.app.Fragment
import co.unit.un_components.common.models.UNError
import co.unit.un_components.components.UNProgramDetailsView
class WirePaymentFragment : Fragment() {
private var unWirePaymentView: UNWirePaymentView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
unWirePaymentView = this.context?.let { UNWirePaymentView(it) }
unWirePaymentView?.configure(
customerToken = CUSTOMER_TOKEN,
accountId = ACCOUNT_ID,
isAutoFocus = true,
initialStageBackButton = true,
finalStageDoneButton = true,
onPaymentCreated = ::onPaymentCreated,
onInitialStageBackButtonClicked = ::onInitialStageBackButtonClicked,
onFinalStageDoneButtonClicked = ::onFinalStageDoneButtonClicked,
onLoad = { onLoadResult: Result<List<UNAccountData>> -> handleUnitOnLoad(onLoadResult) },
)
val fragmentLayout = inflater.inflate(R.layout.wire_payment, container, false)
fragmentLayout.findViewById<FrameLayout>(R.id.wire_payment_container).addView(unWirePaymentView)
return fragmentLayout
}
private fun onPaymentCreated(data: UNWirePaymentData) {
println("[Wire Payment Component] paymentCreatedResponse: $data")
}
private fun onInitialStageBackButtonClicked() {
println("[Wire Payment Component] onInitialStageBackButtonClicked")
findNavController().popBackStack()
}
private fun onFinalStageDoneButtonClicked() {
println("[Wire Payment Component] onFinalStageDoneButtonClicked")
findNavController().popBackStack()
}
}
Flows
Add card to wallet flow
Start by following the Add card to wallet instructions to use this flow.
After that, you can add a card to Google Wallet by calling the Unit SDK's startPushProvisioningFlow(context: Context, cardId: String, customerToken: String)
method.
Example:
...
import co.unit.un_components.api.UnitSdk
...
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
UnitSdk.ui.flows.startPushProvisioning(context, YOUR_CARD_ID, YOUR_CUSTOMER_TOKEN)
...
}
}
Security
UNSecuritySettingsBuilder
is designed to help you configure your security settings.
You can define your custom security settings using the builder. Then, apply these settings by passing them as a parameter to the SDK's init function.
Snapshot Protection
UNSnapshotProtectionStrategy
defines how to protect your app's content from being captured in snapshots. The default strategy is FullProtection
.
The enum offers three strategies:
- Full Protection - Protects the entire activity's views from being captured in snapshots.
- None - no protection applied (likely used if you implement your own protection).
Example:
val securitySettings = UNSecuritySettingsBuilder()
.snapshotProtectionStrategy(UNSnapshotProtectionStrategy.FullProtection)
Additional Capabilities
Add card to wallet
This capability can be used with the Card Component or without it, as a separate flow.
Prerequirements
Complete the following steps at Steps to support Google Pay:
- Google Pay Prerequirements.
- Step 1 (UX/branding review) with screenshots that you can get from Unit.
- Step 2 (Push Provisioning API access).
Steps to integrate with the Visa SDK
Complete the following steps at integration steps:
- Prerequirements.
- Add VDE (Visa Digital Enablement) SDK to your project (steps 1,2 and 3).
Add to Wallet flow
You can find an overview of the add to wallet flow in this link.
Create a Kotlin
class named UNVisaProvider
. This class will be responsible to communicate and pass data from the Unit SDK and the VDE(Visa Digital Enablement) SDK through your app.
UNVisaProvider code
import android.content.Context
import androidx.lifecycle.ProcessLifecycleOwner
import co.unit.un_components.common.interfaces.UNVisaProviding
import co.unit.un_components.common.models.card.addToWallet.*
import com.visa.mobileEnablement.inAppCore.VisaInAppConfig
import com.visa.mobileEnablement.inAppCore.VisaInAppCore
import com.visa.mobileEnablement.inAppCore.VisaInAppCoreApplicationObserver
import com.visa.mobileEnablement.inAppCore.VisaInAppEnvironment
import com.visa.mobileEnablement.pushProvisioning.*
class UNVisaProvider(appId: String, env: VisaInAppEnvironment, context: Context):
UNVisaProviding, VisaPushProvisioningListener {
private var applicationContext: Context
private var pushProvisioningInterface: VisaPushProvisioningInterface? = null
private var launchInitializeCallback: ((signedNonce: UNVPResult<String>) -> Unit)? = null
private var launchGetWalletsCallback: ((wallets: UNVPResult<List<UNVisaWallet>>) -> Unit)? = null
private var launchStartCardProvisioningCallback: ((result: UNVPResult<UNVisaProvisionStatus>) -> Unit)? = null
init {
/* Configure Visa's SDK */
applicationContext = context.applicationContext
val visaInAppConfig = VisaInAppConfig(env, appId)
VisaInAppCore.configure(applicationContext, visaInAppConfig)
ProcessLifecycleOwner.get().lifecycle.addObserver(VisaInAppCoreApplicationObserver())
}
override fun launchInitialize(
launchInitializeCallback: (signedNonce: UNVPResult<String>) -> Unit,
) {
this.launchInitializeCallback = launchInitializeCallback
/* Initialize Visa's SDK */
pushProvisioningInterface = VisaPushProvisioningInterfaceFactory
.createPushProvisioningInterface(this)
pushProvisioningInterface?.initialize()
}
override fun launchGetWallets(
encPayload: String,
launchGetWalletsCallback: (wallets: UNVPResult<List<UNVisaWallet>>) -> Unit
) {
this.launchGetWalletsCallback = launchGetWalletsCallback
/* Get Wallets using Visa's SDK */
val supportedWalletsRequest = VPSupportedWalletRequest(encPayload)
pushProvisioningInterface?.getSupportedWallets(supportedWalletsRequest)
}
override fun launchStartCardProvisioning(
walletCode: String,
walletName: String,
launchStartCardProvisioningCallback: (result: UNVPResult<UNVisaProvisionStatus>) -> Unit
) {
this.launchStartCardProvisioningCallback = launchStartCardProvisioningCallback
/* Start Card Provisioning */
val vpCardProvisioningRequest = VPCardProvisioningRequest(enumValueOf(walletCode), walletName)
pushProvisioningInterface?.startCardProvisioning(applicationContext, vpCardProvisioningRequest)
}
/* VisaPushProvisioningListener callbacks */
override fun initializationFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
launchInitializeCallback?.let {
it(UNVPResult.Error(UNVPError(error.code, error.description, error.type.toUNVPErrorType(), error.correlationId)))
}
}
override fun initializationSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPInitResponse
) {
launchInitializeCallback?.let {
it(UNVPResult.Success(response.signedNonce))
}
}
override fun supportedWalletFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
launchGetWalletsCallback?.let {
it(UNVPResult.Error(UNVPError(error.code, error.description, error.type.toUNVPErrorType(), error.correlationId)))
}
}
override fun supportedWalletSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPSupportedWalletResponse
) {
launchGetWalletsCallback?.let {
it(UNVPResult.Success(response.wallets.map { it.toUNVPSupportedWallet() }))
}
}
override fun cardProvisioningFailure(
pushProvisioningInterface: VisaPushProvisioningInterface,
error: VPError
) {
launchStartCardProvisioningCallback?.let {
it(UNVPResult.Error(UNVPError(error.code, error.description, error.type.toUNVPErrorType(), error.correlationId)))
}
}
override fun cardProvisioningSuccess(
pushProvisioningInterface: VisaPushProvisioningInterface,
response: VPCardProvisioningResponse
) {
launchStartCardProvisioningCallback?.let {
it(UNVPResult.Success(response.walletStatus.toUNVPProvisionStatus()))
}
}
}
fun VPErrorType.toUNVPErrorType(): UNVPErrorType {
return when(this) {
VPErrorType.EmptyAppId -> UNVPErrorType.EmptyAppId
VPErrorType.DeviceAuthenticationFailed -> UNVPErrorType.DeviceAuthenticationFailed
VPErrorType.DeviceRootDetection -> UNVPErrorType.DeviceRootDetection
VPErrorType.HookDetection -> UNVPErrorType.HookDetection
VPErrorType.PlayIntegrityCheckFailed -> UNVPErrorType.PlayIntegrityCheckFailed
VPErrorType.EmptyEncryptedPayload -> UNVPErrorType.EmptyEncryptedPayload
VPErrorType.NoWallets -> UNVPErrorType.NoWallets
VPErrorType.InvalidCardId -> UNVPErrorType.InvalidCardId
VPErrorType.ProvisioningNotAllowed -> UNVPErrorType.ProvisioningNotAllowed
VPErrorType.CancelledByUser -> UNVPErrorType.CancelledByUser
VPErrorType.GoogleWalletCreationFailed -> UNVPErrorType.GoogleWalletCreationFailed
VPErrorType.UnknownErrorOccured -> UNVPErrorType.UnknownErrorOccured
VPErrorType.WalletProvisioningInconclusive -> UNVPErrorType.WalletProvisioningInconclusive
VPErrorType.WalletProvisioningError -> UNVPErrorType.WalletProvisioningError
VPErrorType.NetworkFailure -> UNVPErrorType.NetworkFailure
VPErrorType.SessionExpired -> UNVPErrorType.SessionExpired
VPErrorType.InvalidInfo -> UNVPErrorType.InvalidInfo
VPErrorType.PayloadDecryptionFailed -> UNVPErrorType.PayloadDecryptionFailed
VPErrorType.ApiError -> UNVPErrorType.ApiError
VPErrorType.InvalidNonce -> UNVPErrorType.InvalidNonce
VPErrorType.SDKLockout -> UNVPErrorType.SDKLockout
VPErrorType.NoBrowserFound -> UNVPErrorType.NoBrowserFound
VPErrorType.TLCMFeatureNotSupported -> UNVPErrorType.TLCMFeatureNotSupported
VPErrorType.TLCMUnsupportedWallet -> UNVPErrorType.TLCMUnsupportedWallet
VPErrorType.TLCMDetailsNotFoundInPaySdk -> UNVPErrorType.TLCMDetailsNotFoundInPaySdk
VPErrorType.TLCMTokenStatusExists -> UNVPErrorType.TLCMTokenStatusExists
VPErrorType.TLCMInvalidOperationInput -> UNVPErrorType.TLCMInvalidOperationInput
VPErrorType.TLCMInvalidRequest -> UNVPErrorType.TLCMInvalidRequest
VPErrorType.TLCMUpdateStatusNotAllowed -> UNVPErrorType.TLCMUpdateStatusNotAllowed
VPErrorType.TLCMTokenNotFound -> UNVPErrorType.TLCMTokenNotFound
VPErrorType.VerificationNotAllowed -> UNVPErrorType.VerificationNotAllowed
VPErrorType.PaymentMethodRemoved -> UNVPErrorType.PaymentMethodRemoved
VPErrorType.SdkInternalError -> UNVPErrorType.SdkInternalError
}
}
fun VPSupportedWallet.toUNVPSupportedWallet(): UNVisaWallet {
return UNVisaWallet(
code = this.code.toUNVisaSupportedWalletCode(),
name = this.name,
reason = this.reason?.reason,
status = this.status.toUNVPProvisionStatus()
)
}
fun VPSupportedWalletCode.toUNVisaSupportedWalletCode(): UNVisaSupportedWalletCode {
return when(this) {
VPSupportedWalletCode.GooglePayPushProvision -> UNVisaSupportedWalletCode.GooglePayPushProvision
VPSupportedWalletCode.SamsungPayPushProvision -> UNVisaSupportedWalletCode.SamsungPayPushProvision
VPSupportedWalletCode.VCEHPushProvision -> UNVisaSupportedWalletCode.VCEHPushProvision
}
}
fun VPProvisionStatus.toUNVPProvisionStatus(): UNVisaProvisionStatus {
return when(this) {
VPProvisionStatus.ReadyToProvision -> UNVisaProvisionStatus.ReadyToProvision
VPProvisionStatus.AlreadyProvisioned -> UNVisaProvisionStatus.AlreadyProvisioned
VPProvisionStatus.NotInstalled -> UNVisaProvisionStatus.NotInstalled
VPProvisionStatus.NotAvailable -> UNVisaProvisionStatus.NotAvailable
VPProvisionStatus.WalletSetupNotCompleted -> UNVisaProvisionStatus.WalletSetupNotCompleted
VPProvisionStatus.WalletAppUpdateAvailable -> UNVisaProvisionStatus.WalletAppUpdateAvailable
VPProvisionStatus.AlreadyProvisionedAndSetAsDefault -> UNVisaProvisionStatus.AlreadyProvisionedAndSetAsDefault
}
}
Set Unit SDK with the UNVisaProvider
Call UnitSdk.setVisaProvider
with a UNVisaProvider
instance. It is recommended to call it in the onCreate
method of your main activity. You will need to make sure you're initializing the UnitSdk
with UNEnvironment.Production
environment prior to this step. Make sure you pass the UNVisaProvider
your Visa's App ID, VisaInAppEnvironment.Production
and your app's context
.
Example:
import com.visa.mobileEnablement.inAppCore.VisaInAppEnvironment
import co.unit.un_components.api.UnitSdk
import co.unit.un_components.common.models.UNEnvironment
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
UnitSdk.setVisaProvider(UNVisaProvider(YOUR_APP_ID, VisaInAppEnvironment.Production, this))
...
}
}
After app development remaining steps
When you are done with the integration, complete the following steps.
Error Handling
By using unitOnLoad callback you can get a result of type Kotlin.Result for the requested component. On error - you will get a sealed class UNError that consist of several cases.
UNError:
Case | fields | Description |
---|---|---|
OnLoad | errors: ArrayList<UNErrorResponseObject> | Report UNErrorResponse on errors that happen during loading time |
UNErrorResponseObject:
Name | Type | Description |
---|---|---|
status | String | A Unit-specific code, uniquely identifying the error type. |
title | String | The title of the error type. |
detail | String? | Additional information about the error. |
details | String? | Additional information about the error. |
meta | Any? | Identifier to be used for further investigation. |
Note: An UNErrorResponseObject
MUST contain a title and the HTTP status code members.