gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[libeufin] branch master updated: extend Payto parser


From: gnunet
Subject: [libeufin] branch master updated: extend Payto parser
Date: Thu, 21 Oct 2021 16:39:32 +0200

This is an automated email from the git hooks/post-receive script.

ms pushed a commit to branch master
in repository libeufin.

The following commit(s) were added to refs/heads/master by this push:
     new b041de6  extend Payto parser
b041de6 is described below

commit b041de6e185e334e37572b33e31966028ae50bb6
Author: ms <ms@taler.net>
AuthorDate: Thu Oct 21 16:39:21 2021 +0200

    extend Payto parser
---
 nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt |  1 +
 util/src/main/kotlin/Payto.kt                      | 62 +++++++++++++++-------
 util/src/test/kotlin/PaytoTest.kt                  | 27 +++++++---
 3 files changed, 65 insertions(+), 25 deletions(-)

diff --git a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt 
b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
index 31fff08..6cd34a0 100644
--- a/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
+++ b/nexus/src/main/kotlin/tech/libeufin/nexus/Taler.kt
@@ -181,6 +181,7 @@ private suspend fun talerTransfer(call: ApplicationCall) {
     val transferRequest = call.receive<TalerTransferRequest>()
     val amountObj = parseAmount(transferRequest.amount)
     // FIXME: Right now we only parse the credit_account, should we also 
validate that it matches our account info?
+    // FIXME, another parse happens below; is this really useful here?
     parsePayto(transferRequest.credit_account)
     val facadeId = expectNonNull(call.parameters["fcid"])
     val opaqueRowId = transaction {
diff --git a/util/src/main/kotlin/Payto.kt b/util/src/main/kotlin/Payto.kt
index 1c10263..3fcecff 100644
--- a/util/src/main/kotlin/Payto.kt
+++ b/util/src/main/kotlin/Payto.kt
@@ -1,21 +1,40 @@
 package tech.libeufin.util
 
 import java.net.URI
+import java.net.URLDecoder
 
 /**
- * Helper data structures.
+ * Payto information.
  */
 data class Payto(
-    // Can represent a the sender or a receiver.
+    // represent query param "sender-name" or "receiver-name".
     val name: String?,
     val iban: String,
-    val bic: String?
+    val bic: String?,
+    // Typically, a wire transfer's subject.
+    val message: String?,
+    val amount: String?
 )
 class InvalidPaytoError(msg: String) : Exception(msg)
 
+// Return the value of query string parameter 'name', or null if not found.
+// 'params' is the a 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 }
+    }
+}
+
 fun parsePayto(paytoLine: String): Payto {
+    /**
+     * 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 (!paytoLine.startsWith("payto://"))
         throw InvalidPaytoError("Invalid payto URI: $paytoLine")
+
     val javaParsedUri = try {
         URI(paytoLine)
     } catch (e: java.lang.Exception) {
@@ -25,22 +44,9 @@ fun parsePayto(paytoLine: String): Payto {
         throw InvalidPaytoError("'${paytoLine}' is not payto")
     }
     val wireMethod = javaParsedUri.host
-    if (wireMethod != "sepa") {
-        throw InvalidPaytoError("Only SEPA is supported, not '$wireMethod'")
+    if (wireMethod != "iban") {
+        throw InvalidPaytoError("Only 'iban' is supported, not '$wireMethod'")
     }
-    val accountOwner = if (javaParsedUri.query != null) {
-        val queryStringAsList = javaParsedUri.query.split("&")
-        // admit only ONE parameter: receiver-name.
-        if (queryStringAsList.size != 1) {
-            throw InvalidPaytoError("'${paytoLine}' has unsupported query 
string")
-        }
-        val splitParameter = queryStringAsList.first().split("=")
-        if (splitParameter.first() != "receiver-name" && 
splitParameter.first() != "sender-name") {
-            throw InvalidPaytoError("'${paytoLine}' has unsupported query 
string")
-        }
-        splitParameter.last()
-    } else null
-
     val splitPath = javaParsedUri.path.split("/").filter { it.isNotEmpty() }
     if (splitPath.size > 2) {
         throw InvalidPaytoError("too many path segments in iban payto URI")
@@ -48,5 +54,23 @@ fun parsePayto(paytoLine: String): Payto {
     val (iban, bic) = if (splitPath.size == 1) {
         Pair(splitPath[0], null)
     } else Pair(splitPath[1], splitPath[0])
-    return Payto(iban = iban, bic = bic, name = accountOwner)
+
+    val params: List<Pair<String, String>>? = if (javaParsedUri.query != null) 
{
+        val queryString: List<String> = javaParsedUri.query.split("&")
+        queryString.map {
+            val split = it.split("="); Pair(split[0], split[1])
+        }
+    } else null
+
+    val receiverName = getQueryParamOrNull("receiver-name", params)
+    val senderName = getQueryParamOrNull("sender-name", params)
+    if (receiverName != null  && senderName != null) throw 
InvalidPaytoError("URI had both sender and receiver")
+
+    return Payto(
+        iban = iban,
+        bic = bic,
+        amount = getQueryParamOrNull("amount", params),
+        message = getQueryParamOrNull("message", params),
+        name = listOf(receiverName, senderName).firstNotNullOfOrNull { it }
+    )
 }
\ No newline at end of file
diff --git a/util/src/test/kotlin/PaytoTest.kt 
b/util/src/test/kotlin/PaytoTest.kt
index 635966d..ebc031b 100644
--- a/util/src/test/kotlin/PaytoTest.kt
+++ b/util/src/test/kotlin/PaytoTest.kt
@@ -21,19 +21,21 @@ class PaytoTest {
         }
         try {
             parsePayto(
-                
"payto://iban/BIC123/IBAN123?receiver-name=The%20Name&address=house"
+                
"payto:iban/BIC123/IBAN123?receiver-name=The%20Name&address=house"
             )
         } catch (e: InvalidPaytoError) {
             println(e)
-            println("more than one parameter isn't allowed")
+            println("'://' missing, invalid Payto")
         }
         try {
-            parsePayto(
-                
"payto:iban/BIC123/IBAN123?receiver-name=The%20Name&address=house"
-            )
+            
parsePayto("payto://iban/BIC123/IBAN123?sender-name=Foo&receiver-name=Foo")
+        } catch (e: InvalidPaytoError) {
+            println(e)
+        }
+        try {
+            
parsePayto("payto://wrong/BIC123/IBAN123?sender-name=Foo&receiver-name=Foo")
         } catch (e: InvalidPaytoError) {
             println(e)
-            println("'://' missing, invalid Payto")
         }
     }
 
@@ -43,5 +45,18 @@ class PaytoTest {
         assert(withBic.iban == "IBAN123")
         assert(withBic.bic == "BIC123")
         assert(withBic.name == "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.name == "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.name == null)
+        assert(withoutOptionals.amount == null)
     }
 }
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]