From 574782a71b9e8871630f7b8b4b8b126230981a1f Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Thu, 5 Sep 2024 14:42:26 -0700 Subject: [PATCH 1/5] Adding invoice UUID to PayReq and PayReqResponse, for uma invoice --- .../kotlin/me/uma/UmaProtocolHelper.kt | 7 +++++-- .../kotlin/me/uma/protocol/PayRequest.kt | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt b/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt index 8bc1ece..e62da81 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt @@ -353,6 +353,7 @@ class UmaProtocolHelper @JvmOverloads constructor( travelRuleFormat: TravelRuleFormat? = null, requestedPayeeData: CounterPartyDataOptions? = null, comment: String? = null, + invoiceUUID: String? = null, receiverUmaVersion: String = UMA_VERSION_STRING, ): PayRequest { val compliancePayerData = getSignedCompliancePayerData( @@ -386,6 +387,7 @@ class UmaProtocolHelper @JvmOverloads constructor( amount = amount, requestedPayeeData = requestedPayeeData, comment = comment, + invoiceUUID = invoiceUUID ) } } @@ -668,8 +670,9 @@ class UmaProtocolHelper @JvmOverloads constructor( successAction: Map? = null, senderUmaVersion: String = UMA_VERSION_STRING, ): PayReqResponse { - val encodedPayerData = query.payerData?.let { serialFormat.encodeToString(query.payerData) } ?: "" - val metadataWithPayerData = "$metadata$encodedPayerData" + val encodedPayerData = query.payerData?.let(serialFormat::encodeToString) ?: "" + val encodedInvoiceUUID = query.invoiceUUID()?.let(serialFormat::encodeToString) ?: "" + val metadataWithPayerData = "$metadata$encodedPayerData$encodedInvoiceUUID" if (query.sendingCurrencyCode() != null && query.sendingCurrencyCode() != receivingCurrencyCode) { throw IllegalArgumentException( "Currency code in the pay request must match the receiving currency if not null.", diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt index d1e5c1f..f000042 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt @@ -49,6 +49,8 @@ sealed interface PayRequest { fun comment(): String? + fun invoiceUUID(): String? + fun toQueryParamMap(): Map companion object { @@ -72,6 +74,7 @@ sealed interface PayRequest { ) } val comment = queryMap["comment"]?.firstOrNull() + val invoiceUUID = queryMap["invoiceUUID"]?.firstOrNull() return PayRequestV1( sendingCurrencyCode, receivingCurrencyCode, @@ -100,6 +103,11 @@ internal data class PayRequestV1( * the comment must be less than or equal to the value of `commentAllowed`. */ val comment: String? = null, + /** + * InvoiceUUID is the invoice UUID that the sender is paying. + * This only exists in the v1 pay request since the v0 SDK won't support invoices. + */ + val invoiceUUID: String? = null, ) : PayRequest { override fun receivingCurrencyCode() = receivingCurrencyCode @@ -122,6 +130,8 @@ internal data class PayRequestV1( override fun comment(): String? = comment + override fun invoiceUUID(): String? = invoiceUUID + override fun toQueryParamMap(): Map { val amountStr = if (sendingCurrencyCode != null) { @@ -139,6 +149,7 @@ internal data class PayRequestV1( map["payeeData"] = serialFormat.encodeToString(it) } comment?.let { map["comment"] = it } + invoiceUUID?.let { map["invoiceUUID"] = it} return map } } @@ -163,6 +174,8 @@ internal data class PayRequestV0( override fun comment(): String? = null + override fun invoiceUUID(): String? = null + override fun signablePayload() = payerData.compliance()?.let { "${payerData.identifier()}|${it.signatureNonce}|${it.signatureTimestamp}".encodeToByteArray() } ?: payerData.identifier()?.encodeToByteArray() @@ -186,6 +199,7 @@ internal object PayRequestV1Serializer : KSerializer { element("payerData") element("payeeData", isOptional = true) element("comment", isOptional = true) + element("invoiceUUID", isOptional = true) } override fun serialize(encoder: Encoder, value: PayRequestV1) { @@ -218,6 +232,7 @@ internal object PayRequestV1Serializer : KSerializer { var payerData: PayerData? = null var requestedPayeeData: CounterPartyDataOptions? = null var comment: String? = null + var invoiceUUID: String? = null return decoder.decodeStructure(descriptor) { while (true) { @@ -246,6 +261,7 @@ internal object PayRequestV1Serializer : KSerializer { ) 4 -> comment = decodeNullableSerializableElement(descriptor, index, String.serializer().nullable) + 5 -> invoiceUUID = decodeNullableSerializableElement(descriptor, index, String.serializer().nullable) } } @@ -267,6 +283,7 @@ internal object PayRequestV1Serializer : KSerializer { payerData, requestedPayeeData, comment, + invoiceUUID ) } } From c8e09ae307cbd939727471ec63b163c9df8f7c76 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Thu, 5 Sep 2024 14:56:22 -0700 Subject: [PATCH 2/5] lint --- uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt index f000042..e288f94 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt @@ -149,7 +149,7 @@ internal data class PayRequestV1( map["payeeData"] = serialFormat.encodeToString(it) } comment?.let { map["comment"] = it } - invoiceUUID?.let { map["invoiceUUID"] = it} + invoiceUUID?.let { map["invoiceUUID"] = it } return map } } @@ -261,7 +261,9 @@ internal object PayRequestV1Serializer : KSerializer { ) 4 -> comment = decodeNullableSerializableElement(descriptor, index, String.serializer().nullable) - 5 -> invoiceUUID = decodeNullableSerializableElement(descriptor, index, String.serializer().nullable) + 5 -> + invoiceUUID = + decodeNullableSerializableElement(descriptor, index, String.serializer().nullable) } } From 4cac5d50a355ccd307e5242c8b6a67ef679e1349 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Thu, 5 Sep 2024 15:06:47 -0700 Subject: [PATCH 3/5] fix tests --- javatest/src/test/java/me/uma/javatest/UmaTest.java | 2 ++ uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt | 1 + 2 files changed, 3 insertions(+) diff --git a/javatest/src/test/java/me/uma/javatest/UmaTest.java b/javatest/src/test/java/me/uma/javatest/UmaTest.java index 4e1d751..c56495d 100644 --- a/javatest/src/test/java/me/uma/javatest/UmaTest.java +++ b/javatest/src/test/java/me/uma/javatest/UmaTest.java @@ -216,6 +216,7 @@ public void testGetPayRequest_umaV1() throws Exception { null, null, "comment", + "sample-uuid-string", "1.0" ); assertNotNull(request); @@ -248,6 +249,7 @@ public void testGetPayRequest_umaV0() throws Exception { null, null, "comment", + "sample-uuid-string", "0.3" ); assertNotNull(request); diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt index e288f94..f1a0dae 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt @@ -222,6 +222,7 @@ internal object PayRequestV1Serializer : KSerializer { value.requestedPayeeData, ) value.comment?.let { encodeStringElement(descriptor, 4, it) } + value.invoiceUUID?.let { encodeStringElement(descriptor, 5, it) } } } From 5326b42b3b65dfef2294c18edb7ed7e15d599aae Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 6 Sep 2024 12:09:05 -0700 Subject: [PATCH 4/5] encode invoice uuid as json list --- .../src/test/java/me/uma/javatest/UmaTest.java | 8 ++++---- .../kotlin/me/uma/UmaProtocolHelper.kt | 17 +++++++++++++++-- .../src/commonTest/kotlin/me/uma/UmaTests.kt | 10 ++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/javatest/src/test/java/me/uma/javatest/UmaTest.java b/javatest/src/test/java/me/uma/javatest/UmaTest.java index c56495d..6c50e56 100644 --- a/javatest/src/test/java/me/uma/javatest/UmaTest.java +++ b/javatest/src/test/java/me/uma/javatest/UmaTest.java @@ -2,7 +2,6 @@ import kotlin.coroutines.Continuation; import kotlinx.serialization.json.Json; -import kotlinx.serialization.json.JsonElement; import kotlinx.serialization.json.JsonElementKt; import kotlinx.serialization.json.JsonObject; import me.uma.*; @@ -24,6 +23,7 @@ public class UmaTest { UmaProtocolHelper umaProtocolHelper = new UmaProtocolHelper(new InMemoryPublicKeyCache(), new TestUmaRequester()); private static final String PUBKEY_HEX = "04419c5467ea563f0010fd614f85e885ac99c21b8e8d416241175fdd5efd2244fe907e2e6fa3dd6631b1b17cd28798da8d882a34c4776d44cc4090781c7aadea1b"; private static final String PRIVKEY_HEX = "77e891f0ecd265a3cda435eaa73792233ebd413aeb0dbb66f2940babfc9a2667"; + private static final String encodedPayReqMetadata = "[[\"text/plain\",\"invoiceUUID\"],[\"text/plain\",\"otherInformations\"]]"; private static final String CERT_CHAIN = "-----BEGIN CERTIFICATE-----\n" + "MIIB1zCCAXygAwIBAgIUGN3ihBj1RnKoeTM/auDFnNoThR4wCgYIKoZIzj0EAwIw\n" + @@ -278,7 +278,7 @@ public void testGetPayReqResponseSync_umaV1() throws Exception { PayReqResponse response = umaProtocolHelper.getPayReqResponseSync( request, new TestSyncUmaInvoiceCreator(), - "metadata", + encodedPayReqMetadata, "USD", 2, 12345.0, @@ -322,7 +322,7 @@ public void testGetPayReqResponseSync_umaV0() throws Exception { PayReqResponse response = umaProtocolHelper.getPayReqResponseSync( request, new TestSyncUmaInvoiceCreator(), - "metadata", + encodedPayReqMetadata, "USD", 2, 12345.0, @@ -363,7 +363,7 @@ public void testGetPayReqResponseFuture() throws Exception { PayReqResponse response = umaProtocolHelper.getPayReqResponseFuture( request, new TestUmaInvoiceCreator(), - "metadata", + encodedPayReqMetadata, "USD", 2, 12345.0, diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt b/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt index e62da81..d4258e7 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/UmaProtocolHelper.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.runBlocking import kotlinx.serialization.SerializationException import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.encodeToJsonElement @@ -671,8 +672,10 @@ class UmaProtocolHelper @JvmOverloads constructor( senderUmaVersion: String = UMA_VERSION_STRING, ): PayReqResponse { val encodedPayerData = query.payerData?.let(serialFormat::encodeToString) ?: "" - val encodedInvoiceUUID = query.invoiceUUID()?.let(serialFormat::encodeToString) ?: "" - val metadataWithPayerData = "$metadata$encodedPayerData$encodedInvoiceUUID" + val metadataWithInvoiceUUID = query.invoiceUUID()?.let { + addInvoiceUUIDtoMetadata(metadata, it) + } ?: metadata + val metadataWithPayerData = "$metadataWithInvoiceUUID$encodedPayerData" if (query.sendingCurrencyCode() != null && query.sendingCurrencyCode() != receivingCurrencyCode) { throw IllegalArgumentException( "Currency code in the pay request must match the receiving currency if not null.", @@ -762,6 +765,16 @@ class UmaProtocolHelper @JvmOverloads constructor( ) } + private fun addInvoiceUUIDtoMetadata(metadata: String, invoiceUUID: String): String { + return try { + val decodedMetadata = Json.decodeFromString>>(metadata).toMutableList() + decodedMetadata.add(listOf("text/plain", invoiceUUID)) + Json.encodeToString(decodedMetadata) + } catch (e: Exception) { + metadata + } + } + private fun getSignedCompliancePayeeData( receiverChannelUtxos: List, receiverNodePubKey: String?, diff --git a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt index 37d0484..14fee30 100644 --- a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt +++ b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt @@ -1,5 +1,6 @@ package me.uma +import io.ktor.util.Identity.encode import io.ktor.utils.io.core.toByteArray import me.uma.crypto.Secp256k1 import me.uma.crypto.hexToByteArray @@ -11,6 +12,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive @@ -25,6 +27,14 @@ class UmaTests { @Test fun `test create invoice currency`() = runTest { + val data = listOf( + listOf("text/plain", "invoiceUUID"), + listOf("text/plain", "otherInformations"), + ) + val encoded1 = Json.encodeToString(data) + + val original = Json.decodeFromString>>(encoded1) + println(original.size) val invoiceCurrency = InvoiceCurrency( "usd", From 9a1267dd28ec032735494fc45e1c187a13a90566 Mon Sep 17 00:00:00 2001 From: Matt Davis Date: Fri, 6 Sep 2024 14:50:54 -0700 Subject: [PATCH 5/5] fix lints --- uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt index 14fee30..45851ca 100644 --- a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt +++ b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt @@ -1,6 +1,5 @@ package me.uma -import io.ktor.util.Identity.encode import io.ktor.utils.io.core.toByteArray import me.uma.crypto.Secp256k1 import me.uma.crypto.hexToByteArray