Implemented transferMoneyAsync()
This commit is contained in:
parent
675066c216
commit
c35026bfcc
|
@ -3,9 +3,11 @@ package net.codinux.banking.client
|
|||
import net.codinux.banking.client.model.*
|
||||
import net.codinux.banking.client.model.options.RetrieveTransactions
|
||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
||||
|
||||
interface BankingClient {
|
||||
|
||||
|
@ -39,4 +41,11 @@ interface BankingClient {
|
|||
*/
|
||||
suspend fun updateAccountTransactionsAsync(user: UserAccount, accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
|
||||
|
||||
|
||||
suspend fun transferMoneyAsync(bankCode: String, loginName: String, password: String, recipientName: String,
|
||||
recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null) =
|
||||
transferMoneyAsync(TransferMoneyRequestForUser(bankCode, loginName, password, null, recipientName, recipientAccountIdentifier, null, amount, paymentReference = paymentReference))
|
||||
|
||||
suspend fun transferMoneyAsync(request: TransferMoneyRequestForUser): Response<TransferMoneyResponse>
|
||||
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.BankAccount
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.options.RetrieveTransactions
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequest
|
||||
import net.codinux.banking.client.model.response.GetAccountDataResponse
|
||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
import net.codinux.banking.client.model.response.TransferMoneyResponse
|
||||
|
||||
interface BankingClientForUser {
|
||||
|
||||
|
@ -37,6 +40,11 @@ interface BankingClientForUser {
|
|||
* Updates account's transactions beginning from [BankAccount.lastTransactionsRetrievalTime].
|
||||
* This may requires TAN if [BankAccount.lastTransactionsRetrievalTime] is older than 90 days.
|
||||
*/
|
||||
suspend fun updateAccountTransactionsAsync(): Response<List<GetTransactionsResponse>>
|
||||
suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>? = null): Response<List<GetTransactionsResponse>>
|
||||
|
||||
|
||||
suspend fun transferMoneyAsync(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null): Response<TransferMoneyResponse>
|
||||
|
||||
suspend fun transferMoneyAsync(request: TransferMoneyRequest): Response<TransferMoneyResponse>
|
||||
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import net.codinux.banking.client.model.AccountCredentials
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.BankAccount
|
||||
import net.codinux.banking.client.model.UserAccount
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||
import net.codinux.banking.client.model.response.GetTransactionsResponse
|
||||
import net.codinux.banking.client.model.response.Response
|
||||
|
||||
|
@ -21,7 +25,14 @@ abstract class BankingClientForUserBase(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun updateAccountTransactionsAsync(): Response<List<GetTransactionsResponse>> =
|
||||
client.updateAccountTransactionsAsync(user)
|
||||
override suspend fun updateAccountTransactionsAsync(accounts: List<BankAccount>?): Response<List<GetTransactionsResponse>> =
|
||||
client.updateAccountTransactionsAsync(user, accounts)
|
||||
|
||||
|
||||
override suspend fun transferMoneyAsync(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String?) =
|
||||
transferMoneyAsync(TransferMoneyRequest(null, recipientName, recipientAccountIdentifier, null, amount, paymentReference = paymentReference))
|
||||
|
||||
override suspend fun transferMoneyAsync(request: TransferMoneyRequest) =
|
||||
client.transferMoneyAsync(TransferMoneyRequestForUser(user.bankCode, user.loginName, user.password!!, request))
|
||||
|
||||
}
|
|
@ -1,32 +1,58 @@
|
|||
package net.codinux.banking.client
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.BankAccount
|
||||
import net.codinux.banking.client.model.UserAccount
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||
|
||||
/* BankingClient */
|
||||
|
||||
fun BankingClient.getAccountData(bankCode: String, loginName: String, password: String) = runBlocking {
|
||||
this@getAccountData.getAccountDataAsync(bankCode, loginName, password)
|
||||
getAccountDataAsync(bankCode, loginName, password)
|
||||
}
|
||||
|
||||
fun BankingClient.getAccountData(request: GetAccountDataRequest) = runBlocking {
|
||||
this@getAccountData.getAccountDataAsync(request)
|
||||
getAccountDataAsync(request)
|
||||
}
|
||||
|
||||
fun BankingClient.updateAccountTransactions(user: UserAccount, accounts: List<BankAccount>? = null) = runBlocking {
|
||||
this@updateAccountTransactions.updateAccountTransactionsAsync(user, accounts)
|
||||
updateAccountTransactionsAsync(user, accounts)
|
||||
}
|
||||
|
||||
|
||||
fun BankingClient.transferMoney(bankCode: String, loginName: String, password: String, recipientName: String,
|
||||
recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null) = runBlocking {
|
||||
transferMoneyAsync(bankCode, loginName, password, recipientName, recipientAccountIdentifier, amount, paymentReference)
|
||||
}
|
||||
|
||||
fun BankingClient.transferMoney(request: TransferMoneyRequestForUser) = runBlocking {
|
||||
transferMoneyAsync(request)
|
||||
}
|
||||
|
||||
|
||||
/* BankingClientForUser */
|
||||
|
||||
fun BankingClientForUser.getAccountData() = runBlocking {
|
||||
this@getAccountData.getAccountDataAsync()
|
||||
getAccountDataAsync()
|
||||
}
|
||||
|
||||
fun BankingClientForUser.getAccountData(options: GetAccountDataOptions) = runBlocking {
|
||||
this@getAccountData.getAccountDataAsync(options)
|
||||
getAccountDataAsync(options)
|
||||
}
|
||||
|
||||
fun BankingClientForUser.updateAccountTransactions() = runBlocking {
|
||||
this@updateAccountTransactions.updateAccountTransactionsAsync()
|
||||
updateAccountTransactionsAsync()
|
||||
}
|
||||
|
||||
|
||||
fun BankingClientForUser.transferMoney(recipientName: String, recipientAccountIdentifier: String, amount: Amount, paymentReference: String? = null) = runBlocking {
|
||||
transferMoneyAsync(recipientName, recipientAccountIdentifier, amount, paymentReference)
|
||||
}
|
||||
|
||||
fun BankingClientForUser.transferMoney(request: TransferMoneyRequest) = runBlocking {
|
||||
transferMoneyAsync(request)
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package net.codinux.banking.client.model.request
|
||||
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.BankAccountIdentifier
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
import net.codinux.banking.client.model.tan.TanMethodType
|
||||
|
||||
@NoArgConstructor
|
||||
open class TransferMoneyRequest(
|
||||
|
||||
/* Sender settings */
|
||||
|
||||
/**
|
||||
* The account from which the money should be withdrawn.
|
||||
* If not specified client retrieves all bank accounts and checks if there is exactly one that supports money transfer.
|
||||
* If no or more than one bank account supports money transfer, the error codes NoAccountSupportsMoneyTransfer or MoreThanOneAccountSupportsMoneyTransfer are returned.
|
||||
*/
|
||||
open val senderAccount: BankAccountIdentifier? = null,
|
||||
|
||||
|
||||
/* Recipient settings */
|
||||
|
||||
open val recipientName: String,
|
||||
/**
|
||||
* The identifier of recipient's account. In most cases the IBAN.
|
||||
*/
|
||||
open val recipientAccountIdentifier: String,
|
||||
/**
|
||||
* The identifier of recipient's bank. In most cases the BIC.
|
||||
* Can be omitted for German banks as the BIC can be derived from IBAN.
|
||||
*/
|
||||
open val recipientBankIdentifier: String? = null,
|
||||
|
||||
|
||||
/* Transfer data */
|
||||
|
||||
open val amount: Amount,
|
||||
|
||||
open val currency: String = "EUR",
|
||||
|
||||
/**
|
||||
* The purpose of payment. An optional value that tells the reason for the transfer.
|
||||
*
|
||||
* May not be longer than 140 characters. Some characters are forbidden (TODO: add reference of forbidden characters).
|
||||
*/
|
||||
open val paymentReference: String? = null, // Alternativ: Purpose of payment
|
||||
|
||||
/**
|
||||
* If transfer should be executed as 'real-time transfer', that is the money is in less than 10 seconds
|
||||
* transferred to the account of the recipient.
|
||||
*
|
||||
* May costs extra fees.
|
||||
*
|
||||
* Not supported by all sender and recipient banks.
|
||||
*/
|
||||
open val instantTransfer: Boolean = false, // Alternativ: Instant payment ("Instant payment" ist ebenfalls weit verbreitet und wird oft im Kontext von digitalen Zahlungen verwendet, bei denen die Zahlung in Echtzeit erfolgt. Es kann jedoch breiter gefasst sein und umfasst nicht nur Banktransfers, sondern auch andere Arten von Sofortzahlungen (z.B. mobile Zahlungen).)
|
||||
|
||||
/**
|
||||
* Specifies which [TanMethodType] should be preferred when having to choose between multiple available for user
|
||||
* without requesting the user to choose one.
|
||||
*
|
||||
* By default we don't ask the user which TanMethod she prefers but choose one that could match best. If she really
|
||||
* likes to use a different one, she can select another one in EnterTanDialog.
|
||||
*
|
||||
* By default we prefer non visual TanMethods (like AppTan and SMS) over image based TanMethods (like QR-code and
|
||||
* photoTan) and exclude ChipTanUsb, which is not supported by application, and Flickercode, which is hard to
|
||||
* implement and therefore most applications have not implemented.
|
||||
*
|
||||
* Console apps can only handle non visual TanMethods.
|
||||
* But also graphical applications prefer non visual TanMethods as then they only have to display a text field to input
|
||||
* TAN, and then image based TanMethods as then they additionally only have to display an image.
|
||||
*/
|
||||
val preferredTanMethods: List<TanMethodType>? = TanMethodType.NonVisualOrImageBased
|
||||
) {
|
||||
override fun toString() = "$amount to $recipientName - $paymentReference"
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package net.codinux.banking.client.model.request
|
||||
|
||||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.BankAccountIdentifier
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
import net.codinux.banking.client.model.tan.TanMethodType
|
||||
|
||||
/**
|
||||
* For documentation see [TransferMoneyRequest].
|
||||
*/
|
||||
@NoArgConstructor
|
||||
open class TransferMoneyRequestForUser(
|
||||
|
||||
/* Sender settings */
|
||||
|
||||
val bankCode: String,
|
||||
val loginName: String,
|
||||
val password: String,
|
||||
|
||||
senderAccount: BankAccountIdentifier? = null,
|
||||
|
||||
|
||||
/* Recipient settings */
|
||||
|
||||
recipientName: String,
|
||||
recipientAccountIdentifier: String,
|
||||
recipientBankIdentifier: String? = null,
|
||||
|
||||
|
||||
/* Transfer data */
|
||||
|
||||
amount: Amount,
|
||||
currency: String = "EUR",
|
||||
paymentReference: String? = null,
|
||||
|
||||
instantTransfer: Boolean = false,
|
||||
|
||||
|
||||
preferredTanMethods: List<TanMethodType>? = TanMethodType.NonVisualOrImageBased
|
||||
) : TransferMoneyRequest(senderAccount, recipientName, recipientAccountIdentifier, recipientBankIdentifier, amount, currency, paymentReference, instantTransfer, preferredTanMethods) {
|
||||
|
||||
constructor(bankCode: String, loginName: String, password: String, request: TransferMoneyRequest)
|
||||
: this(bankCode, loginName, password, request.senderAccount, request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
||||
request.amount, request.currency, request.paymentReference, request.instantTransfer, request.preferredTanMethods)
|
||||
|
||||
override fun toString() = "$bankCode $loginName ${super.toString()}"
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package net.codinux.banking.client.model.response
|
||||
|
||||
import net.codinux.banking.client.model.config.NoArgConstructor
|
||||
|
||||
/**
|
||||
* Transfer money process does not return any data, only if successful or not (and in latter case an error message).
|
||||
*/
|
||||
@NoArgConstructor
|
||||
open class TransferMoneyResponse
|
|
@ -7,6 +7,7 @@ import net.codinux.banking.client.model.BankAccountFeatures
|
|||
import net.codinux.banking.client.model.UserAccount
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.request.GetAccountDataRequest
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||
import net.codinux.banking.client.model.response.*
|
||||
import net.codinux.banking.fints.FinTsClient
|
||||
import net.codinux.banking.fints.config.FinTsClientConfiguration
|
||||
|
@ -20,9 +21,9 @@ open class FinTs4kBankingClient(
|
|||
constructor(callback: BankingClientCallback) : this(FinTsClientConfiguration(), callback)
|
||||
|
||||
|
||||
protected val mapper = FinTs4kMapper()
|
||||
protected open val mapper = FinTs4kMapper()
|
||||
|
||||
protected val client = FinTsClient(config, BridgeFintTsToBankingClientCallback(callback, mapper))
|
||||
protected open val client = FinTsClient(config, BridgeFintTsToBankingClientCallback(callback, mapper))
|
||||
|
||||
|
||||
override suspend fun getAccountDataAsync(request: GetAccountDataRequest): Response<GetAccountDataResponse> {
|
||||
|
@ -55,4 +56,11 @@ open class FinTs4kBankingClient(
|
|||
return Response.error(ErrorType.NoneOfTheAccountsSupportsRetrievingData, "Keiner der Konten unterstützt das Abholen der Umsätze oder des Kontostands") // TODO: translate
|
||||
}
|
||||
|
||||
|
||||
override suspend fun transferMoneyAsync(request: TransferMoneyRequestForUser): Response<TransferMoneyResponse> {
|
||||
val response = client.transferMoneyAsync(mapper.mapToTransferMoneyParameter(request))
|
||||
|
||||
return mapper.mapTransferMoneyResponse(response)
|
||||
}
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import net.codinux.banking.client.model.AccountTransaction
|
|||
import net.codinux.banking.client.model.Amount
|
||||
import net.codinux.banking.client.model.tan.*
|
||||
import net.codinux.banking.client.model.options.GetAccountDataOptions
|
||||
import net.codinux.banking.client.model.request.TransferMoneyRequestForUser
|
||||
import net.codinux.banking.client.model.response.*
|
||||
import net.codinux.banking.client.model.tan.ActionRequiringTan
|
||||
import net.codinux.banking.client.model.tan.TanChallenge
|
||||
|
@ -26,6 +27,7 @@ import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.Mobil
|
|||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanGeneratorTanMedium
|
||||
import net.codinux.banking.fints.messages.datenelemente.implementierte.tan.TanMediumStatus
|
||||
import net.dankito.banking.banklistcreator.prettifier.BankingGroupMapper
|
||||
import net.dankito.banking.client.model.parameter.TransferMoneyParameter
|
||||
import kotlin.io.encoding.Base64
|
||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
||||
|
||||
|
@ -38,7 +40,7 @@ open class FinTs4kMapper {
|
|||
|
||||
open fun mapToGetAccountDataParameter(credentials: AccountCredentials, options: GetAccountDataOptions) = GetAccountDataParameter(
|
||||
credentials.bankCode, credentials.loginName, credentials.password,
|
||||
options.accounts.map { BankAccountIdentifierImpl(it.identifier, it.subAccountNumber, it.iban) },
|
||||
options.accounts.map { mapBankAccountIdentifier(it) },
|
||||
options.retrieveBalance,
|
||||
RetrieveTransactions.valueOf(options.retrieveTransactions.name), options.retrieveTransactionsFrom, options.retrieveTransactionsTo,
|
||||
preferredTanMethods = options.preferredTanMethods?.map { mapTanMethodType(it) },
|
||||
|
@ -60,17 +62,19 @@ open class FinTs4kMapper {
|
|||
)
|
||||
}
|
||||
|
||||
open fun mapBankAccountIdentifier(account: BankAccountIdentifier): BankAccountIdentifierImpl =
|
||||
BankAccountIdentifierImpl(account.identifier, account.subAccountNumber, account.iban)
|
||||
|
||||
protected open fun mapTanMethodType(type: TanMethodType): net.codinux.banking.fints.model.TanMethodType =
|
||||
net.codinux.banking.fints.model.TanMethodType.valueOf(type.name)
|
||||
|
||||
|
||||
open fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse): Response<GetAccountDataResponse> {
|
||||
return if (response.successful && response.customerAccount != null) {
|
||||
open fun map(response: net.dankito.banking.client.model.response.GetAccountDataResponse): Response<GetAccountDataResponse> =
|
||||
if (response.successful && response.customerAccount != null) {
|
||||
Response.success(GetAccountDataResponse(mapUser(response.customerAccount!!)))
|
||||
} else {
|
||||
mapError(response)
|
||||
}
|
||||
}
|
||||
|
||||
open fun map(responses: List<Triple<BankAccount, GetAccountDataParameter, net.dankito.banking.client.model.response.GetAccountDataResponse>>): Response<List<GetTransactionsResponse>> {
|
||||
val type = if (responses.all { it.third.successful }) ResponseType.Success else ResponseType.Error
|
||||
|
@ -270,7 +274,26 @@ open class FinTs4kMapper {
|
|||
FlickerCode(flickerCode.challengeHHD_UC, flickerCode.parsedDataSet, mapException(flickerCode.decodingError))
|
||||
|
||||
|
||||
protected open fun <T> mapError(response: net.dankito.banking.client.model.response.GetAccountDataResponse): Response<T> {
|
||||
/* Transfer Money */
|
||||
|
||||
open fun mapToTransferMoneyParameter(request: TransferMoneyRequestForUser): TransferMoneyParameter = TransferMoneyParameter(
|
||||
request.bankCode, request.loginName, request.password, request.senderAccount?.let { mapBankAccountIdentifier(it) },
|
||||
request.recipientName, request.recipientAccountIdentifier, request.recipientBankIdentifier,
|
||||
mapToMoney(request.amount, request.currency), request.paymentReference, request.instantTransfer,
|
||||
request.preferredTanMethods?.map { mapTanMethodType(it) }
|
||||
)
|
||||
|
||||
open fun mapTransferMoneyResponse(response: net.dankito.banking.client.model.response.TransferMoneyResponse): Response<TransferMoneyResponse> =
|
||||
if (response.successful) {
|
||||
Response.success(TransferMoneyResponse())
|
||||
} else {
|
||||
mapError(response)
|
||||
}
|
||||
|
||||
open fun mapToMoney(amount: Amount, currency: String): Money = Money(amount.amount, currency)
|
||||
|
||||
|
||||
protected open fun <T> mapError(response: net.dankito.banking.client.model.response.FinTsClientResponse): Response<T> {
|
||||
return if (response.error != null) {
|
||||
Response.error(ErrorType.valueOf(response.error!!.name), if (response.error == ErrorCode.BankReturnedError) null else response.errorMessage,
|
||||
if (response.error == ErrorCode.BankReturnedError && response.errorMessage !== null) listOf(response.errorMessage!!) else emptyList())
|
||||
|
|
Loading…
Reference in New Issue