Advanced Features Android SDK
Advanced configuration and custom integrations for Android.
Lite SDK Flow
For custom payment method selection, use startPaymentLite() instead of startPayment():
import com.yuno.sdk.payments.startCheckout
import com.yuno.sdk.payments.startPaymentLite
import com.yuno.sdk.payments.updateCheckoutSession
import com.yuno.presentation.core.components.PaymentSelected
class CheckoutLiteActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_checkout_lite)
// Initialize checkout with session and country
startCheckout(
checkoutSession = "your_checkout_session_id",
countryCode = "CO",
callbackPaymentState = ::onPaymentStateChange
)
}
private fun startPaymentWithSelectedMethod(
paymentMethodType: String,
vaultedToken: String? = null
) {
startPaymentLite(
paymentSelected = PaymentSelected(
paymentMethodType = paymentMethodType,
vaultedToken = vaultedToken
),
callBackTokenWithInformation = { ottModel ->
// Optional: Receive additional token information
Log.i("OTT Info", ottModel.toString())
},
callbackOTT = ::onTokenReceived,
showPaymentStatus = true
)
}
private fun onTokenReceived(token: String?) {
token?.let {
// Send token to your backend to create payment
createPaymentOnBackend(it)
}
}
private fun onPaymentStateChange(paymentState: String?, paymentSubState: String?) {
when (paymentState) {
"SUCCEEDED" -> navigateToSuccess()
"FAIL" -> showError("Payment failed")
"PROCESSING" -> showPendingMessage()
"REJECT" -> showError("Payment rejected")
"INTERNAL_ERROR" -> showError("An error occurred")
"CANCELED" -> showCanceledMessage()
}
// paymentSubState provides additional details about the payment status
}
}PaymentSelected Data Class
PaymentSelected(
paymentMethodType: String, // Payment type: "CARD", "PIX", etc.
vaultedToken: String? // Optional: Saved card token for vaulted payments
)Keep Loader Flow
To keep Yuno's loading screen until payment is created, use keepLoader = true in YunoConfig and startCompletePaymentFlow():
// Step 1: Initialize with keepLoader = true
class CustomApplication : Application() {
override fun onCreate() {
super.onCreate()
Yuno.initialize(
this,
"your-api-key",
YunoConfig(keepLoader = true)
)
}
}
// Step 2: Use startCompletePaymentFlow instead of startPayment
class PaymentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startCheckout(
callbackPaymentState = ::onPaymentStateChange
)
updateCheckoutSession(
checkoutSession = "your_checkout_session_id",
countryCode = "CO"
)
}
private fun processPayment() {
startCompletePaymentFlow(
paymentSelected = null, // null for Full SDK, PaymentSelected for Lite
showPaymentStatus = true,
createPaymentFun = { ott ->
// This suspend function keeps the loader until you complete
createPaymentOnBackend(ott)
},
callbackPaymentState = ::onPaymentStateChange,
callbackOTT = ::onTokenReceived
)
}
private suspend fun createPaymentOnBackend(ott: String) {
// Call your backend API to create payment
// Loader stays visible until this function completes
apiClient.createPayment(ott)
}
private fun onPaymentStateChange(paymentState: String?, paymentSubState: String?) {
// Handle payment state changes
// paymentSubState provides additional details about the payment status
}
}Enrollment (Save Cards)
Initialize Enrollment
You must call initEnrollment() in the onCreate() method of your activity to register the callback:
import com.yuno.sdk.enrollment.initEnrollment
import com.yuno.sdk.enrollment.startEnrollment
class EnrollmentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_enrollment)
// Must be called in onCreate to register callback
initEnrollment(::onEnrollmentStateChange)
}
private fun startEnrollmentFlow() {
startEnrollment(
customerSession = "your_customer_session_id",
countryCode = "CO",
showEnrollmentStatus = true
)
}
private fun onEnrollmentStateChange(enrollmentState: String?) {
enrollmentState?.let {
Log.d("Enrollment", "State: $it")
// Handle enrollment result
}
}
}Check Enrollment Status
To check a previous enrollment status without starting a new enrollment:
import com.yuno.sdk.enrollment.initEnrollment
import com.yuno.sdk.enrollment.enrollmentStatus
class EnrollmentStatusActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Must be called in onCreate
initEnrollment(::onEnrollmentStateChange)
}
private fun checkStatus() {
// This does NOT start enrollment, only checks status
enrollmentStatus(
customerSession = "your_customer_session_id",
countryCode = "CO",
showEnrollmentStatus = false,
callbackEnrollmentState = ::onEnrollmentStateChange
)
}
private fun onEnrollmentStateChange(state: String?) {
// Handle enrollment status
}
}Important:
enrollmentStatus()is read-only and does NOT start a new enrollment process. UsestartEnrollment()to begin a new enrollment.
Vaulted Token Payments
Pay with a saved card using the vaulted token:
private fun payWithSavedCard(vaultedToken: String) {
startPaymentLite(
paymentSelected = PaymentSelected(
paymentMethodType = "CARD",
vaultedToken = vaultedToken
),
callbackOTT = ::onTokenReceived,
showPaymentStatus = true
)
}Payment Methods Views
Full SDK - PaymentMethodListView
Display all available payment methods:
<com.yuno.payments.features.payment.ui.views.PaymentMethodListView
android:id="@+id/list_payment_methods"
android:layout_width="match_parent"
android:layout_height="wrap_content" />Compose Integration
import com.yuno.presentation.core.components.PaymentMethodListViewComponent
composeView.setContent {
PaymentMethodListViewComponent(
activity = this@PaymentActivity,
onPaymentSelected = { isSelected ->
payButton.isEnabled = isSelected
}
)
}Enrollment Methods Views
Show only enrollment-available payment methods:
<com.yuno.payments.features.enrollment.ui.views.EnrollmentMethodsListView
android:id="@+id/list_enrollment_methods"
android:layout_width="match_parent"
android:layout_height="wrap_content" />Show only enrolled payment methods:
<com.yuno.payments.features.enrollment.ui.views.EnrollmentPaymentMethodListView
android:id="@+id/list_enrolled_methods"
android:layout_width="match_parent"
android:layout_height="wrap_content" />Show only unenrolled payment methods:
<com.yuno.payments.features.enrollment.ui.views.UnEnrolledPaymentMethodListView
android:id="@+id/list_unenrolled_methods"
android:layout_width="match_parent"
android:layout_height="wrap_content" />Custom Card Form (Secure Fields)
Build custom card forms while maintaining PCI compliance using Yuno Secure Fields.
Step 1: Create Custom Layout
Create screen_payment_card_form.xml to override the default card form:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<!-- Close Button -->
<ImageView
android:id="@+id/imageView_close"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_close" />
<!-- Card Number Field -->
<com.yuno.payments.features.base.ui.views.CardNumberEditText
android:id="@+id/textField_number"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Expiration Date and CVV -->
<com.yuno.payments.features.base.ui.views.CardDataStackView
android:id="@+id/cardDataStackView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Voucher Card Type Copy -->
<TextView
android:id="@+id/textView_voucher_copy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<!-- Card Holder Name -->
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Document Type Spinner -->
<com.yuno.payments.features.base.ui.views.SpinnerFieldItemView
android:id="@+id/spinner_document_type"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Document Number -->
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_user_document"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- Phone Information (hidden by default) -->
<com.yuno.payments.features.base.ui.views.PhoneInformationView
android:id="@+id/layout_phone_information"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<!-- Installments -->
<LinearLayout
android:id="@+id/container_installments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.yuno.payments.features.base.ui.views.SpinnerFieldItemView
android:id="@+id/spinner_installments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.facebook.shimmer.ShimmerFrameLayout
android:id="@+id/shimmer_installments"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<include layout="@layout/shimmer_component_field" />
</com.facebook.shimmer.ShimmerFrameLayout>
</LinearLayout>
<!-- Address Fields (hidden by default) -->
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.yuno.payments.features.base.ui.views.TextFieldItemView
android:id="@+id/textField_zip_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.yuno.payments.features.base.ui.views.SpinnerFieldItemView
android:id="@+id/spinner_country"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<com.yuno.payments.features.base.ui.views.SpinnerFieldItemView
android:id="@+id/spinner_gender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<!-- Credit/Debit Switch -->
<com.yuno.payments.features.base.ui.views.CustomYunoSwitch
android:id="@+id/switch_cardType"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<ImageView
android:id="@+id/switch_tooltip"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/ic_thin_info"
android:visibility="gone" />
<!-- Save Card Checkbox -->
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/checkBox_save_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save card for future purchases" />
<!-- Secure Payment Text -->
<TextView
android:id="@+id/textView_secure_payment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Secured by Yuno" />
<!-- Submit Button -->
<Button
android:id="@+id/button_complete_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pay" />
</LinearLayout>
</ScrollView>Note: For installments shimmer, add dependency:
implementation 'com.facebook.shimmer:shimmer:0.5.0'
Error Handling
Handle errors in payment state callback:
private fun onPaymentStateChange(paymentState: String?, paymentSubState: String?) {
when (paymentState) {
"SUCCEEDED" -> {
// Payment successful
navigateToSuccess()
}
"FAIL" -> {
// Payment failed - show error to user
showError("Payment failed. Please try again.")
}
"REJECT" -> {
// Payment rejected by processor
showError("Payment was rejected. Please try a different payment method.")
}
"INTERNAL_ERROR" -> {
// Internal SDK error
showError("An error occurred. Please try again.")
}
"PROCESSING" -> {
// Payment is being processed
showPendingMessage("Your payment is being processed.")
}
"CANCELED" -> {
// User canceled the payment
Toast.makeText(this, "Payment canceled", Toast.LENGTH_SHORT).show()
}
}
// paymentSubState provides additional details about the payment status
}Show/Hide Payment Status Screens
Control whether Yuno shows its built-in status screens:
// Show Yuno's status screens (default)
startPayment(
callbackOTT = ::onTokenReceived,
showPaymentStatus = true
)
// Hide Yuno's status screens and handle in your app
startPayment(
callbackOTT = ::onTokenReceived,
showPaymentStatus = false
)
// Same for continuePayment
continuePayment(
showPaymentStatus = false,
callbackPaymentState = ::onPaymentStateChange
)Anti-Fraud Session ID
Pass a merchant session ID for anti-fraud purposes:
startCheckout(
checkoutSession = "your_checkout_session_id",
countryCode = "CO",
callbackOTT = ::onTokenReceived,
callbackPaymentState = ::onPaymentStateChange,
merchantSessionId = "your_antifraud_session_id"
)External Browser Return (Deep Links)
Handle users returning to your app after external payment flows like 3DS authentication.
1. Set callback_url in Checkout Session
Include callback_url when creating the checkout session on your backend:
{
"callback_url": "myapp://payment/return"
}2. Configure Deep Link Handling
Add an intent-filter to your activity in AndroidManifest.xml:
<activity android:name=".PaymentActivity">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="payment"
android:path="/return" />
</intent-filter>
</activity>3. Handle Return Intent
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize checkout
startCheckout(
callbackPaymentState = ::onPaymentStateChange
)
// Check if app was opened via deep link
intent.data?.let { uri ->
if (uri.toString().contains("myapp://payment/return")) {
handlePaymentReturn(uri)
}
}
}
private fun handlePaymentReturn(uri: Uri) {
// Continue payment after returning from external browser
continuePayment(
showPaymentStatus = true,
callbackPaymentState = ::onPaymentStateChange
)
}
private fun onPaymentStateChange(paymentState: String?, paymentSubState: String?) {
// Handle payment state changes
// paymentSubState provides additional details about the payment status
}Best Practices
- Always call
startCheckout()inonCreate()before any other SDK methods - Always call
initEnrollment()inonCreate()before starting enrollment - Handle all payment states in your callback
- Use
showPaymentStatus = falseif you want to show custom status screens - Test deep link handling on multiple devices and Android versions
- Include
callback_urlin payment flows that may redirect to external browsers
Updated about 20 hours ago