commit 8ad947fd464260822cec3dc9b0f6492f903f81d1
parent 65e2e51fa87b6222fb1b3818deea8ed200d3b72e
Author: Antoine A <>
Date: Mon, 16 Sep 2024 00:59:56 +0200
nexus: use iban payto type
Diffstat:
15 files changed, 64 insertions(+), 48 deletions(-)
diff --git a/common/src/main/kotlin/TalerCommon.kt b/common/src/main/kotlin/TalerCommon.kt
@@ -332,6 +332,12 @@ sealed class Payto {
}
}
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Payto) return false
+ return this.parsed == other.parsed
+ }
+
internal object Serializer : KSerializer<Payto> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Payto", PrimitiveKind.STRING)
diff --git a/common/src/main/kotlin/db/types.kt b/common/src/main/kotlin/db/types.kt
@@ -57,4 +57,8 @@ fun ResultSet.getTalerTimestamp(name: String): TalerProtocolTimestamp{
fun ResultSet.getBankPayto(payto: String, name: String, ctx: BankPaytoCtx): String {
return Payto.parse(getString(payto)).bank(getString(name), ctx)
+}
+
+fun ResultSet.getIbanPayto(payto: String): IbanPayto {
+ return Payto.parse(getString(payto)).expectIban()
}
\ No newline at end of file
diff --git a/common/src/main/kotlin/helpers.kt b/common/src/main/kotlin/helpers.kt
@@ -169,4 +169,10 @@ fun ApplicationCall.longPath(name: String): Long {
} catch (e: Exception) {
throw badRequest("Long uri component malformed: ${e.message}", TalerErrorCode.GENERIC_PARAMETER_MALFORMED) // TODO better error ?
}
+}
+
+/* ----- Payto ----- */
+
+fun ibanPayto(iban: String, name: String? = null): IbanPayto {
+ return Payto.parse(IbanPayto.build(iban, null, name)).expectIban()
}
\ No newline at end of file
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/api/WireGatewayApi.kt
@@ -109,12 +109,12 @@ fun Routing.wireGatewayApi(db: Database, cfg: NexusConfig) = authApi(cfg.wireGat
metadata: TalerIncomingMetadata
) {
cfg.checkCurrency(amount)
- debitAccount.expectRequestIban()
+ val debitAccount = debitAccount.expectRequestIban()
val timestamp = Instant.now()
val bankId = randEbicsId()
val res = db.payment.registerTalerableIncoming(IncomingPayment(
amount = amount,
- debitPaytoUri = debitAccount.toString(),
+ debtorPayto = debitAccount,
subject = subject,
executionTime = timestamp,
bankId = bankId
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/EbicsSubmit.kt
@@ -37,8 +37,7 @@ fun batchToPain001Msg(account: IbanAccountMetadata, batch: PaymentBatch): Pain00
debtor = account,
sum = batch.sum,
txs = batch.payments.map { payment ->
- val payto = Payto.parse(payment.creditPaytoUri).expectIban()
- // TODO handle payto error
+ val payto = payment.creditor
Pain001Tx(
creditor = IbanAccountMetadata(
iban = payto.iban.value,
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/InitiatePayment.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/InitiatePayment.kt
@@ -66,7 +66,7 @@ class InitiatePayment: CliktCommand("Initiate an outgoing payment") {
id = -1,
amount = amount,
subject = subject,
- creditPaytoUri = payto.toString(),
+ creditor = payto,
initiationTime = Instant.now(),
endToEndId = endToEndId ?: randEbicsId()
)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/cli/Testing.kt
@@ -89,7 +89,7 @@ class FakeIncoming: CliktCommand("Genere a fake incoming payment") {
ingestIncomingPayment(db,
IncomingPayment(
amount = amount,
- debitPaytoUri = payto.toString(),
+ debtorPayto = payto,
subject = subject,
executionTime = Instant.now(),
bankId = randEbicsId()
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/Database.kt
@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import org.slf4j.LoggerFactory
import tech.libeufin.common.TalerAmount
+import tech.libeufin.common.IbanPayto
import tech.libeufin.common.db.DatabaseConfig
import tech.libeufin.common.db.DbPool
import tech.libeufin.common.db.watchNotifications
@@ -41,7 +42,7 @@ data class InitiatedPayment(
val id: Long,
val amount: TalerAmount,
val subject: String,
- val creditPaytoUri: String,
+ val creditor: IbanPayto,
val initiationTime: Instant,
val endToEndId: String
)
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/InitiatedDAO.kt
@@ -51,7 +51,7 @@ class InitiatedDAO(private val db: Database) {
setLong(1, paymentData.amount.value)
setInt(2, paymentData.amount.frac)
setString(3, paymentData.subject)
- setString(4, paymentData.creditPaytoUri)
+ setString(4, paymentData.creditor.toString())
setLong(5, paymentData.initiationTime.micros())
setString(6, paymentData.endToEndId)
oneUniqueViolation(PaymentInitiationResult.RequestUidReuse) {
@@ -375,7 +375,7 @@ class InitiatedDAO(private val db: Database) {
val payment = InitiatedPayment(
id = it.getLong("initiated_outgoing_transaction_id"),
amount = it.getAmount("amount", currency),
- creditPaytoUri = it.getString("credit_payto"),
+ creditor = it.getIbanPayto("credit_payto"),
subject = it.getString("subject"),
initiationTime = it.getLong("initiation_time").asInstant(),
endToEndId = it.getString("end_to_end_id")
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/db/PaymentDAO.kt
@@ -52,7 +52,7 @@ class PaymentDAO(private val db: Database) {
setInt(2, paymentData.amount.frac)
setString(3, paymentData.subject)
setLong(4, executionTime)
- setString(5, paymentData.creditPaytoUri)
+ setString(5, paymentData.creditorPayto.toString())
setString(6, paymentData.endToEndId)
setBytes(7, wtid?.raw)
setString(8, baseUrl?.url)
@@ -103,7 +103,7 @@ class PaymentDAO(private val db: Database) {
setInt(2, paymentData.amount.frac)
setString(3, paymentData.subject)
setLong(4, paymentData.executionTime.micros())
- setString(5, paymentData.debitPaytoUri)
+ setString(5, paymentData.debtorPayto.toString())
setString(6, paymentData.bankId)
setLong(7, bounceAmount.value)
setInt(8, bounceAmount.frac)
@@ -138,7 +138,7 @@ class PaymentDAO(private val db: Database) {
setInt(2, paymentData.amount.frac)
setString(3, paymentData.subject)
setLong(4, executionTime)
- setString(5, paymentData.debitPaytoUri)
+ setString(5, paymentData.debtorPayto.toString())
setString(6, paymentData.bankId)
setString(7, metadata.type.name)
when (metadata.type) {
@@ -177,7 +177,7 @@ class PaymentDAO(private val db: Database) {
setInt(2, paymentData.amount.frac)
setString(3, paymentData.subject)
setLong(4, executionTime)
- setString(5, paymentData.debitPaytoUri)
+ setString(5, paymentData.debtorPayto.toString())
setString(6, paymentData.bankId)
one {
IncomingRegistrationResult.Success(
diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/camt.kt b/nexus/src/main/kotlin/tech/libeufin/nexus/iso20022/camt.kt
@@ -36,10 +36,10 @@ data class IncomingPayment(
val amount: TalerAmount,
val subject: String,
override val executionTime: Instant,
- val debitPaytoUri: String
+ val debtorPayto: IbanPayto
): TxNotification {
override fun toString(): String {
- return "IN ${executionTime.fmtDate()} $amount $bankId debitor=$debitPaytoUri subject=\"$subject\""
+ return "IN ${executionTime.fmtDate()} $amount $bankId debitor=$debtorPayto subject=\"$subject\""
}
}
@@ -52,11 +52,11 @@ data class OutgoingPayment(
val amount: TalerAmount,
val subject: String? = null, // Some implementation does not provide this for recovery
override val executionTime: Instant,
- val creditPaytoUri: String? = null // Some implementation does not provide this for recovery
+ val creditorPayto: IbanPayto? = null // Some implementation does not provide this for recovery
): TxNotification {
override fun toString(): String {
val msgIdFmt = if (msgId == null) "" else "$msgId."
- return "OUT ${executionTime.fmtDate()} $amount $msgIdFmt$endToEndId creditor=$creditPaytoUri subject=\"$subject\""
+ return "OUT ${executionTime.fmtDate()} $amount $msgIdFmt$endToEndId creditor=$creditorPayto subject=\"$subject\""
}
}
@@ -104,13 +104,13 @@ private data class OutgoingId(
}
/** Parse a payto */
-private fun XmlDestructor.payto(prefix: String): String? {
+private fun XmlDestructor.payto(prefix: String): IbanPayto? {
return opt("RltdPties") {
val iban = opt("${prefix}Acct")?.one("Id")?.one("IBAN")?.text()
if (iban != null) {
val name = opt(prefix) { opt("Nm")?.text() ?: opt("Pty")?.one("Nm")?.text() }
- // Parse bic ?
- IbanPayto.build(iban, null, name)
+ // TODO more performant option
+ ibanPayto(iban, name)
} else {
null
}
@@ -467,7 +467,7 @@ private sealed interface TxInfo {
val bankId: String?,
val amount: TalerAmount,
val subject: String?,
- val debtorPayto: String?
+ val debtorPayto: IbanPayto?
): TxInfo
data class Debit(
override val ref: String?,
@@ -477,7 +477,7 @@ private sealed interface TxInfo {
val id: OutgoingId,
val amount: TalerAmount,
val subject: String?,
- val creditorPayto: String?
+ val creditorPayto: IbanPayto?
): TxInfo
}
@@ -503,7 +503,7 @@ private fun parseTxLogic(info: TxInfo): TxNotification {
IncomingPayment(
amount = info.amount,
bankId = info.bankId,
- debitPaytoUri = info.debtorPayto,
+ debtorPayto = info.debtorPayto,
executionTime = info.bookDate,
subject = info.subject
)
@@ -517,7 +517,7 @@ private fun parseTxLogic(info: TxInfo): TxNotification {
endToEndId = info.id.endToEndId,
msgId = info.id.msgId,
executionTime = info.bookDate,
- creditPaytoUri = info.creditorPayto,
+ creditorPayto = info.creditorPayto,
subject = info.subject
)
} else {
diff --git a/nexus/src/test/kotlin/IngestionTest.kt b/nexus/src/test/kotlin/IngestionTest.kt
@@ -119,7 +119,7 @@ class IngestionTest {
amount = it.getAmount("amount", this@check.bankCurrency),
subject = it.getString("subject"),
executionTime = it.getLong("execution_time").asInstant(),
- debitPaytoUri = it.getString("debit_payto"),
+ debtorPayto = it.getIbanPayto("debit_payto"),
)
}
}
@@ -144,7 +144,7 @@ class IngestionTest {
amount = it.getAmount("amount", this@check.bankCurrency),
subject = it.getString("subject"),
executionTime = it.getLong("execution_time").asInstant(),
- creditPaytoUri = it.getString("credit_payto"),
+ creditorPayto = it.getIbanPayto("credit_payto"),
)
}
}
@@ -323,7 +323,7 @@ class IngestionTest {
amount = TalerAmount("EUR:3"),
subject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-",
executionTime = dateToInstant("2024-04-12"),
- debitPaytoUri = "payto://iban/DE84500105177118117964?receiver-name=John%20Smith"
+ debtorPayto = ibanPayto("DE84500105177118117964", "John Smith")
),
),
outgoing = listOf(
@@ -332,28 +332,28 @@ class IngestionTest {
amount = TalerAmount("EUR:2"),
subject = "TestABC123",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
OutgoingPayment(
endToEndId = "FD622SMXKT5QWSAHDY0H8NYG3G",
amount = TalerAmount("EUR:1.1"),
subject = "single 2024-09-02T14:29:52.875253314Z",
executionTime = dateToInstant("2024-09-02"),
- creditPaytoUri = "payto://iban/DE89500105173198527518?receiver-name=Grothoff%20Hans"
+ creditorPayto = ibanPayto("DE89500105173198527518", "Grothoff Hans")
),
OutgoingPayment(
endToEndId = "YF5QBARGQ0MNY0VK59S477VDG4",
amount = TalerAmount("EUR:1.1"),
subject = "Simple tx",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
OutgoingPayment(
endToEndId = "27SK3166EG36SJ7VP7VFYP0MW8",
amount = TalerAmount("EUR:44"),
subject = "init payment",
executionTime = dateToInstant("2024-09-04"),
- creditPaytoUri = "payto://iban/CH4189144589712575493?receiver-name=Test"
+ creditorPayto = ibanPayto("CH4189144589712575493", "Test")
),
)
)
diff --git a/nexus/src/test/kotlin/Iso20022Test.kt b/nexus/src/test/kotlin/Iso20022Test.kt
@@ -148,21 +148,21 @@ class Iso20022Test {
amount = TalerAmount("CHF:3.00"),
subject = null,
executionTime = dateToInstant("2024-01-15"),
- creditPaytoUri = null
+ creditorPayto = null
),
IncomingPayment(
bankId = "62e2b511-7313-4ccd-8d40-c9d8e612cd71",
amount = TalerAmount("CHF:10"),
subject = "G1XTY6HGWGMVRM7E6XQ4JHJK561ETFDFTJZ7JVGV543XZCB27YBG",
executionTime = dateToInstant("2023-12-19"),
- debitPaytoUri = "payto://iban/CH7389144832588726658?receiver-name=Mr%20Test"
+ debtorPayto = ibanPayto("CH7389144832588726658", "Mr Test")
),
IncomingPayment(
bankId = "62e2b511-7313-4ccd-8d40-c9d8e612cd71",
amount = TalerAmount("CHF:2.53"),
subject = "G1XTY6HGWGMVRM7E6XQ4JHJK561ETFDFTJZ7JVGV543XZCB27YB",
executionTime = dateToInstant("2023-12-19"),
- debitPaytoUri = "payto://iban/CH7389144832588726658?receiver-name=Mr%20Test"
+ debtorPayto = ibanPayto("CH7389144832588726658", "Mr Test")
),
OutgoingBatch(
msgId = "ZS1PGNTSV0ZNDFAJBBWWB8015G",
@@ -204,7 +204,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:2"),
subject = "TestABC123",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
OutgoingReversal(
endToEndId = "8XK8Z7RAX224FGWK832FD40GYC",
@@ -216,7 +216,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:3"),
subject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-",
executionTime = dateToInstant("2024-04-12"),
- debitPaytoUri = "payto://iban/DE84500105177118117964?receiver-name=John%20Smith"
+ debtorPayto = ibanPayto("DE84500105177118117964", "John Smith")
),
OutgoingReversal(
endToEndId = "COMPAT_FAILURE",
@@ -229,7 +229,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:1.1"),
subject = "single 2024-09-02T14:29:52.875253314Z",
executionTime = dateToInstant("2024-09-02"),
- creditPaytoUri = "payto://iban/DE89500105173198527518?receiver-name=Grothoff%20Hans"
+ creditorPayto = ibanPayto("DE89500105173198527518", "Grothoff Hans")
),
OutgoingPayment(
endToEndId = "YF5QBARGQ0MNY0VK59S477VDG4",
@@ -237,7 +237,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:1.1"),
subject = "Simple tx",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
)
)
@@ -254,7 +254,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:2"),
subject = "TestABC123",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
OutgoingReversal(
endToEndId = "KGTDBASWTJ6JM89WXD3Q5KFQC4",
@@ -270,7 +270,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:3"),
subject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-",
executionTime = dateToInstant("2024-04-12"),
- debitPaytoUri = "payto://iban/DE84500105177118117964?receiver-name=John%20Smith"
+ debtorPayto = ibanPayto("DE84500105177118117964", "John Smith")
),
OutgoingReversal(
endToEndId = "COMPAT_FAILURE",
@@ -283,7 +283,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:1.1"),
subject = "single 2024-09-02T14:29:52.875253314Z",
executionTime = dateToInstant("2024-09-02"),
- creditPaytoUri = "payto://iban/DE89500105173198527518?receiver-name=Grothoff%20Hans"
+ creditorPayto = ibanPayto("DE89500105173198527518", "Grothoff Hans")
),
OutgoingPayment(
endToEndId = "YF5QBARGQ0MNY0VK59S477VDG4",
@@ -291,7 +291,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:1.1"),
subject = "Simple tx",
executionTime = dateToInstant("2024-04-18"),
- creditPaytoUri = "payto://iban/DE20500105172419259181?receiver-name=John%20Smith"
+ creditorPayto = ibanPayto("DE20500105172419259181", "John Smith")
),
)
)
@@ -307,7 +307,7 @@ class Iso20022Test {
amount = TalerAmount("EUR:2.5"),
subject = "Test ICT",
executionTime = dateToInstant("2024-05-05"),
- debitPaytoUri = "payto://iban/DE84500105177118117964?receiver-name=Mr%20Test"
+ debtorPayto = ibanPayto("DE84500105177118117964", "Mr Test")
)
)
)
diff --git a/nexus/src/test/kotlin/helpers.kt b/nexus/src/test/kotlin/helpers.kt
@@ -86,11 +86,11 @@ fun genInitPay(
endToEndId: String,
subject: String = "init payment",
amount: String = "KUDOS:44",
- creditPaytoUri: String = "payto://iban/CH4189144589712575493?receiver-name=Test"
+ creditorPayto: IbanPayto = ibanPayto("CH4189144589712575493", "Test")
) = InitiatedPayment(
id = -1,
amount = TalerAmount(amount),
- creditPaytoUri = creditPaytoUri,
+ creditor = creditorPayto,
subject = subject,
initiationTime = Instant.now(),
endToEndId = endToEndId
@@ -101,7 +101,7 @@ fun genInPay(subject: String, amount: String = "KUDOS:44"): IncomingPayment {
val bankId = randEbicsId()
return IncomingPayment(
amount = TalerAmount(amount),
- debitPaytoUri = "payto://iban/not-used",
+ debtorPayto = ibanPayto("DE84500105177118117964"),
subject = subject,
executionTime = Instant.now(),
bankId = bankId
@@ -113,7 +113,7 @@ fun genOutPay(subject: String, endToEndId: String? = null): OutgoingPayment {
val id = endToEndId ?: randEbicsId()
return OutgoingPayment(
amount = TalerAmount(44, 0, "KUDOS"),
- creditPaytoUri = "payto://iban/CH4189144589712575493?receiver-name=Test",
+ creditorPayto = ibanPayto("CH4189144589712575493", "Test"),
subject = subject,
executionTime = Instant.now(),
endToEndId = id
diff --git a/testbench/src/test/kotlin/IntegrationTest.kt b/testbench/src/test/kotlin/IntegrationTest.kt
@@ -162,7 +162,7 @@ class IntegrationTest {
val reservePub = EddsaPublicKey.rand()
val reservePayment = IncomingPayment(
amount = TalerAmount("EUR:10"),
- debitPaytoUri = userPayTo.toString(),
+ debtorPayto = userPayTo,
subject = "Error test $reservePub",
executionTime = Instant.now(),
bankId = "reserve_error"