The FineractClient is a Kotlin client library for interacting with Apache Fineract's REST APIs. It provides a
comprehensive wrapper around Ktorfit for making HTTP requests to Fineract servers and accessing various financial
services endpoints.
To use the library in your Gradle project, follow the steps below:
implementation("io.github.niyajali:fineract-client-kmp:1.0.6")
The client follows a Builder pattern for configuration and provides type-safe access to Fineract's API endpoints through generated interfaces.
class FineractClient private constructor(
private val ktorfit: Ktorfit,
) {
// API endpoint accessors
val accountingClosures = ktorfit.createAccountingClosureApi()
val accountingRules = ktorfit.createAccountingRulesApi()
val accountNumberFormats = ktorfit.createAccountNumberFormatApi()
val accountTransfers = ktorfit.createAccountTransfersApi()
// Utility methods
fun baseURL(): String
fun httpClient(): HttpClient
}accountingClosures- Manage accounting period closuresaccountingRules- Handle accounting rules and configurationsaccountNumberFormats- Manage account number formatting rulesaccountTransfers- Process account-to-account transfers
baseURL()- Returns the configured base URL for the Fineract serverhttpClient()- Provides access to the underlying HttpClient for advanced customizations
The Builder class provides a fluent API for configuring the FineractClient:
class Builder internal constructor() {
fun baseURL(baseURL: String): Builder
fun tenant(tenant: String?): Builder
fun httpClient(httpClient: HttpClient): Builder
@Deprecated
fun inSecure(insecure: Boolean): Builder
fun build(): FineractClient
}import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
// Create HTTP client with authentication
val httpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
install(Auth) {
basic {
credentials {
BasicAuthCredentials(username = "admin", password = "password")
}
}
}
}
// Build the Fineract client
val fineractClient = FineractClient.builder()
.baseURL("https://your-fineract-server.com/fineract-provider/api/v1")
.tenant("default")
.httpClient(httpClient)
.build()val customHttpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
isLenient = true
})
}
install(Auth) {
bearer {
loadTokens {
BearerTokens("your-access-token", "your-refresh-token")
}
}
}
install(Logging) {
level = LogLevel.INFO
}
// Custom timeout settings
install(HttpTimeout) {
requestTimeoutMillis = 30000
connectTimeoutMillis = 10000
}
}
val client = FineractClient.builder()
.baseURL("https://your-fineract-server.com/fineract-provider/api/v1")
.tenant("production")
.httpClient(customHttpClient)
.build()// Example: Working with accounting closures
suspend fun manageAccountingClosures(client: FineractClient) {
try {
// Get all accounting closures
val closures = client.accountingClosures.getAllClosures()
println("Found ${closures.size} accounting closures")
// Create a new accounting closure
val newClosure = CreateAccountingClosureRequest(
officeId = 1,
closingDate = LocalDate.now().format(DateTimeFormatter.ofPattern(FineractClient.DATE_FORMAT)),
comments = "Monthly closure"
)
val createdClosure = client.accountingClosures.createClosure(newClosure)
println("Created closure with ID: ${createdClosure.resourceId}")
} catch (e: Exception) {
println("Error managing accounting closures: ${e.message}")
}
}
// Example: Working with account transfers
suspend fun processAccountTransfer(client: FineractClient) {
try {
val transferRequest = AccountTransferRequest(
fromAccountId = 123,
toAccountId = 456,
transferAmount = 1000.00,
transferDate = LocalDate.now().format(DateTimeFormatter.ofPattern(FineractClient.DATE_FORMAT)),
transferDescription = "Monthly payment"
)
val transfer = client.accountTransfers.create(transferRequest)
println("Transfer completed: ${transfer.resourceId}")
} catch (e: Exception) {
println("Transfer failed: ${e.message}")
}
}The client provides a constant for consistent date formatting across API calls:
import java.time.LocalDate
import java.time.format.DateTimeFormatter
val currentDate = LocalDate.now()
val formattedDate = currentDate.format(DateTimeFormatter.ofPattern(FineractClient.DATE_FORMAT))
// Result: "2025-05-31"
// Use in API requests
val request = SomeApiRequest(
date = formattedDate,
// other fields...
)suspend fun robustApiCall(client: FineractClient) {
try {
val result = client.accountingRules.getAllRules()
// Process successful result
result.forEach { rule ->
println("Rule: ${rule.name} - ${rule.description}")
}
} catch (e: ClientRequestException) {
// Handle 4xx errors (client errors)
println("Client error (${e.response.status}): ${e.message}")
} catch (e: ServerResponseException) {
// Handle 5xx errors (server errors)
println("Server error (${e.response.status}): ${e.message}")
} catch (e: Exception) {
// Handle other network/parsing errors
println("Unexpected error: ${e.message}")
}
}class FineractClientManager {
private val clients = mutableMapOf<String, FineractClient>()
fun getClientForTenant(tenant: String): FineractClient {
return clients.getOrPut(tenant) {
FineractClient.builder()
.baseURL("https://your-fineract-server.com/fineract-provider/api/v1")
.tenant(tenant)
.httpClient(createHttpClientForTenant(tenant))
.build()
}
}
private fun createHttpClientForTenant(tenant: String): HttpClient {
return HttpClient(CIO) {
install(ContentNegotiation) { json() }
install(Auth) {
basic {
credentials {
// Load tenant-specific credentials
BasicAuthCredentials(
username = loadUsernameForTenant(tenant),
password = loadPasswordForTenant(tenant)
)
}
}
}
}
}
}
// Usage
val clientManager = FineractClientManager()
val productionClient = clientManager.getClientForTenant("production")
val stagingClient = clientManager.getClientForTenant("staging")baseURL- The base URL of your Fineract serverhttpClient- Configured HttpClient with authentication
tenant- Fineract tenant identifier (for multi-tenant setups)inSecure-⚠️ Deprecated - Use proper SSL configuration in HttpClient instead
- Reuse Client Instances - Create one client per tenant and reuse it across your application
- Proper Authentication - Always configure authentication in the HttpClient
- Error Handling - Implement comprehensive error handling for network and API errors
- Date Formatting - Use
FineractClient.DATE_FORMATconstant for consistent date formatting - Resource Management - Properly close HttpClient resources when shutting down
- SSL Configuration - Use proper SSL settings instead of the deprecated
inSecureoption
The FineractClient is thread-safe and can be safely used from multiple coroutines concurrently. The underlying Ktorfit and HttpClient are designed for concurrent use.
Ensure you have the following dependencies in your project:
// build.gradle.kts
dependencies {
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-client-auth:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("de.jensklingenberg.ktorfit:ktorfit-lib:$ktorfit_version")
// Add other Fineract client dependencies
}