Added BankingClient
This commit is contained in:
parent
d89a55e8d7
commit
0c9d1ab91c
|
@ -0,0 +1,106 @@
|
||||||
|
@file:OptIn(ExperimentalWasmDsl::class)
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
kotlin("multiplatform")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvmToolchain(8)
|
||||||
|
|
||||||
|
jvm {
|
||||||
|
withJava()
|
||||||
|
|
||||||
|
testRuns["test"].executionTask.configure {
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
testLogging {
|
||||||
|
showExceptions = true
|
||||||
|
showStandardStreams = true
|
||||||
|
events("passed", "skipped", "failed")
|
||||||
|
// exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
js {
|
||||||
|
moduleName = "banking-client"
|
||||||
|
binaries.executable()
|
||||||
|
|
||||||
|
browser {
|
||||||
|
testTask {
|
||||||
|
useKarma {
|
||||||
|
useChromeHeadless()
|
||||||
|
useFirefoxHeadless()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodejs {
|
||||||
|
testTask {
|
||||||
|
useMocha {
|
||||||
|
timeout = "20s" // Mocha times out after 2 s, which is too short for bufferExceeded() test
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasmJs()
|
||||||
|
|
||||||
|
|
||||||
|
linuxX64()
|
||||||
|
mingwX64()
|
||||||
|
|
||||||
|
iosArm64()
|
||||||
|
iosSimulatorArm64()
|
||||||
|
macosX64()
|
||||||
|
macosArm64()
|
||||||
|
watchosArm64()
|
||||||
|
watchosSimulatorArm64()
|
||||||
|
tvosArm64()
|
||||||
|
tvosSimulatorArm64()
|
||||||
|
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val coroutinesVersion: String by project
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api(project(":BankingClientModel"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonTest {
|
||||||
|
dependencies {
|
||||||
|
implementation(kotlin("test"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmMain {
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmTest { }
|
||||||
|
|
||||||
|
jsMain {
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsTest { }
|
||||||
|
|
||||||
|
nativeMain { }
|
||||||
|
nativeTest { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ext["customArtifactId"] = "banking-client"
|
||||||
|
|
||||||
|
apply(from = "../gradle/scripts/publish-codinux.gradle.kts")
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
|
||||||
|
interface BankingClient {
|
||||||
|
|
||||||
|
suspend fun getAccountDataAsync(bankCode: String, loginName: String, password: String) =
|
||||||
|
getAccountDataAsync(AccountCredentials(bankCode, loginName, password))
|
||||||
|
|
||||||
|
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||||
|
suspend fun getAccountDataAsync(credentials: AccountCredentials) =
|
||||||
|
getAccountDataAsync(credentials, GetAccountDataOptions())
|
||||||
|
|
||||||
|
suspend fun getAccountDataAsync(credentials: AccountCredentials, options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
|
||||||
|
interface BankingClientForCustomer {
|
||||||
|
|
||||||
|
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||||
|
suspend fun getAccountDataAsync() = getAccountDataAsync(GetAccountDataOptions())
|
||||||
|
|
||||||
|
suspend fun getAccountDataAsync(options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
|
||||||
|
abstract class BankingClientForCustomerBase(
|
||||||
|
protected val credentials: AccountCredentials,
|
||||||
|
protected val client: BankingClient
|
||||||
|
) : BankingClientForCustomer {
|
||||||
|
|
||||||
|
override suspend fun getAccountDataAsync(options: GetAccountDataOptions) =
|
||||||
|
client.getAccountDataAsync(credentials, options)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
|
||||||
|
interface BlockingBankingClient {
|
||||||
|
|
||||||
|
suspend fun getAccountData(bankCode: String, loginName: String, password: String) =
|
||||||
|
getAccountData(AccountCredentials(bankCode, loginName, password))
|
||||||
|
|
||||||
|
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||||
|
fun getAccountData(credentials: AccountCredentials) =
|
||||||
|
getAccountData(credentials, GetAccountDataOptions())
|
||||||
|
|
||||||
|
fun getAccountData(credentials: AccountCredentials, options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||||
|
import net.codinux.banking.client.model.response.Response
|
||||||
|
|
||||||
|
interface BlockingBankingClientForCustomer {
|
||||||
|
|
||||||
|
// for languages not supporting default parameters (Java, Swift, JS, ...)
|
||||||
|
fun getAccountData() = getAccountData(GetAccountDataOptions())
|
||||||
|
|
||||||
|
fun getAccountData(options: GetAccountDataOptions): Response<GetAccountDataResponse>
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package net.codinux.banking.client
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountCredentials
|
||||||
|
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||||
|
|
||||||
|
abstract class BlockingBankingClientForCustomerBase(
|
||||||
|
protected val credentials: AccountCredentials,
|
||||||
|
protected val client: BlockingBankingClient
|
||||||
|
) : BlockingBankingClientForCustomer {
|
||||||
|
|
||||||
|
override fun getAccountData(options: GetAccountDataOptions) =
|
||||||
|
client.getAccountData(credentials, options)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package net.codinux.banking.client.model.options
|
||||||
|
|
||||||
|
import kotlinx.datetime.LocalDate
|
||||||
|
|
||||||
|
data class GetAccountDataOptions(
|
||||||
|
val retrieveBalance: Boolean = true,
|
||||||
|
val retrieveTransactions: RetrieveTransactions = RetrieveTransactions.OfLast90Days,
|
||||||
|
val retrieveTransactionsFrom: LocalDate? = null,
|
||||||
|
val retrieveTransactionsTo: LocalDate? = null,
|
||||||
|
val abortIfTanIsRequired: Boolean = false
|
||||||
|
)
|
|
@ -0,0 +1,18 @@
|
||||||
|
package net.codinux.banking.client.model.options
|
||||||
|
|
||||||
|
enum class RetrieveTransactions {
|
||||||
|
No,
|
||||||
|
|
||||||
|
All,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some banks support that according to PSD2 account transactions of last 90 days may be retrieved without
|
||||||
|
* a TAN (= no strong customer authorization needed). So try this options if you don't want to enter a TAN.
|
||||||
|
*/
|
||||||
|
OfLast90Days,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves account transactions in the boundaries of [GetAccountDataOptions.retrieveTransactionsFrom] to [GetAccountDataOptions.retrieveTransactionsTo].
|
||||||
|
*/
|
||||||
|
AccordingToRetrieveFromAndTo
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@NoArgConstructor
|
||||||
|
class Error(
|
||||||
|
val type: ErrorType,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A banking client internal error like an error occurred during response parsing.
|
||||||
|
*/
|
||||||
|
val internalError: String? = null,
|
||||||
|
/**
|
||||||
|
* Error messages as received from bank
|
||||||
|
*/
|
||||||
|
val errorMessagesFromBank: List<String> = emptyList(),
|
||||||
|
) {
|
||||||
|
override fun toString() = "$type: ${internalError ?: errorMessagesFromBank.joinToString()}"
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
enum class ErrorType {
|
||||||
|
BankDoesNotSupportFinTs3,
|
||||||
|
|
||||||
|
NetworkError,
|
||||||
|
|
||||||
|
InternalError,
|
||||||
|
|
||||||
|
BankReturnedError,
|
||||||
|
|
||||||
|
WrongCredentials,
|
||||||
|
|
||||||
|
AccountLocked,
|
||||||
|
|
||||||
|
JobNotSupported,
|
||||||
|
|
||||||
|
UserCancelledAction,
|
||||||
|
|
||||||
|
TanRequiredButShouldAbortIfRequiresTan,
|
||||||
|
|
||||||
|
NoneOfTheAccountsSupportsRetrievingData,
|
||||||
|
|
||||||
|
DidNotRetrieveAllAccountData,
|
||||||
|
|
||||||
|
CanNotDetermineBicForIban,
|
||||||
|
|
||||||
|
NoAccountSupportsMoneyTransfer,
|
||||||
|
|
||||||
|
MoreThanOneAccountSupportsMoneyTransfer,
|
||||||
|
|
||||||
|
UnknownError
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.AccountTransaction
|
||||||
|
import net.codinux.banking.client.model.CustomerAccount
|
||||||
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@NoArgConstructor
|
||||||
|
class GetAccountDataResponse(
|
||||||
|
val customer: CustomerAccount
|
||||||
|
) {
|
||||||
|
|
||||||
|
val bookedTransactions: List<AccountTransaction>
|
||||||
|
get() = customer.accounts.flatMap { it.bookedTransactions }.sortedByDescending { it.valueDate }
|
||||||
|
|
||||||
|
override fun toString() = customer.toString()
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@NoArgConstructor
|
||||||
|
class Response<T> (
|
||||||
|
val type: ResponseType,
|
||||||
|
val data: T? = null,
|
||||||
|
val error: Error? = null,
|
||||||
|
val tanRequired: TanRequired? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun <T> success(data: T): Response<T> =
|
||||||
|
Response(ResponseType.Success, data)
|
||||||
|
|
||||||
|
fun <T> error(errorType: ErrorType, internalError: String? = null, errorMessagesFromBank: List<String> = emptyList()): Response<T> =
|
||||||
|
Response(ResponseType.Error, null, Error(errorType, internalError, errorMessagesFromBank))
|
||||||
|
|
||||||
|
fun <T> bankReturnedError(errorMessagesFromBank: List<String>): Response<T> =
|
||||||
|
Response.error(ErrorType.BankReturnedError, null, errorMessagesFromBank)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun toString() = when (type) {
|
||||||
|
ResponseType.Success -> "Success: $data"
|
||||||
|
ResponseType.Error -> "Error: $error"
|
||||||
|
ResponseType.TanRequired -> "TanRequired: $tanRequired"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
enum class ResponseType {
|
||||||
|
Success,
|
||||||
|
|
||||||
|
Error,
|
||||||
|
|
||||||
|
TanRequired
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package net.codinux.banking.client.model.response
|
||||||
|
|
||||||
|
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||||
|
|
||||||
|
@NoArgConstructor
|
||||||
|
class TanRequired (
|
||||||
|
val tanRequestId: String,
|
||||||
|
// TODO: add TAN model
|
||||||
|
// val tanChallenge: TanChallenge
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
|
@ -27,12 +27,14 @@ allprojects {
|
||||||
|
|
||||||
tasks.register("publishAllToMavenLocal") {
|
tasks.register("publishAllToMavenLocal") {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
":BankingClientModel:publishToMavenLocal"
|
":BankingClientModel:publishToMavenLocal",
|
||||||
|
":BankingClient:publishToMavenLocal"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register("publishAll") {
|
tasks.register("publishAll") {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
":BankingClientModel:publish"
|
":BankingClientModel:publish",
|
||||||
|
":BankingClient:publish"
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -21,6 +21,8 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
rootProject.name = "BankingClient"
|
rootProject.name = "BankingClientProject"
|
||||||
|
|
||||||
|
|
||||||
include("BankingClientModel")
|
include("BankingClientModel")
|
||||||
|
include("BankingClient")
|
||||||
|
|
Loading…
Reference in New Issue