aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAntoine A <>2024-01-23 19:38:32 +0100
committerAntoine A <>2024-01-23 19:38:32 +0100
commit51300a1a75c137c215189dff8ca38e53d9ec5dea (patch)
tree321a2b6d2b98553c4ac805adb759efbaedcbf3c5
parentbb7e455b0f71ba1870f4233f58bcb4bd4fbf05ed (diff)
downloadlibeufin-51300a1a75c137c215189dff8ca38e53d9ec5dea.tar.gz
libeufin-51300a1a75c137c215189dff8ca38e53d9ec5dea.tar.bz2
libeufin-51300a1a75c137c215189dff8ca38e53d9ec5dea.zip
Share IbanPayto logic and improve full IBAN payto logic
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt4
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/Main.kt13
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt86
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt14
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt16
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt2
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt2
-rw-r--r--bank/src/main/kotlin/tech/libeufin/bank/helpers.kt2
-rw-r--r--bank/src/test/kotlin/CoreBankApiTest.kt26
-rw-r--r--bank/src/test/kotlin/DatabaseTest.kt34
-rw-r--r--bank/src/test/kotlin/helpers.kt14
-rw-r--r--common/src/main/kotlin/Cli.kt4
-rw-r--r--common/src/main/kotlin/IbanPayto.kt104
-rw-r--r--common/src/main/kotlin/TalerCommon.kt101
-rw-r--r--common/src/test/kotlin/PaytoTest.kt71
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt20
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt10
-rw-r--r--nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt11
-rw-r--r--nexus/src/test/kotlin/Common.kt4
-rw-r--r--nexus/src/test/kotlin/DatabaseTest.kt2
-rw-r--r--testbench/src/main/kotlin/Main.kt6
-rw-r--r--testbench/src/test/kotlin/IntegrationTest.kt47
22 files changed, 240 insertions, 353 deletions
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
index 099d4d82..d17842fe 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/CoreBankApi.kt
@@ -138,7 +138,7 @@ private fun Routing.coreBankTokenApi(db: Database) {
}
}
-suspend fun createAccount(db: Database, ctx: BankConfig, req: RegisterAccountRequest, isAdmin: Boolean): Pair<AccountCreationResult, IbanPayTo> {
+suspend fun createAccount(db: Database, ctx: BankConfig, req: RegisterAccountRequest, isAdmin: Boolean): Pair<AccountCreationResult, IbanPayto> {
// Prohibit reserved usernames:
if (RESERVED_ACCOUNTS.contains(req.username))
throw conflict(
@@ -183,7 +183,7 @@ suspend fun createAccount(db: Database, ctx: BankConfig, req: RegisterAccountReq
var retry = if (req.payto_uri == null) IBAN_ALLOCATION_RETRY_COUNTER else 0
while (true) {
- val internalPayto = req.payto_uri ?: IbanPayTo(genIbanPaytoUri())
+ val internalPayto = req.payto_uri ?: IbanPayto(genIbanPaytoUri())
val res = db.account.create(
login = req.username,
name = req.name,
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
index c2f4e8b1..575efe6c 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/Main.kt
@@ -179,6 +179,7 @@ fun Application.corebankWebApp(db: Database, ctx: BankConfig) {
rootCause is CommonError -> when (rootCause) {
is CommonError.AmountFormat -> TalerErrorCode.BANK_BAD_FORMAT_AMOUNT
is CommonError.AmountNumberTooBig -> TalerErrorCode.BANK_NUMBER_TOO_BIG
+ is CommonError.IbanPayto -> TalerErrorCode.GENERIC_JSON_INVALID
}
else -> TalerErrorCode.GENERIC_JSON_INVALID
}
@@ -364,7 +365,7 @@ class EditAccount : CliktCommand(
private val email: String? by option(help = "E-Mail address used for TAN transmission")
private val phone: String? by option(help = "Phone number used for TAN transmission")
private val tan_channel: String? by option(help = "which channel TAN challenges should be sent to")
- private val cashout_payto_uri: IbanPayTo? by option(help = "Payto URI of a fiant account who receive cashout amount").convert { IbanPayTo(it) }
+ private val cashout_payto_uri: IbanPayto? by option(help = "Payto URI of a fiant account who receive cashout amount").convert { IbanPayto(it) }
private val debit_threshold: TalerAmount? by option(help = "Max debit allowed for this account").convert { TalerAmount(it) }
override fun run() = cliCmd(logger, common.log) {
@@ -425,13 +426,13 @@ class CreateAccountOption: OptionGroup() {
).flag()
val email: String? by option(help = "E-Mail address used for TAN transmission")
val phone: String? by option(help = "Phone number used for TAN transmission")
- val cashout_payto_uri: IbanPayTo? by option(
+ val cashout_payto_uri: IbanPayto? by option(
help = "Payto URI of a fiant account who receive cashout amount"
- ).convert { IbanPayTo(it) }
- val internal_payto_uri: IbanPayTo? by option(hidden = true).convert { IbanPayTo(it) }
- val payto_uri: IbanPayTo? by option(
+ ).convert { IbanPayto(it) }
+ val internal_payto_uri: IbanPayto? by option(hidden = true).convert { IbanPayto(it) }
+ val payto_uri: IbanPayto? by option(
help = "Payto URI of this account"
- ).convert { IbanPayTo(it) }
+ ).convert { IbanPayto(it) }
val debit_threshold: TalerAmount? by option(
help = "Max debit allowed for this account")
.convert { TalerAmount(it) }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
index 40736d09..389ebc8b 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerCommon.kt
@@ -319,88 +319,4 @@ class ExchangeUrl {
return ExchangeUrl(decoder.decodeString())
}
}
-}
-
-sealed class PaytoUri {
- abstract val amount: TalerAmount?
- abstract val message: String?
- abstract val receiverName: String?
-}
-
-// TODO x-taler-bank Payto
-
-@Serializable(with = IbanPayTo.Serializer::class)
-class IbanPayTo: PaytoUri {
- val parsed: URI
- val canonical: String
- val iban: String
- override val amount: TalerAmount?
- override val message: String?
- override val receiverName: String?
-
- constructor(raw: String) {
- parsed = URI(raw)
- require(parsed.scheme == "payto") { "expect a payto URI" }
- require(parsed.host == "iban") { "expect a IBAN payto URI" }
-
- val splitPath = parsed.path.split("/").filter { it.isNotEmpty() }
- require(splitPath.size < 3 && splitPath.isNotEmpty()) { "too many path segments" }
- val rawIban = if (splitPath.size == 1) splitPath[0] else splitPath[1]
- iban = rawIban.uppercase().replace(SEPARATOR, "")
- checkIban(iban)
- canonical = "payto://iban/$iban"
-
- val params = (parsed.query ?: "").parseUrlEncodedParameters();
- amount = params["amount"]?.run { TalerAmount(this) }
- message = params["message"]
- receiverName = params["receiver-name"]
- }
-
- /** Canonical IBAN payto with receiver-name parameter if present */
- fun maybeFull(): String {
- return canonical + if (receiverName != null) ("?receiver-name=" + receiverName.encodeURLParameter()) else ""
- }
-
- /** Canonical IBAN payto with receiver-name parameter, fail if absent */
- fun expectFull(): String {
- return canonical + "?receiver-name=" + receiverName!!.encodeURLParameter()
- }
-
- /** Canonical IBAN payto with receiver-name parameter set to [defaultName] if absent */
- fun fullOptName(defaultName: String): String {
- return canonical + "?receiver-name=" + (receiverName ?: defaultName).encodeURLParameter()
- }
-
- override fun toString(): String = canonical
-
- internal object Serializer : KSerializer<IbanPayTo> {
- override val descriptor: SerialDescriptor =
- PrimitiveSerialDescriptor("IbanPayTo", PrimitiveKind.STRING)
-
- override fun serialize(encoder: Encoder, value: IbanPayTo) {
- encoder.encodeString(value.parsed.toString())
- }
-
- override fun deserialize(decoder: Decoder): IbanPayTo {
- return IbanPayTo(decoder.decodeString())
- }
- }
-
- companion object {
- private val SEPARATOR = Regex("[\\ \\-]");
-
- fun checkIban(iban: String) {
- val builder = StringBuilder(iban.length + iban.asSequence().map { if (it.isDigit()) 1 else 2 }.sum())
- (iban.subSequence(4, iban.length).asSequence() + iban.subSequence(0, 4).asSequence()).forEach {
- if (it.isDigit()) {
- builder.append(it)
- } else {
- builder.append((it.code - 'A'.code) + 10)
- }
- }
- val str = builder.toString()
- val mod = str.toBigInteger().mod(97.toBigInteger()).toInt();
- if (mod != 1) throw badRequest("Iban malformed, modulo is $mod expected 1")
- }
- }
-}
+} \ No newline at end of file
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
index 170881e4..b4857085 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/TalerMessage.kt
@@ -172,8 +172,8 @@ data class RegisterAccountRequest(
val is_public: Boolean = false,
val is_taler_exchange: Boolean = false,
val contact_data: ChallengeContactData? = null,
- val cashout_payto_uri: IbanPayTo? = null,
- val payto_uri: IbanPayTo? = null,
+ val cashout_payto_uri: IbanPayto? = null,
+ val payto_uri: IbanPayto? = null,
val debit_threshold: TalerAmount? = null,
val tan_channel: TanChannel? = null,
)
@@ -189,7 +189,7 @@ data class RegisterAccountResponse(
@Serializable
data class AccountReconfiguration(
val contact_data: ChallengeContactData? = null,
- val cashout_payto_uri: Option<IbanPayTo?> = Option.None,
+ val cashout_payto_uri: Option<IbanPayto?> = Option.None,
val name: String? = null,
val is_public: Boolean? = null,
val debit_threshold: TalerAmount? = null,
@@ -358,7 +358,7 @@ data class AccountData(
@Serializable
data class TransactionCreateRequest(
- val payto_uri: IbanPayTo,
+ val payto_uri: IbanPayto,
val amount: TalerAmount?
)
@@ -440,7 +440,7 @@ data class BankWithdrawalOperationStatus(
@Serializable
data class BankWithdrawalOperationPostRequest(
val reserve_pub: EddsaPublicKey,
- val selected_exchange: IbanPayTo,
+ val selected_exchange: IbanPayto,
)
/**
@@ -523,7 +523,7 @@ data class ConversionResponse(
data class AddIncomingRequest(
val amount: TalerAmount,
val reserve_pub: EddsaPublicKey,
- val debit_account: IbanPayTo
+ val debit_account: IbanPayto
)
/**
@@ -611,7 +611,7 @@ data class TransferRequest(
val amount: TalerAmount,
val exchange_base_url: ExchangeUrl,
val wtid: ShortHashCode,
- val credit_account: IbanPayTo
+ val credit_account: IbanPayto
)
/**
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
index 5b7db6ad..cfd65156 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/AccountDAO.kt
@@ -41,8 +41,8 @@ class AccountDAO(private val db: Database) {
name: String,
email: String?,
phone: String?,
- cashoutPayto: IbanPayTo?,
- internalPaytoUri: IbanPayTo,
+ cashoutPayto: IbanPayto?,
+ internalPaytoUri: IbanPayto,
isPublic: Boolean,
isTalerExchange: Boolean,
maxDebt: TalerAmount,
@@ -71,7 +71,7 @@ class AccountDAO(private val db: Database) {
setString(1, name)
setString(2, email)
setString(3, phone)
- setString(4, cashoutPayto?.fullOptName(name))
+ setString(4, cashoutPayto?.full(name)?.full)
setBoolean(5, checkPaytoIdempotent)
setString(6, internalPaytoUri.canonical)
setBoolean(7, isPublic)
@@ -122,7 +122,7 @@ class AccountDAO(private val db: Database) {
setString(3, name)
setString(4, email)
setString(5, phone)
- setString(6, cashoutPayto?.fullOptName(name))
+ setString(6, cashoutPayto?.full(name)?.full)
setString(7, tanChannel?.name)
oneOrNull { it.getLong("customer_id") }!!
}
@@ -223,7 +223,7 @@ class AccountDAO(private val db: Database) {
suspend fun reconfig(
login: String,
name: String?,
- cashoutPayto: Option<IbanPayTo?>,
+ cashoutPayto: Option<IbanPayto?>,
phone: Option<String?>,
email: Option<String?>,
tan_channel: Option<TanChannel?>,
@@ -292,12 +292,12 @@ class AccountDAO(private val db: Database) {
null -> null
}
// Cashout payto with a receiver-name using if receiver-name is missing the new named if present or the current one
- val cashoutPaytoNamed = cashoutPayto.get()?.fullOptName(name ?: curr.name)
+ val fullCashoutPayto = cashoutPayto.get()?.full(name ?: curr.name)
// Check reconfig rights
if (checkName && name != curr.name)
return@transaction AccountPatchResult.NonAdminName
- if (checkCashout && cashoutPaytoNamed != curr.cashoutPayTo)
+ if (checkCashout && fullCashoutPayto?.full != curr.cashoutPayTo)
return@transaction AccountPatchResult.NonAdminCashout
if (checkDebtLimit && debtLimit != curr.debtLimit)
return@transaction AccountPatchResult.NonAdminDebtLimit
@@ -352,7 +352,7 @@ class AccountDAO(private val db: Database) {
},
"WHERE customer_id = ?",
sequence {
- cashoutPayto.some { yield(cashoutPaytoNamed) }
+ cashoutPayto.some { yield(fullCashoutPayto?.full) }
phone.some { yield(it) }
email.some { yield(it) }
tan_channel.some { yield(it?.name) }
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
index b4f5c025..b6038dc5 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/TransactionDAO.kt
@@ -43,7 +43,7 @@ class TransactionDAO(private val db: Database) {
/** Create a new transaction */
suspend fun create(
- creditAccountPayto: IbanPayTo,
+ creditAccountPayto: IbanPayto,
debitAccountUsername: String,
subject: String,
amount: TalerAmount,
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt b/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt
index 13dcbff3..72b92490 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/db/WithdrawalDAO.kt
@@ -102,7 +102,7 @@ class WithdrawalDAO(private val db: Database) {
/** Set details ([exchangePayto] & [reservePub]) for withdrawal operation [uuid] */
suspend fun setDetails(
uuid: UUID,
- exchangePayto: IbanPayTo,
+ exchangePayto: IbanPayto,
reservePub: EddsaPublicKey
): WithdrawalSelectionResult = db.serializable { conn ->
val stmt = conn.prepareStatement("""
diff --git a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
index 16799103..b40223c6 100644
--- a/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
+++ b/bank/src/main/kotlin/tech/libeufin/bank/helpers.kt
@@ -125,7 +125,7 @@ suspend fun maybeCreateAdminAccount(db: Database, ctx: BankConfig, pw: String? =
login = "admin",
password = pwStr,
name = "Bank administrator",
- internalPaytoUri = IbanPayTo(genIbanPaytoUri()),
+ internalPaytoUri = IbanPayto(genIbanPaytoUri()),
isPublic = false,
isTalerExchange = false,
maxDebt = ctx.defaultDebtLimit,
diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt
index 0df15208..bd001feb 100644
--- a/bank/src/test/kotlin/CoreBankApiTest.kt
+++ b/bank/src/test/kotlin/CoreBankApiTest.kt
@@ -193,20 +193,20 @@ class CoreBankAccountsApiTest {
}
// Check given payto
- val ibanPayto = IbanPayTo(genIbanPaytoUri())
+ val IbanPayto = IbanPayto(genIbanPaytoUri())
val req = obj {
"username" to "foo"
"password" to "password"
"name" to "Jane"
"is_public" to true
- "payto_uri" to ibanPayto
+ "payto_uri" to IbanPayto
"is_taler_exchange" to true
}
// Check Ok
client.post("/accounts") {
json(req)
}.assertOkJson<RegisterAccountResponse> {
- assertEquals(ibanPayto.canonical, it.internal_payto_uri)
+ assertEquals(IbanPayto.canonical, it.internal_payto_uri)
}
// Testing idempotency
client.post("/accounts") {
@@ -303,13 +303,13 @@ class CoreBankAccountsApiTest {
"username" to "cashout_guess"
"password" to "cashout_guess-password"
"name" to "Mr Guess My Name"
- "cashout_payto_uri" to ibanPayto
+ "cashout_payto_uri" to IbanPayto
}
}.assertOk()
client.getA("/accounts/cashout_guess").assertOkJson<AccountData> {
- assertEquals(ibanPayto.fullOptName("Mr Guess My Name"), it.cashout_payto_uri)
+ assertEquals(IbanPayto.full("Mr Guess My Name").full, it.cashout_payto_uri)
}
- val full = ibanPayto.fullOptName("Santa Claus")
+ val full = IbanPayto.full("Santa Claus").full
client.post("/accounts") {
json {
"username" to "cashout_keep"
@@ -486,7 +486,7 @@ class CoreBankAccountsApiTest {
}
// Successful attempt now
- val cashout = IbanPayTo(genIbanPaytoUri())
+ val cashout = IbanPayto(genIbanPaytoUri())
val req = obj {
"cashout_payto_uri" to cashout
"name" to "Roger"
@@ -519,7 +519,7 @@ class CoreBankAccountsApiTest {
// Check patch
client.getA("/accounts/merchant").assertOkJson<AccountData> { obj ->
assertEquals("Roger", obj.name)
- assertEquals(cashout.fullOptName(obj.name), obj.cashout_payto_uri)
+ assertEquals(cashout.full(obj.name).full, obj.cashout_payto_uri)
assertEquals("+99", obj.contact_data?.phone?.get())
assertEquals("foo@example.com", obj.contact_data?.email?.get())
assertEquals(TalerAmount("KUDOS:100"), obj.debit_threshold)
@@ -533,7 +533,7 @@ class CoreBankAccountsApiTest {
}.assertNoContent()
client.getA("/accounts/merchant").assertOkJson<AccountData> { obj ->
assertEquals("Roger", obj.name)
- assertEquals(cashout.fullOptName(obj.name), obj.cashout_payto_uri)
+ assertEquals(cashout.full(obj.name).full, obj.cashout_payto_uri)
assertEquals("+99", obj.contact_data?.phone?.get())
assertEquals("foo@example.com", obj.contact_data?.email?.get())
assertEquals(TalerAmount("KUDOS:100"), obj.debit_threshold)
@@ -557,10 +557,10 @@ class CoreBankAccountsApiTest {
}
}.assertOk()
for ((cashout, name, expect) in listOf(
- Triple(cashout.canonical, null, cashout.fullOptName("Mr Cashout Cashout")),
- Triple(cashout.canonical, "New name", cashout.fullOptName("New name")),
- Triple(cashout.fullOptName("Full name"), null, cashout.fullOptName("Full name")),
- Triple(cashout.fullOptName("Full second name"), "Another name", cashout.fullOptName("Full second name"))
+ Triple(cashout.canonical, null, cashout.full("Mr Cashout Cashout").full),
+ Triple(cashout.canonical, "New name", cashout.full("New name").full),
+ Triple(cashout.full("Full name").full, null, cashout.full("Full name").full),
+ Triple(cashout.full("Full second name").full, "Another name", cashout.full("Full second name").full)
)) {
client.patch("/accounts/cashout") {
pwAuth("admin")
diff --git a/bank/src/test/kotlin/DatabaseTest.kt b/bank/src/test/kotlin/DatabaseTest.kt
index f03e151c..01cb0dd2 100644
--- a/bank/src/test/kotlin/DatabaseTest.kt
+++ b/bank/src/test/kotlin/DatabaseTest.kt
@@ -168,40 +168,6 @@ class DatabaseTest {
assertEquals(Triple(true, false, false), cTry(this, "new-code", expired))
}
}}
-
- // Testing iban payto uri normalization
- @Test
- fun ibanPayto() = setup { _, _ ->
- val canonical = "payto://iban/CH9300762011623852957"
- val inputs = listOf(
- "payto://iban/BIC/CH9300762011623852957?receiver-name=NotGiven",
- "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
- "payto://iban/ch%209300-7620-1162-3852-957",
- )
- val names = listOf(
- "NotGiven", "Grothoff Hans", null
- )
- val full = listOf(
- "payto://iban/CH9300762011623852957?receiver-name=NotGiven",
- "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
- canonical
- )
- for ((i, input) in inputs.withIndex()) {
- val payto = IbanPayTo(input)
- assertEquals(canonical, payto.canonical)
- assertEquals(full[i], payto.maybeFull())
- assertEquals(names[i], payto.receiverName)
- }
-
- assertEquals(
- "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
- IbanPayTo("payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans").fullOptName("Santa Claus")
- )
- assertEquals(
- "payto://iban/CH9300762011623852957?receiver-name=Santa%20Claus",
- IbanPayTo("payto://iban/CH9300762011623852957").fullOptName("Santa Claus")
- )
- }
}
diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt
index fac02408..69c0ede9 100644
--- a/bank/src/test/kotlin/helpers.kt
+++ b/bank/src/test/kotlin/helpers.kt
@@ -36,19 +36,19 @@ import tech.libeufin.common.*
/* ----- Setup ----- */
-val merchantPayto = IbanPayTo(genIbanPaytoUri())
-val exchangePayto = IbanPayTo(genIbanPaytoUri())
-val customerPayto = IbanPayTo(genIbanPaytoUri())
-val unknownPayto = IbanPayTo(genIbanPaytoUri())
-var tmpPayTo = IbanPayTo(genIbanPaytoUri())
+val merchantPayto = IbanPayto(genIbanPaytoUri())
+val exchangePayto = IbanPayto(genIbanPaytoUri())
+val customerPayto = IbanPayto(genIbanPaytoUri())
+val unknownPayto = IbanPayto(genIbanPaytoUri())
+var tmpPayTo = IbanPayto(genIbanPaytoUri())
val paytos = mapOf(
"merchant" to merchantPayto,
"exchange" to exchangePayto,
"customer" to customerPayto
)
-fun genTmpPayTo(): IbanPayTo {
- tmpPayTo = IbanPayTo(genIbanPaytoUri())
+fun genTmpPayTo(): IbanPayto {
+ tmpPayTo = IbanPayto(genIbanPaytoUri())
return tmpPayTo
}
diff --git a/common/src/main/kotlin/Cli.kt b/common/src/main/kotlin/Cli.kt
index d0a61e7e..2db1e906 100644
--- a/common/src/main/kotlin/Cli.kt
+++ b/common/src/main/kotlin/Cli.kt
@@ -38,11 +38,11 @@ fun cliCmd(logger: Logger, level: Level, lambda: () -> Unit) {
try {
lambda()
} catch (e: Throwable) {
- var msg = StringBuilder(e.message)
+ var msg = StringBuilder(e.message ?: e::class.simpleName)
var cause = e.cause;
while (cause != null) {
msg.append(": ")
- msg.append(cause.message)
+ msg.append(cause.message ?: cause::class.simpleName)
cause = cause.cause
}
logger.error(msg.toString())
diff --git a/common/src/main/kotlin/IbanPayto.kt b/common/src/main/kotlin/IbanPayto.kt
deleted file mode 100644
index d3ab7576..00000000
--- a/common/src/main/kotlin/IbanPayto.kt
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * This file is part of LibEuFin.
- * Copyright (C) 2024 Taler Systems S.A.
-
- * LibEuFin is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation; either version 3, or
- * (at your option) any later version.
-
- * LibEuFin is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
- * Public License for more details.
-
- * You should have received a copy of the GNU Affero General Public
- * License along with LibEuFin; see the file COPYING. If not, see
- * <http://www.gnu.org/licenses/>
- */
-
-package tech.libeufin.common
-
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import java.net.URI
-import java.net.URLDecoder
-
-private val logger: Logger = LoggerFactory.getLogger("libeufin-common")
-
-// Payto information.
-data class IbanPayto(
- // represent query param "sender-name" or "receiver-name".
- val receiverName: String?,
- val iban: String,
- val bic: String?,
- // Typically, a wire transfer's subject.
- val message: String?,
- val amount: String?
-)
-
-// Return the value of query string parameter 'name', or null if not found.
-// 'params' is the list of key-value elements of all the query parameters found in the URI.
-private fun getQueryParamOrNull(name: String, params: List<Pair<String, String>>?): String? {
- if (params == null) return null
- return params.firstNotNullOfOrNull { pair ->
- URLDecoder.decode(pair.second, Charsets.UTF_8).takeIf { pair.first == name }
- }
-}
-
-// Parses a Payto URI, returning null if the input is invalid.
-fun parsePayto(payto: String): IbanPayto? {
- /**
- * This check is due because URIs having a "payto:" prefix without
- * slashes are correctly parsed by the Java 'URI' class. 'mailto'
- * for example lacks the double-slash part.
- */
- if (!payto.startsWith("payto://")) {
- logger.error("Invalid payto URI: $payto")
- return null
- }
-
- val javaParsedUri = try {
- URI(payto)
- } catch (e: java.lang.Exception) {
- logger.error("'${payto}' is not a valid URI")
- return null
- }
- if (javaParsedUri.scheme != "payto") {
- logger.error("'${payto}' is not payto")
- return null
- }
- val wireMethod = javaParsedUri.host
- if (wireMethod != "iban") {
- logger.error("Only 'iban' is supported, not '$wireMethod'")
- return null
- }
- val splitPath = javaParsedUri.path.split("/").filter { it.isNotEmpty() }
- if (splitPath.size > 2) {
- logger.error("too many path segments in iban payto URI: $payto")
- return null
- }
- val (iban, bic) = if (splitPath.size == 1) {
- Pair(splitPath[0], null)
- } else Pair(splitPath[1], splitPath[0])
-
- val params: List<Pair<String, String>>? = if (javaParsedUri.query != null) {
- val queryString: List<String> = javaParsedUri.query.split("&")
- queryString.map {
- val split = it.split("=");
- if (split.size != 2) {
- logger.error("parameter '$it' was malformed")
- return null
- }
- Pair(split[0], split[1])
- }
- } else null
-
- return IbanPayto(
- iban = iban,
- bic = bic,
- amount = getQueryParamOrNull("amount", params),
- message = getQueryParamOrNull("message", params),
- receiverName = getQueryParamOrNull("receiver-name", params)
- )
-} \ No newline at end of file
diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt
index 96d20a86..76c8d801 100644
--- a/common/src/main/kotlin/TalerCommon.kt
+++ b/common/src/main/kotlin/TalerCommon.kt
@@ -23,10 +23,13 @@ import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
+import io.ktor.http.*
+import java.net.URI
sealed class CommonError(msg: String): Exception(msg) {
class AmountFormat(msg: String): CommonError(msg)
class AmountNumberTooBig(msg: String): CommonError(msg)
+ class IbanPayto(msg: String): CommonError(msg)
}
@Serializable(with = TalerAmount.Serializer::class)
@@ -97,4 +100,102 @@ class TalerAmount {
const val MAX_VALUE = 4503599627370496L; // 2^52
private val PATTERN = Regex("([A-Z]{1,11}):([0-9]+)(?:\\.([0-9]{1,8}))?");
}
+}
+
+
+sealed class PaytoUri {
+ abstract val amount: TalerAmount?
+ abstract val message: String?
+ abstract val receiverName: String?
+}
+
+// TODO x-taler-bank Payto
+
+@Serializable(with = IbanPayto.Serializer::class)
+class IbanPayto: PaytoUri {
+ val parsed: URI
+ val canonical: String
+ val iban: String
+ override val amount: TalerAmount?
+ override val message: String?
+ override val receiverName: String?
+
+ constructor(raw: String) {
+ println(raw)
+ try {
+ parsed = URI(raw)
+ } catch (e: Exception) {
+ throw CommonError.IbanPayto("expecteda valid URI")
+ }
+
+ if (parsed.scheme != "payto") throw CommonError.IbanPayto("expect a payto URI")
+ if (parsed.host != "iban") throw CommonError.IbanPayto("expect a IBAN payto URI")
+
+ val splitPath = parsed.path.split("/").filter { it.isNotEmpty() }
+ val rawIban = when (splitPath.size) {
+ 1 -> splitPath[0]
+ 2 -> splitPath[1]
+ else -> throw CommonError.IbanPayto("too many path segments")
+ }
+ iban = rawIban.uppercase().replace(SEPARATOR, "")
+ checkIban(iban)
+ canonical = "payto://iban/$iban"
+
+ val params = (parsed.query ?: "").parseUrlEncodedParameters();
+ amount = params["amount"]?.run { TalerAmount(this) }
+ message = params["message"]
+ receiverName = params["receiver-name"]
+ }
+
+ /** Full IBAN payto with receiver-name parameter if present */
+ fun maybeFull(): FullIbanPayto? {
+ return FullIbanPayto(this, receiverName ?: return null)
+ }
+
+ /** Full IBAN payto with receiver-name parameter if present, fail if absent */
+ fun requireFull(): FullIbanPayto {
+ return maybeFull() ?: throw Exception("Missing receiver-name")
+ }
+
+ /** Full IBAN payto with receiver-name parameter set to [defaultName] if absent */
+ fun full(defaultName: String): FullIbanPayto {
+ return FullIbanPayto(this, receiverName ?: defaultName)
+ }
+
+ override fun toString(): String = canonical
+
+ internal object Serializer : KSerializer<IbanPayto> {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("IbanPayto", PrimitiveKind.STRING)
+
+ override fun serialize(encoder: Encoder, value: IbanPayto) {
+ encoder.encodeString(value.parsed.toString())
+ }
+
+ override fun deserialize(decoder: Decoder): IbanPayto {
+ return IbanPayto(decoder.decodeString())
+ }
+ }
+
+ companion object {
+ private val SEPARATOR = Regex("[\\ \\-]");
+
+ fun checkIban(iban: String) {
+ val builder = StringBuilder(iban.length + iban.asSequence().map { if (it.isDigit()) 1 else 2 }.sum())
+ (iban.subSequence(4, iban.length).asSequence() + iban.subSequence(0, 4).asSequence()).forEach {
+ if (it.isDigit()) {
+ builder.append(it)
+ } else {
+ builder.append((it.code - 'A'.code) + 10)
+ }
+ }
+ val str = builder.toString()
+ val mod = str.toBigInteger().mod(97.toBigInteger()).toInt();
+ if (mod != 1) throw CommonError.IbanPayto("Iban malformed, modulo is $mod expected 1")
+ }
+ }
+}
+
+class FullIbanPayto(val payto: IbanPayto, val receiverName: String) {
+ val full = payto.canonical + "?receiver-name=" + receiverName.encodeURLParameter()
} \ No newline at end of file
diff --git a/common/src/test/kotlin/PaytoTest.kt b/common/src/test/kotlin/PaytoTest.kt
index a18dafe0..e1a6b976 100644
--- a/common/src/test/kotlin/PaytoTest.kt
+++ b/common/src/test/kotlin/PaytoTest.kt
@@ -18,34 +18,63 @@
*/
import org.junit.Test
-import tech.libeufin.common.IbanPayto
-import tech.libeufin.common.parsePayto
+import tech.libeufin.common.*
+import kotlin.test.*
class PaytoTest {
-
@Test
fun wrongCases() {
- assert(parsePayto("http://iban/BIC123/IBAN123?receiver-name=The%20Name") == null)
- assert(parsePayto("payto:iban/BIC123/IBAN123?receiver-name=The%20Name&address=house") == null)
- assert(parsePayto("payto://wrong/BIC123/IBAN123?sender-name=Foo&receiver-name=Foo") == null)
+ assertFailsWith<CommonError.IbanPayto> { IbanPayto("http://iban/BIC123/IBAN123?receiver-name=The%20Name") }
+ assertFailsWith<CommonError.IbanPayto> { IbanPayto("payto:iban/BIC123/IBAN123?receiver-name=The%20Name&address=house") }
+ assertFailsWith<CommonError.IbanPayto> { IbanPayto("payto://wrong/BIC123/IBAN123?sender-name=Foo&receiver-name=Foo") }
}
@Test
fun parsePaytoTest() {
- val withBic: IbanPayto = parsePayto("payto://iban/BIC123/IBAN123?receiver-name=The%20Name")!!
- assert(withBic.iban == "IBAN123")
- assert(withBic.bic == "BIC123")
- assert(withBic.receiverName == "The Name")
- val complete = parsePayto("payto://iban/BIC123/IBAN123?sender-name=The%20Name&amount=EUR:1&message=donation")!!
- assert(withBic.iban == "IBAN123")
- assert(withBic.bic == "BIC123")
- assert(withBic.receiverName == "The Name")
- assert(complete.message == "donation")
- assert(complete.amount == "EUR:1")
- val withoutOptionals = parsePayto("payto://iban/IBAN123")!!
- assert(withoutOptionals.bic == null)
- assert(withoutOptionals.message == null)
- assert(withoutOptionals.receiverName == null)
- assert(withoutOptionals.amount == null)
+ val withBic = IbanPayto("payto://iban/BIC123/CH9300762011623852957?receiver-name=The%20Name")
+ assertEquals(withBic.iban, "CH9300762011623852957")
+ assertEquals(withBic.receiverName, "The Name")
+ val complete = IbanPayto("payto://iban/BIC123/CH9300762011623852957?sender-name=The%20Name&amount=EUR:1&message=donation")
+ assertEquals(withBic.iban, "CH9300762011623852957")
+ assertEquals(withBic.receiverName, "The Name")
+ assertEquals(complete.message, "donation")
+ assertEquals(complete.amount.toString(), "EUR:1")
+ val withoutOptionals = IbanPayto("payto://iban/CH9300762011623852957")
+ assertNull(withoutOptionals.message)
+ assertNull(withoutOptionals.receiverName)
+ assertNull(withoutOptionals.amount)
+ }
+
+ @Test
+ fun normalization() {
+ val canonical = "payto://iban/CH9300762011623852957"
+ val inputs = listOf(
+ "payto://iban/BIC/CH9300762011623852957?receiver-name=NotGiven",
+ "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
+ "payto://iban/ch%209300-7620-1162-3852-957",
+ )
+ val names = listOf(
+ "NotGiven", "Grothoff Hans", null
+ )
+ val full = listOf(
+ "payto://iban/CH9300762011623852957?receiver-name=NotGiven",
+ "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
+ canonical
+ )
+ for ((i, input) in inputs.withIndex()) {
+ val payto = IbanPayto(input)
+ assertEquals(canonical, payto.canonical)
+ assertEquals(full[i], payto.maybeFull()?.full ?: payto.canonical)
+ assertEquals(names[i], payto.receiverName)
+ }
+
+ assertEquals(
+ "payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans",
+ IbanPayto("payto://iban/CH9300762011623852957?receiver-name=Grothoff%20Hans").full("Santa Claus").full
+ )
+ assertEquals(
+ "payto://iban/CH9300762011623852957?receiver-name=Santa%20Claus",
+ IbanPayto("payto://iban/CH9300762011623852957").full("Santa Claus").full
+ )
}
} \ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
index be913d38..82d493e8 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Database.kt
@@ -88,7 +88,7 @@ enum class DatabaseSubmissionState {
data class InitiatedPayment(
val amount: TalerAmount,
val wireTransferSubject: String,
- val creditPaytoUri: String,
+ val creditPaytoUri: FullIbanPayto,
val initiationTime: Instant,
val requestUid: String
)
@@ -98,16 +98,6 @@ data class InitiatedPayment(
* into the database.
*/
enum class PaymentInitiationOutcome {
- /**
- * The Payto address to send the payment to was invalid.
- */
- BAD_CREDIT_PAYTO,
-
- /**
- * The receiver payto address lacks the name, that would
- * cause the bank to reject the pain.001.
- */
- RECEIVER_NAME_MISSING,
/**
* The row contains a client_request_uid that exists
@@ -461,7 +451,7 @@ class Database(dbConfig: String): DbPool(dbConfig, "libeufin_nexus") {
}
maybeMap[rowId] = InitiatedPayment(
amount = it.getAmount("amount", currency),
- creditPaytoUri = it.getString("credit_payto_uri"),
+ creditPaytoUri = IbanPayto(it.getString("credit_payto_uri")).requireFull(),
wireTransferSubject = it.getString("wire_transfer_subject"),
initiationTime = initiationTime,
requestUid = it.getString("request_uid")
@@ -497,11 +487,7 @@ class Database(dbConfig: String): DbPool(dbConfig, "libeufin_nexus") {
stmt.setLong(1, paymentData.amount.value)
stmt.setInt(2, paymentData.amount.frac)
stmt.setString(3, paymentData.wireTransferSubject)
- parsePayto(paymentData.creditPaytoUri).apply {
- if (this == null) return@conn PaymentInitiationOutcome.BAD_CREDIT_PAYTO
- if (this.receiverName == null) return@conn PaymentInitiationOutcome.RECEIVER_NAME_MISSING
- }
- stmt.setString(4, paymentData.creditPaytoUri)
+ stmt.setString(4, paymentData.creditPaytoUri.full)
val initiationTime = paymentData.initiationTime.toDbMicros() ?: run {
throw Exception("Initiation time could not be converted to microseconds for the database.")
}
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
index c43bb378..dbd178b3 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/EbicsSubmit.kt
@@ -97,18 +97,12 @@ class NexusSubmitException(
private suspend fun submitInitiatedPayment(
ctx: SubmissionContext,
initiatedPayment: InitiatedPayment
-) {
- val creditor = parsePayto(initiatedPayment.creditPaytoUri)
- if (creditor?.receiverName == null)
- throw NexusSubmitException(
- "Won't create pain.001 without the receiver name",
- stage = NexusSubmissionStage.pain
- )
+) {
val xml = createPain001(
requestUid = initiatedPayment.requestUid,
initiationTimestamp = initiatedPayment.initiationTime,
amount = initiatedPayment.amount,
- creditAccount = creditor,
+ creditAccount = initiatedPayment.creditPaytoUri,
debitAccount = ctx.cfg.myIbanAccount,
wireTransferSubject = initiatedPayment.wireTransferSubject
)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
index 3edfbded..005b010d 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Iso20022.kt
@@ -77,7 +77,7 @@ fun createPain001(
debitAccount: IbanAccountMetadata,
amount: TalerAmount,
wireTransferSubject: String,
- creditAccount: IbanPayto
+ creditAccount: FullIbanPayto
): String {
val namespace = Pain001Namespaces(
fullNamespace = "urn:iso:std:iso:20022:tech:xsd:pain.001.001.09",
@@ -85,11 +85,6 @@ fun createPain001(
)
val zonedTimestamp = ZonedDateTime.ofInstant(initiationTimestamp, ZoneId.of("UTC"))
val amountWithoutCurrency: String = getAmountNoCurrency(amount)
- val creditorName: String = creditAccount.receiverName
- ?: throw NexusSubmitException(
- "Cannot operate without the creditor name",
- stage=NexusSubmissionStage.pain
- )
return constructXml {
root("Document") {
attribute(
@@ -157,10 +152,10 @@ fun createPain001(
text(amountWithoutCurrency)
}
element("Cdtr/Nm") {
- text(creditorName)
+ text(creditAccount.receiverName)
}
element("CdtrAcct/Id/IBAN") {
- text(creditAccount.iban)
+ text(creditAccount.payto.iban)
}
element("RmtInf/Ustrd") {
text(wireTransferSubject)
diff --git a/nexus/src/test/kotlin/Common.kt b/nexus/src/test/kotlin/Common.kt
index 7812499a..f5c2f07e 100644
--- a/nexus/src/test/kotlin/Common.kt
+++ b/nexus/src/test/kotlin/Common.kt
@@ -103,7 +103,7 @@ fun genInitPay(
) =
InitiatedPayment(
amount = TalerAmount(44, 0, "KUDOS"),
- creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test",
+ creditPaytoUri = IbanPayto("payto://iban/CH9300762011623852957?receiver-name=Test").requireFull(),
wireTransferSubject = subject,
initiationTime = Instant.now(),
requestUid = requestUid
@@ -123,7 +123,7 @@ fun genInPay(subject: String) =
fun genOutPay(subject: String, messageId: String) =
OutgoingPayment(
amount = TalerAmount(44, 0, "KUDOS"),
- creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test",
+ creditPaytoUri = "payto://iban/CH9300762011623852957?receiver-name=Test",
wireTransferSubject = subject,
executionTime = Instant.now(),
messageId = messageId
diff --git a/nexus/src/test/kotlin/DatabaseTest.kt b/nexus/src/test/kotlin/DatabaseTest.kt
index 1d3ffcd7..58fa9735 100644
--- a/nexus/src/test/kotlin/DatabaseTest.kt
+++ b/nexus/src/test/kotlin/DatabaseTest.kt
@@ -206,7 +206,7 @@ class PaymentInitiationsTest {
}
val initPay = InitiatedPayment(
amount = TalerAmount(44, 0, "KUDOS"),
- creditPaytoUri = "payto://iban/TEST-IBAN?receiver-name=Test",
+ creditPaytoUri = IbanPayto("payto://iban/CH9300762011623852957?receiver-name=Test").requireFull(),
wireTransferSubject = "test",
requestUid = "unique",
initiationTime = Instant.now()
diff --git a/testbench/src/main/kotlin/Main.kt b/testbench/src/main/kotlin/Main.kt
index b1c549c2..fef935b9 100644
--- a/testbench/src/main/kotlin/Main.kt
+++ b/testbench/src/main/kotlin/Main.kt
@@ -135,7 +135,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
step("Test submit one transaction")
nexusDb.initiatedPaymentCreate(InitiatedPayment(
amount = TalerAmount("CFH:42"),
- creditPaytoUri = payto,
+ creditPaytoUri = IbanPayto(payto).requireFull(),
wireTransferSubject = "single transaction test",
initiationTime = Instant.now(),
requestUid = Base32Crockford.encode(randBytes(16))
@@ -147,7 +147,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
repeat(4) {
nexusDb.initiatedPaymentCreate(InitiatedPayment(
amount = TalerAmount("CFH:${100L+it}"),
- creditPaytoUri = payto,
+ creditPaytoUri = IbanPayto(payto).requireFull(),
wireTransferSubject = "multi transaction test $it",
initiationTime = Instant.now(),
requestUid = Base32Crockford.encode(randBytes(16))
@@ -198,7 +198,7 @@ class Cli : CliktCommand("Run integration tests on banks provider") {
// TODO interactive payment editor
nexusDb.initiatedPaymentCreate(InitiatedPayment(
amount = TalerAmount("CFH:1.1"),
- creditPaytoUri = "payto://iban/CH6208704048981247126?receiver-name=Grothoff%20Hans",
+ creditPaytoUri = IbanPayto("payto://iban/CH6208704048981247126?receiver-name=Grothoff%20Hans").requireFull(),
wireTransferSubject = "single transaction test",
initiationTime = Instant.now(),
requestUid = Base32Crockford.encode(randBytes(16))
diff --git a/testbench/src/test/kotlin/IntegrationTest.kt b/testbench/src/test/kotlin/IntegrationTest.kt
index 87f75baa..dd4023aa 100644
--- a/testbench/src/test/kotlin/IntegrationTest.kt
+++ b/testbench/src/test/kotlin/IntegrationTest.kt
@@ -101,12 +101,13 @@ class IntegrationTest {
@Test
fun mini() {
- bankCmd.run("dbinit -c conf/mini.conf -r")
- bankCmd.run("passwd admin password -c conf/mini.conf")
- bankCmd.run("dbinit -c conf/mini.conf") // Indempotent
+ val flags = "-c conf/mini.conf -L DEBUG"
+ bankCmd.run("dbinit $flags -r")
+ bankCmd.run("passwd admin password $flags")
+ bankCmd.run("dbinit $flags") // Indempotent
server {
- bankCmd.run("serve -c conf/mini.conf")
+ bankCmd.run("serve $flags")
}
setup { _ ->
@@ -117,9 +118,10 @@ class IntegrationTest {
@Test
fun errors() {
- nexusCmd.run("dbinit -c conf/integration.conf -r")
- bankCmd.run("dbinit -c conf/integration.conf -r")
- bankCmd.run("passwd admin password -c conf/integration.conf")
+ val flags = "-c conf/integration.conf -L DEBUG"
+ nexusCmd.run("dbinit $flags -r")
+ bankCmd.run("dbinit $flags -r")
+ bankCmd.run("passwd admin password $flags")
suspend fun checkCount(db: NexusDb, nbIncoming: Int, nbBounce: Int, nbTalerable: Int) {
db.conn { conn ->
@@ -136,8 +138,8 @@ class IntegrationTest {
}
setup { db ->
- val userPayTo = IbanPayTo(genIbanPaytoUri())
- val fiatPayTo = IbanPayTo(genIbanPaytoUri())
+ val userPayTo = IbanPayto(genIbanPaytoUri())
+ val fiatPayTo = IbanPayto(genIbanPaytoUri())
// Load conversion setup manually as the server would refuse to start without an exchange account
val sqlProcedures = File("../database-versioning/libeufin-conversion-setup.sql")
@@ -160,7 +162,7 @@ class IntegrationTest {
}
// Create exchange account
- bankCmd.run("create-account -c conf/integration.conf -u exchange -p password --name 'Mr Money' --exchange")
+ bankCmd.run("create-account $flags -u exchange -p password --name 'Mr Money' --exchange")
assertException("ERROR: cashin currency conversion failed: missing conversion rates") {
ingestIncomingPayment(db, payment)
@@ -168,7 +170,7 @@ class IntegrationTest {
// Start server
server {
- bankCmd.run("serve -c conf/integration.conf")
+ bankCmd.run("serve $flags")
}
// Set conversion rates
@@ -193,7 +195,7 @@ class IntegrationTest {
}
// Allow admin debt
- bankCmd.run("edit-account admin --debit_threshold KUDOS:100 -c conf/integration.conf")
+ bankCmd.run("edit-account admin --debit_threshold KUDOS:100 $flags")
// Too small amount
checkCount(db, 0, 0, 0)
@@ -224,21 +226,22 @@ class IntegrationTest {
@Test
fun conversion() {
- nexusCmd.run("dbinit -c conf/integration.conf -r")
- bankCmd.run("dbinit -c conf/integration.conf -r")
- bankCmd.run("passwd admin password -c conf/integration.conf")
- bankCmd.run("edit-account admin --debit_threshold KUDOS:1000 -c conf/integration.conf")
- bankCmd.run("create-account -c conf/integration.conf -u exchange -p password --name 'Mr Money' --exchange")
- nexusCmd.run("dbinit -c conf/integration.conf") // Idempotent
- bankCmd.run("dbinit -c conf/integration.conf") // Idempotent
+ val flags = "-c conf/integration.conf -L DEBUG"
+ nexusCmd.run("dbinit $flags -r")
+ bankCmd.run("dbinit $flags -r")
+ bankCmd.run("passwd admin password $flags")
+ bankCmd.run("edit-account admin --debit_threshold KUDOS:1000 $flags")
+ bankCmd.run("create-account $flags -u exchange -p password --name 'Mr Money' --exchange")
+ nexusCmd.run("dbinit $flags") // Idempotent
+ bankCmd.run("dbinit $flags") // Idempotent
server {
- bankCmd.run("serve -c conf/integration.conf")
+ bankCmd.run("serve $flags")
}
setup { db ->
- val userPayTo = IbanPayTo(genIbanPaytoUri())
- val fiatPayTo = IbanPayTo(genIbanPaytoUri())
+ val userPayTo = IbanPayto(genIbanPaytoUri())
+ val fiatPayTo = IbanPayto(genIbanPaytoUri())
// Create user
client.post("http://0.0.0.0:8080/accounts") {