libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit a488eecd3cbb88a2bb2167c5e67e1090bf405f59
parent 9b8065058041fdde79794c4d0a43a03727907991
Author: Antoine A <>
Date:   Thu, 19 Sep 2024 14:24:58 +0200

nexus: WIP HKD parsing

Diffstat:
Mnexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt | 6++++++
Mnexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt | 87++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
2 files changed, 66 insertions(+), 27 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSetup.kt @@ -229,9 +229,15 @@ class EbicsSetup: CliktCommand("Set up the EBICS subscriber") { if (account.name != null && account.name != cfg.account.name) logger.warn("Expected NAME '${cfg.account.name}' from config got '${account.name}' from bank") + logger.debug("User status: ${hkd.status.name} ${hkd.status.description}") + logger.debug("Supported orders:") for (order in hkd.orders) { logger.debug("${order.type}${order.params}: ${order.description}") } + logger.debug("Authorized orders:") + for (order in hkd.permissions) { + logger.debug("${order.type}${order.params}") + } } } } catch (e: Exception) { diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/ebics/EbicsAdministrative.kt @@ -31,7 +31,9 @@ data class VersionNumber(val number: Float, val schema: String) { data class HKD ( val account: AccountInfo, - val orders: List<OrderInfo> + val orders: List<OrderInfo>, + val permissions: List<OrderInfo>, + val status: UserStatus ) data class AccountInfo ( val currency: String?, @@ -43,6 +45,17 @@ data class OrderInfo ( val params: String, val description: String, ) +// TODO use this in ebics setup to get current state and required actions +enum class UserStatus(val description: String) { + Ready("Subscriber is permitted access"), + New("Subscriber is established, pending access permission"), + INI("Subscriber has sent INI file, but no HIA file yet"), + HIA("Subscriber has sent HIA order, but no INI file yet"), + Initialised("Subscriber has sent both HIA order and INI file"), + SuspendedFailedAttempts("Suspended after several failed attempts, new initialisation via INI and HIA possible"), + SuspendedSPR("Suspended after SPR order, new initialisation via INI and HIA possible"), + SuspendedBank("Suspended by bank, new initialisation via INI and HIA is not possible, suspension can only be revoked by the bank"), +} object EbicsAdministrative { fun HEV(cfg: NexusEbicsConfig): ByteArray { @@ -69,8 +82,33 @@ object EbicsAdministrative { } fun parseHKD(stream: InputStream): HKD { + fun XmlDestructor.orderInfo() = OrderInfo( + one("AdminOrderType").text(), + opt("Service") { + // TODO user a structured type to enable comparison + val params = StringBuilder() + opt("ServiceName")?.run { + params.append(" ${text()}") + } + opt("Scope")?.run { + params.append(" ${text()}") + } + opt("ServiceOption")?.run { + params.append(" ${text()}") + } + opt("MsgName")?.run { + params.append(" ${text()}") + } + opt("Container")?.run { + params.append(" ${attr("containerType")}") + } + params.toString() + } ?: "", + opt("Description")?.text() ?: "" + ) + // TODO handle multiple partner, accounts and user using their respective ids return XmlDestructor.fromStream(stream, "HKDResponseOrderData") { - one("PartnerInfo") { + val (account, orders) = one("PartnerInfo") { var currency: String? = null var iban: String? = null val name = opt("AddressInfo")?.one("Name")?.text() @@ -82,33 +120,28 @@ object EbicsAdministrative { } } } - val orders = map("OrderInfo") { - OrderInfo( - one("AdminOrderType").text(), - opt("Service") { - val params = StringBuilder() - opt("ServiceName")?.run { - params.append(" ${text()}") - } - opt("Scope")?.run { - params.append(" ${text()}") - } - opt("ServiceOption")?.run { - params.append(" ${text()}") - } - opt("MsgName")?.run { - params.append(" ${text()}") - } - opt("Container")?.run { - params.append(" ${attr("containerType")}") - } - params.toString() - } ?: "", - one("Description").text() - ) + val orders = map("OrderInfo") { orderInfo() } + Pair(AccountInfo(currency, iban, name), orders) + } + val (permissions, status) = one("UserInfo") { + val userId = one("UserID").text() + val status = when (val status = one("UserID").attr("Status")) { + "1" -> UserStatus.Ready + "2" -> UserStatus.New + "3" -> UserStatus.INI + "4" -> UserStatus.HIA + "5" -> UserStatus.Initialised + "6" -> UserStatus.SuspendedFailedAttempts + // 7 is not applicable per spec + "8" -> UserStatus.SuspendedSPR + "9" -> UserStatus.SuspendedBank + else -> throw Exception("Unknown user statte $status") } - HKD(AccountInfo(currency, iban, name), orders) + val userName = opt("Name")?.text() + val permissions = map("Permission") { orderInfo() } + Pair(permissions, status) } + HKD(account, orders, permissions, status) } } }