Skip to main content

Terminal Gateway SDK for Android (Kotlin)

The Xendit’s Payment Terminal Gateway Android SDK enables you to integrate secure payment processing into your Android applications. Connect to physical payment terminals and process transactions seamlessly using our Kotlin-based SDK.
This SDK works alongside the Terminal API to provide complete in-person payment functionality. You’ll use the Terminal API to create payment sessions and this SDK to interact with physical payment devices.

Version information

What’s new:
  • Fixed NTT payment method value mapping to ensure correct provider selection
  • Updated BRI payment flow to validate transaction data before executing terminal actions
  • Added timeout configuration for card and QR transactions to auto-cancel and retry stalled requests
  • Added retry logic via Terminal API for more resilient recovery after timeouts
What’s new:
  • Added support for multiple concurrent device connections, enabling simultaneous transactions across different terminals
  • [BRI] Fixed status value handling in void and cancel API responses for improved transaction status accuracy
What’s new:
  • Introduced command ID handling for settlement operations
  • Enhanced retry logic with configurable attempt counts
  • Improved error handling and logging throughout the gateway service
Bug fixes and improvements:
  • Fix data mapping for terminal responses
  • [BRI] Enhanced status verification after transaction timeout for improved reliability
XenTerminal is the companion SDK to In-Person Payment Sessions API. This version provides core functionality for connecting to payment terminals and processing transactions.
The SDK follows semantic versioning. Breaking changes will bump the major version number.

Download

Download the Terminal Gateway Android SDK:

Installation

Follow these steps to add the Terminal Gateway Android SDK to your project.
1

Extract the SDK

Extract the provided SDK zip file so that a repository directory is available in your project root.
Verify that the repository folder contains the necessary Maven artifacts for the SDK.
2

Configure Gradle settings

Add the repository to your Gradle configuration. Choose one of the following approaches:
3

Add dependencies

Add the required dependencies to your app module’s build.gradle file:
dependencies {
  implementation("co.xendit.terminal:core-android:<latest_version>")
  implementation("co.xendit.terminal:gateway-android:<latest_version>")
  implementation("co.xendit.terminal:id-bri-android:<latest_version>")

  // NTT (Thailand) - uncomment if needed
  // implementation("co.xendit.terminal:th-ntt-android:<latest_version>")
}
Only add the terminal provider dependencies you actually need. Each provider adds to your app’s size.

Getting Started

Before you begin, ensure you have the following:
  • Client key: Request an In-Person Payment CLIENT_KEY from the Xendit team
  • Terminal device information: Obtain the Terminal ID and IP address for each device
  • Android project: Set up with minimum SDK version 21 (Android 5.0)
Keep your client key secure and never commit it to version control. Use environment variables or secure storage for production applications.

Step 1: Initialize the SDK

Initialize the Terminal Gateway SDK in your Application class:
MyApp.kt
class MyApp : Application() {
  override fun onCreate() {
    super.onCreate()

    TerminalApp.initialize(
      application = this,
      clientKey = TEST_CLIENT_KEY,
      mode = TerminalMode.INTEGRATION
    )

    // Add terminal providers based on your needs
    TerminalGateway.addProvider(TerminalBRI)
    // TerminalGateway.addProvider(TerminalNTT)
  }
}
Use TerminalMode.INTEGRATION for development and testing. Switch to TerminalMode.LIVE for production with a live client key.

Step 2: Register Terminal Devices

Register your terminal devices with the SDK:
TerminalGateway.registerDevice(
  listOf(
    TerminalDevice(TERMINAL_ID_1, TERMINAL_IP_1),
    TerminalDevice(TERMINAL_ID_2, TERMINAL_IP_2)
  )
)

// Registration completed - devices are now available for use
Log.i("TerminalGateway", "Devices registered successfully")
After successful registration, your terminal devices will be available for payment processing.

Configuration and Management

Set Operation Timeout

Configure the timeout for terminal operations:
// Set timeout to 5 minutes (default)
TerminalGateway.setOperationTimeout(minutes = 5)

// Set custom timeout for longer operations
TerminalGateway.setOperationTimeout(minutes = 10)

// Set provider-specific timeouts
TerminalGateway.setOperationTimeout(type = TimeoutType.CARD, minutes = 10)
TerminalGateway.setOperationTimeout(type = TimeoutType.QR, minutes = 10)
The default timeout is 5 minutes. Use the type-specific methods to override card vs QR timeouts when needed.
This timeout configuration is only available for the BRI provider. Other terminal providers use their own internal timeout settings.

Restart Terminal Connection

Manage and restart terminal service connections:
  • Restart All Connections
  • Restart Single Connection
Call the method without parameters to restart all connections and tasks:
// This restarts all connections
TerminalGateway.restart()

Monitor Connection State

Observe terminal connection states in real-time:
  • Using Coroutine Scope
  • Using Flow
Use the observeConnection method with a coroutine scope:
TerminalGateway.observeConnection(
  CoroutineScope(Dispatchers.IO),
  terminalDevice
) { state ->
  when (state) {
    ConnectionState.CONNECTED -> {
      // Terminal is ready for transactions
      Log.i("Terminal", "Connected to ${terminalDevice.id}")
    }
    ConnectionState.DISCONNECTED -> {
      // Terminal is offline
      Log.w("Terminal", "Disconnected from ${terminalDevice.id}")
    }
    ConnectionState.CONNECTING_FAILED -> {
      // Connection attempt failed
      Log.e("Terminal", "Failed to connect to ${terminalDevice.id}")
    }
    // ... handle other states
  }
}

Connection States

StateDescription
CONNECTEDDevice successfully connected to terminal and ready for transactions
DISCONNECTEDDevice disconnected from terminal or network unavailable
UNKNOWNInitial state before any connection attempt
CONNECTINGCurrently establishing connection to terminal
CONNECTING_FAILEDConnection attempt failed due to network or authentication issues
UNSUPPORTEDTerminal type or connection method not supported

Monitor Error States

Observe and handle errors across all terminal devices:
// Global error monitoring
TerminalGateway.observeError(
  CoroutineScope(Dispatchers.IO)
) { device, error ->
  Log.e("TerminalGateway", "Error on ${device.id}: ${error.message}")
  
  when (error.code) {
    ErrorCode.TERMINAL_BUSY -> {
      // Terminal is processing another transaction
      // Wait and retry later
    }
    ErrorCode.FAILED_TO_CONNECT -> {
      // Network connectivity issue
      // Check network connection and terminal <a href="#finding-terminal-information">IP</a>
    }
    ErrorCode.AUTHENTICATION_FAILED -> {
      // Invalid credentials or <a href="#finding-terminal-information">terminal ID</a>
      // Verify client key and terminal configuration
    }
    // ... handle other error codes
  }
}

// Device-specific error monitoring
val errorFlow = TerminalGateway.errorState(terminalDevice)
val deviceErrorFlow = terminalDevice.error

Error Handling

Error Data Structure

All errors returned by the Terminal Gateway SDK follow this structure:
data class TerminalError(
  val code: ErrorCode,
  val message: String
)

Error Codes Reference

Error CodeHTTP StatusDescription
INVALID_CREDENTIAL401Provided credentials are invalid or expired
INVALID_REQUEST400Request format or parameters are invalid
INTERNAL_SERVER_ERROR500Server-side error occurred during processing
UNKNOWN_ERROR0Unexpected error that doesn’t fit other categories
KEY_INVALID-1API key is invalid or not authorized
SEND_FAILED-2Failed to send data to the terminal device
NOT_CONNECTED-3No active connection to any terminal device
FAILED_TO_CONNECT-4Unable to establish connection to terminal
UNSUPPORTED-5Operation or platform not supported
TERMINAL_BUSY-6Terminal is processing another transaction
ENCRYPTION_FAILED-7Encryption key invalid or encryption process failed
AUTHENTICATION_FAILED-21Terminal authentication failed - check TID and credentials

Error Handling Best Practices

Implement comprehensive error handling for robust payment processing:
fun handleTerminalError(error: TerminalError?) {
  when (error?.code) {
    ErrorCode.AUTHENTICATION_FAILED -> {
      // Verify API key and <a href="#finding-terminal-information">terminal ID</a>
      Log.e("TerminalGateway", "Authentication failed: ${error.message}")
      // Show user-friendly message and retry with correct credentials
    }
    
    ErrorCode.TERMINAL_BUSY -> {
      // Terminal is processing another transaction
      Log.w("TerminalGateway", "Terminal busy: ${error.message}")
      // Wait and retry after a delay
      retryAfterDelay(5000) // 5 seconds
    }
    
    ErrorCode.FAILED_TO_CONNECT, ErrorCode.NOT_CONNECTED -> {
      // Network connectivity issues
      Log.e("TerminalGateway", "Connection failed: ${error.message}")
      // Check network connection and terminal <a href="#finding-terminal-information">IP address</a>
      checkNetworkConnectivity()
    }
    
    ErrorCode.KEY_INVALID, ErrorCode.INVALID_CREDENTIAL -> {
      // Credential issues
      Log.e("TerminalGateway", "Invalid credentials: ${error.message}")
      // Rotate or refresh API credentials
      refreshCredentials()
    }
    
    ErrorCode.ENCRYPTION_FAILED -> {
      // Encryption key issues
      Log.e("TerminalGateway", "Encryption failed: ${error.message}")
      // Re-initialize SDK with correct encryption keys
      reinitializeSDK()
    }
    
    else -> {
      // Log and escalate unknown errors
      Log.e("TerminalGateway", "Unknown error: ${error?.message}")
      // Report to monitoring system
      reportError(error)
    }
  }
}
Always implement retry logic for transient errors like TERMINAL_BUSY and FAILED_TO_CONNECT. Use exponential backoff to avoid overwhelming the terminal.

Troubleshooting

Common Issues and Solutions

Problem: The EDC (Electronic Data Capture) machine becomes unresponsive or stops processing transactions.Solution:
  1. Restart the EDC machine by holding the power button and selecting “Restart”
  2. Wait for the device to fully boot up and reconnect
  3. Verify the terminal is back online using the connection monitoring features
Always ensure the EDC is properly restarted before attempting new transactions to avoid data corruption.
Problem: Unexpected behaviors or unauthorized access to terminal functions.Solution:
  1. Ensure all transactions are initiated only through the SDK
  2. Contact the Xendit EDC team to enable POS-only mode for your Terminal IDs
  3. Configure terminal settings to disable manual transaction entry
POS-only mode prevents manual transaction entry and ensures all operations go through your application.
Problem: EDC fails to send transaction results to the SDK after payment completion.Solution:
  1. Query the Payment Session using the Terminal API to retrieve the latest status
  2. Check network connectivity between the EDC and your application
  3. Verify the callback URL configuration in your payment session
  4. Implement retry logic for failed status updates
// Example: Query payment session status
val paymentSession = terminalApi.getPaymentSession(sessionId)
when (paymentSession.status) {
  PaymentStatus.COMPLETED -> {
    // Process successful payment
  }
  PaymentStatus.FAILED -> {
    // Handle failed payment
  }
  PaymentStatus.PENDING -> {
    // Payment still in progress
  }
}
Problem: Unable to establish or maintain connection with terminal devices.Solutions:
  • Check network connectivity: Ensure both devices are on the same network
  • Verify IP addresses: Confirm terminal IP addresses are correct and accessible
  • Firewall settings: Check if firewall is blocking the connection ports
  • Terminal status: Ensure the terminal is powered on and in ready state
  • SDK initialization: Verify client key and terminal configuration
Use the connection monitoring features to diagnose specific connection issues.

Finding Terminal Information

  • BRI Terminals
  • NTT Terminals
1

Find Terminal ID (TID)

Open the BRI FMS app on the terminal device and locate the Terminal ID in the device information section.
BRI terminal showing Terminal ID in FMS app
2

Find IP Address

Open the ECRLink app on the terminal and check the network settings for the IP address.
BRI terminal showing IP address in ECRLink app
Screenshots and UI layouts may vary by firmware or app version. Refer to the latest vendor documentation if the interface differs from these instructions.