gnunet-svn
[Top][All Lists]
Advanced

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

[taler-taler-android] branch master updated (d3955c2 -> 0e28f81)


From: gnunet
Subject: [taler-taler-android] branch master updated (d3955c2 -> 0e28f81)
Date: Mon, 24 Aug 2020 22:29:43 +0200

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

torsten-grote pushed a change to branch master
in repository taler-android.

    from d3955c2  Add README files to top-level and update the project ones
     new 35bc917  Upgrade to Kotlin 1.4
     new 39dcd04  Get rid of Jackson and only use multi-platform serialization
     new 0e28f81  [wallet] upgrade to latest wallet-core version

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .idea/compiler.xml                                 |   4 +
 build.gradle                                       |   4 +-
 cashier/build.gradle                               |   7 +-
 merchant-lib/build.gradle                          |   2 -
 .../main/java/net/taler/merchantlib/MerchantApi.kt |  12 +-
 .../src/main/java/net/taler/merchantlib/Orders.kt  |  34 +---
 .../main/java/net/taler/merchantlib/Response.kt    |   5 +-
 .../java/net/taler/merchantlib/MockHttpClient.kt   |   5 +-
 .../net/taler/merchantpos/config/ConfigManager.kt  |   2 +-
 multiplatform                                      |   2 +-
 taler-kotlin-android/build.gradle                  |   4 +-
 .../main/java/net/taler/common/ContractTerms.kt    |  18 +--
 .../java/net/taler/lib/android/Serialization.kt    |  11 +-
 .../main/java/net/taler/lib/common/AmountMixin.kt  |  51 ------
 .../java/net/taler/lib/common/TimestampMixin.kt    |  39 -----
 .../java/net/taler/common/ContractTermsTest.kt     |  24 +--
 wallet/build.gradle                                |  13 +-
 wallet/proguard-rules.pro                          |  33 ----
 .../main/java/net/taler/wallet/MainViewModel.kt    |  17 +-
 .../net/taler/wallet/backend/WalletBackendApi.kt   |  43 ++---
 .../net/taler/wallet/backend/WalletResponse.kt     |  30 +---
 .../net/taler/wallet/payment/PaymentManager.kt     |  25 +--
 .../net/taler/wallet/payment/PaymentResponses.kt   |  47 +++---
 .../taler/wallet/payment/PromptPaymentFragment.kt  |   4 +-
 .../wallet/transactions/TransactionAdapter.kt      |   2 +-
 .../transactions/TransactionDetailFragment.kt      |   2 +-
 .../wallet/transactions/TransactionManager.kt      |   9 +-
 .../net/taler/wallet/transactions/Transactions.kt  | 180 +++++++++++----------
 .../net/taler/wallet/backend/WalletResponseTest.kt |  27 +---
 29 files changed, 207 insertions(+), 449 deletions(-)
 copy wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt => 
taler-kotlin-android/src/main/java/net/taler/lib/android/Serialization.kt (81%)
 delete mode 100644 
taler-kotlin-android/src/main/java/net/taler/lib/common/AmountMixin.kt
 delete mode 100644 
taler-kotlin-android/src/main/java/net/taler/lib/common/TimestampMixin.kt

diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index 2b48706..9389bf3 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -12,6 +12,10 @@
       <entry name="!?*.clj" />
     </wildcardResourcePatterns>
     <bytecodeTargetLevel>
+      <module name="common_commonMain" target="1.6" />
+      <module name="common_commonTest" target="1.6" />
+      <module name="common_jvmMain" target="1.6" />
+      <module name="common_jvmTest" target="1.6" />
       <module name="taler-kotlin-common_jvmMain" target="1.6" />
       <module name="taler-kotlin-common_jvmTest" target="1.6" />
     </bytecodeTargetLevel>
diff --git a/build.gradle b/build.gradle
index 442d232..7c9e378 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
-    ext.kotlin_version = '1.3.72'
-    ext.ktor_version = "1.3.2"
+    ext.kotlin_version = '1.4.0'
+    ext.ktor_version = "1.4.0"
     ext.nav_version = "2.3.0"
     ext.lifecycle_version = "2.2.0"
     // check https://android-rebuilds.beuc.net/ for availability of free build 
tools
diff --git a/cashier/build.gradle b/cashier/build.gradle
index 341562d..916758b 100644
--- a/cashier/build.gradle
+++ b/cashier/build.gradle
@@ -59,13 +59,14 @@ android {
 
 dependencies {
     implementation project(":taler-kotlin-android")
-    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
-    implementation 'androidx.security:security-crypto:1.0.0-rc02'
-    implementation 'com.google.android.material:material:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
+    implementation 'androidx.security:security-crypto:1.0.0-rc03'
+    implementation 'com.google.android.material:material:1.2.0'
 
     implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
     implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
 
+    // https://github.com/square/okhttp/releases
     implementation "com.squareup.okhttp3:okhttp:3.12.12"
 
     testImplementation 'junit:junit:4.13'
diff --git a/merchant-lib/build.gradle b/merchant-lib/build.gradle
index 5082253..9b349ea 100644
--- a/merchant-lib/build.gradle
+++ b/merchant-lib/build.gradle
@@ -47,8 +47,6 @@ android {
 dependencies {
     api project(":taler-kotlin-android")
 
-    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
-
     api "io.ktor:ktor-client:$ktor_version"
     api "io.ktor:ktor-client-okhttp:$ktor_version"
     api "io.ktor:ktor-client-serialization-jvm:$ktor_version"
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
index a4ca397..a467c41 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/MerchantApi.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.JsonConfiguration
 import net.taler.merchantlib.Response.Companion.response
 
 class MerchantApi(
@@ -116,10 +115,9 @@ fun getDefaultHttpClient(): HttpClient = 
HttpClient(OkHttp) {
 }
 
 fun getSerializer() = KotlinxSerializer(
-    Json(
-        JsonConfiguration(
-            encodeDefaults = false,
-            ignoreUnknownKeys = true
-        )
-    )
+    Json {
+        encodeDefaults = false
+        ignoreUnknownKeys = true
+        classDiscriminator = "order_status"
+    }
 )
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
index 0a405ad..9242df3 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Orders.kt
@@ -16,15 +16,10 @@
 
 package net.taler.merchantlib
 
-import kotlinx.serialization.Decoder
-import kotlinx.serialization.Encoder
-import kotlinx.serialization.KSerializer
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.Serializer
-import kotlinx.serialization.json.JsonInput
-import kotlinx.serialization.json.JsonObject
 import net.taler.common.ContractTerms
+import net.taler.lib.android.CustomClassDiscriminator
 import net.taler.lib.common.Duration
 
 @Serializable
@@ -42,32 +37,12 @@ data class PostOrderResponse(
 )
 
 @Serializable
-sealed class CheckPaymentResponse {
+sealed class CheckPaymentResponse: CustomClassDiscriminator {
+    override val discriminator: String = "order_status"
     abstract val paid: Boolean
 
-    @Serializer(forClass = CheckPaymentResponse::class)
-    companion object : KSerializer<CheckPaymentResponse> {
-        override fun deserialize(decoder: Decoder): CheckPaymentResponse {
-            val input = decoder as JsonInput
-            val tree = input.decodeJson() as JsonObject
-            val orderStatus = tree.getPrimitive("order_status").content
-//            return if (orderStatus == "paid") 
decoder.json.fromJson(Paid.serializer(), tree)
-//            else decoder.json.fromJson(Unpaid.serializer(), tree)
-            // manual parsing due to 
https://github.com/Kotlin/kotlinx.serialization/issues/576
-            return if (orderStatus == "paid") Paid(
-                refunded = tree.getPrimitive("refunded").boolean
-            ) else Unpaid(
-                talerPayUri = tree.getPrimitive("taler_pay_uri").content
-            )
-        }
-
-        override fun serialize(encoder: Encoder, value: CheckPaymentResponse) 
= when (value) {
-            is Unpaid -> Unpaid.serializer().serialize(encoder, value)
-            is Paid -> Paid.serializer().serialize(encoder, value)
-        }
-    }
-
     @Serializable
+    @SerialName("unpaid")
     data class Unpaid(
         override val paid: Boolean = false,
         @SerialName("taler_pay_uri")
@@ -77,6 +52,7 @@ sealed class CheckPaymentResponse {
     ) : CheckPaymentResponse()
 
     @Serializable
+    @SerialName("paid")
     data class Paid(
         override val paid: Boolean = true,
         val refunded: Boolean
diff --git a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt 
b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
index fb48b46..1b63900 100644
--- a/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
+++ b/merchant-lib/src/main/java/net/taler/merchantlib/Response.kt
@@ -70,11 +70,12 @@ class Response<out T> private constructor(
     }
 
     private suspend fun getExceptionString(e: ResponseException): String {
+        val response = e.response ?: return e.toString()
         return try {
-            val error: Error = e.response.receive()
+            val error: Error = response.receive()
             "Error ${error.code}: ${error.hint}"
         } catch (ex: Exception) {
-            "Status code: ${e.response.status.value}"
+            "Status code: ${response.status.value}"
         }
     }
 
diff --git a/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt 
b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
index 993be15..c8e6f22 100644
--- a/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
+++ b/merchant-lib/src/test/java/net/taler/merchantlib/MockHttpClient.kt
@@ -32,6 +32,7 @@ import io.ktor.http.content.TextContent
 import io.ktor.http.fullPath
 import io.ktor.http.headersOf
 import io.ktor.http.hostWithPort
+import kotlinx.serialization.json.Json.Default.parseToJsonElement
 import org.junit.Assert.assertEquals
 
 object MockHttpClient {
@@ -75,8 +76,8 @@ object MockHttpClient {
     private val Url.fullUrl: String get() = 
"${protocol.name}://$hostWithPortIfRequired$fullPath"
 
     private fun assertJsonEquals(json1: String, json2: String) {
-        val parsed1 = kotlinx.serialization.json.Json.parseJson(json1)
-        val parsed2 = kotlinx.serialization.json.Json.parseJson(json2)
+        val parsed1 = parseToJsonElement(json1)
+        val parsed2 = parseToJsonElement(json2)
         assertEquals(parsed1, parsed2)
     }
 
diff --git 
a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt 
b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
index 23abe7d..d7c446f 100644
--- 
a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
+++ 
b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt
@@ -115,7 +115,7 @@ class ConfigManager(
                 Log.e(TAG, "Error retrieving merchant config", e)
                 val msg = if (e is ClientRequestException) {
                     context.getString(
-                        if (e.response.status == Unauthorized) 
R.string.config_auth_error
+                        if (e.response?.status == Unauthorized) 
R.string.config_auth_error
                         else R.string.config_error_network
                     )
                 } else {
diff --git a/multiplatform b/multiplatform
index dade047..6f698f4 160000
--- a/multiplatform
+++ b/multiplatform
@@ -1 +1 @@
-Subproject commit dade0470c7e378c72ac2f2fd2a623416dadbff10
+Subproject commit 6f698f4d0ef580a898d3b03e9e8954af4f194037
diff --git a/taler-kotlin-android/build.gradle 
b/taler-kotlin-android/build.gradle
index ca4df8a..6d992a0 100644
--- a/taler-kotlin-android/build.gradle
+++ b/taler-kotlin-android/build.gradle
@@ -52,7 +52,6 @@ android {
 dependencies {
     api project(":multiplatform:common")
 
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'androidx.core:core-ktx:1.3.1'
 
@@ -67,8 +66,7 @@ dependencies {
     implementation 'com.google.zxing:core:3.4.0'  // needs minSdkVersion 24+
 
     // JSON parsing and serialization
-    api "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
-    implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2"
+    api "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
 
     lintPublish 'com.github.thirdegg:lint-rules:0.0.4-alpha'
 
diff --git 
a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt 
b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
index 8bf77e8..2c50fa9 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/ContractTerms.kt
@@ -17,10 +17,6 @@
 package net.taler.common
 
 import androidx.annotation.RequiresApi
-import com.fasterxml.jackson.annotation.JsonIgnore
-import com.fasterxml.jackson.annotation.JsonInclude
-import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL
-import com.fasterxml.jackson.annotation.JsonProperty
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import net.taler.common.TalerUtils.getLocalizedString
@@ -31,36 +27,24 @@ import net.taler.lib.common.Timestamp
 data class ContractTerms(
     val summary: String,
     @SerialName("summary_i18n")
-    @get:JsonProperty("summary_i18n")
     val summaryI18n: Map<String, String>? = null,
     val amount: Amount,
     @SerialName("fulfillment_url")
-    @get:JsonProperty("fulfillment_url")
     val fulfillmentUrl: String,
     val products: List<ContractProduct>,
     @SerialName("wire_transfer_deadline")
-    @get:JsonProperty("wire_transfer_deadline")
     val wireTransferDeadline: Timestamp? = null,
     @SerialName("refund_deadline")
-    @get:JsonProperty("refund_deadline")
     val refundDeadline: Timestamp? = null
 )
 
-@JsonInclude(NON_NULL)
 abstract class Product {
-    @get:JsonProperty("product_id")
     abstract val productId: String?
     abstract val description: String
-
-    @get:JsonProperty("description_i18n")
     abstract val descriptionI18n: Map<String, String>?
     abstract val price: Amount
-
-    @get:JsonProperty("delivery_location")
     abstract val location: String?
     abstract val image: String?
-
-    @get:JsonIgnore
     val localizedDescription: String
         @RequiresApi(26)
         get() = getLocalizedString(descriptionI18n, description)
@@ -79,12 +63,12 @@ data class ContractProduct(
     override val image: String? = null,
     val quantity: Int
 ) : Product() {
-    @get:JsonIgnore
     val totalPrice: Amount by lazy {
         price * quantity
     }
 }
 
+@Serializable
 data class ContractMerchant(
     val name: String
 )
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt 
b/taler-kotlin-android/src/main/java/net/taler/lib/android/Serialization.kt
similarity index 81%
copy from wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
copy to 
taler-kotlin-android/src/main/java/net/taler/lib/android/Serialization.kt
index d1a111f..7eb4480 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceResponse.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/lib/android/Serialization.kt
@@ -14,11 +14,8 @@
  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-package net.taler.wallet.balances
+package net.taler.lib.android
 
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class BalanceResponse(
-    val balances: List<BalanceItem>
-)
+interface CustomClassDiscriminator {
+    val discriminator: String
+}
diff --git 
a/taler-kotlin-android/src/main/java/net/taler/lib/common/AmountMixin.kt 
b/taler-kotlin-android/src/main/java/net/taler/lib/common/AmountMixin.kt
deleted file mode 100644
index 59285b6..0000000
--- a/taler-kotlin-android/src/main/java/net/taler/lib/common/AmountMixin.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under 
the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.lib.common
-
-import com.fasterxml.jackson.core.JsonGenerator
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.JsonMappingException
-import com.fasterxml.jackson.databind.SerializerProvider
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize
-import com.fasterxml.jackson.databind.annotation.JsonSerialize
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import com.fasterxml.jackson.databind.ser.std.StdSerializer
-
-/**
- * Used to support Jackson serialization along with KotlinX.
- */
-@JsonSerialize(using = AmountSerializer::class)
-@JsonDeserialize(using = AmountDeserializer::class)
-abstract class AmountMixin
-
-class AmountSerializer : StdSerializer<Amount>(Amount::class.java) {
-    override fun serialize(value: Amount, gen: JsonGenerator, provider: 
SerializerProvider) {
-        gen.writeString(value.toJSONString())
-    }
-}
-
-class AmountDeserializer : StdDeserializer<Amount>(Amount::class.java) {
-    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): 
Amount {
-        val node = p.codec.readValue(p, String::class.java)
-        try {
-            return Amount.fromJSONString(node)
-        } catch (e: AmountParserException) {
-            throw JsonMappingException(p, "Error parsing Amount", e)
-        }
-    }
-}
diff --git 
a/taler-kotlin-android/src/main/java/net/taler/lib/common/TimestampMixin.kt 
b/taler-kotlin-android/src/main/java/net/taler/lib/common/TimestampMixin.kt
deleted file mode 100644
index 40c03f6..0000000
--- a/taler-kotlin-android/src/main/java/net/taler/lib/common/TimestampMixin.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under 
the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.lib.common
-
-import com.fasterxml.jackson.annotation.JsonProperty
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-
-/**
- * Used to support Jackson serialization along with KotlinX.
- */
-abstract class TimestampMixin(
-    @get:JsonDeserialize(using = NeverDeserializer::class)
-    @get:JsonProperty("t_ms")
-    val ms: Long
-)
-
-class NeverDeserializer : StdDeserializer<Long>(Long::class.java) {
-    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): 
Long {
-        return if (p.text == "never") -1
-        else p.longValue
-    }
-}
diff --git 
a/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt 
b/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
index 62e8922..3a2cdb4 100644
--- a/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
+++ b/taler-kotlin-android/src/test/java/net/taler/common/ContractTermsTest.kt
@@ -16,29 +16,21 @@
 
 package net.taler.common
 
-import com.fasterxml.jackson.databind.DeserializationFeature
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import com.fasterxml.jackson.module.kotlin.readValue
-import net.taler.lib.common.Amount
-import net.taler.lib.common.AmountMixin
+import kotlinx.serialization.decodeFromString
+import kotlinx.serialization.json.Json
 import net.taler.lib.common.Timestamp
-import net.taler.lib.common.Timestamp.Companion.NEVER
-import net.taler.lib.common.TimestampMixin
 import org.junit.Assert.assertEquals
 import org.junit.Test
 
 class ContractTermsTest {
 
-    private val mapper = ObjectMapper()
-        .registerModule(KotlinModule())
-        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
-        .addMixIn(Amount::class.java, AmountMixin::class.java)
-        .addMixIn(Timestamp::class.java, TimestampMixin::class.java)
+    private val json = Json {
+        ignoreUnknownKeys = true
+    }
 
     @Test
     fun test() {
-        val json = """
+        val jsonStr = """
             {
               "amount":"TESTKUDOS:0.5",
               "extra":{
@@ -73,9 +65,9 @@ class ContractTermsTest {
                 "nonce":"FK8ZKJRV6VX6YFAG4CDSC6W0DWD084Q09DP81ANF30GRFQYM2KPG"
               }
         """.trimIndent()
-        val contractTerms: ContractTerms = mapper.readValue(json)
+        val contractTerms: ContractTerms = json.decodeFromString(jsonStr)
         assertEquals("Essay: 1. The Free Software Definition", 
contractTerms.summary)
-        assertEquals(Timestamp(NEVER), contractTerms.refundDeadline)
+        assertEquals(Timestamp.never(), contractTerms.refundDeadline)
     }
 
 }
diff --git a/wallet/build.gradle b/wallet/build.gradle
index 87019f8..70b9648 100644
--- a/wallet/build.gradle
+++ b/wallet/build.gradle
@@ -20,11 +20,11 @@ plugins {
     id "com.android.application"
     id "kotlin-android"
     id "kotlin-android-extensions"
-    id 'kotlinx-serialization'
+    id "kotlinx-serialization"
     id "de.undercouch.download"
 }
 
-def walletCoreVersion = "v0.7.1-dev.21"
+def walletCoreVersion = "v0.7.1-dev.22"
 
 static def versionCodeEpoch() {
     return (new Date().getTime() / 1000).toInteger()
@@ -48,7 +48,7 @@ android {
         minSdkVersion 24
         targetSdkVersion 29
         versionCode 6
-        versionName "0.7.1.dev.21"
+        versionName "0.7.1.dev.22"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         buildConfigField "String", "WALLET_CORE_VERSION", 
"\"$walletCoreVersion\""
     }
@@ -102,9 +102,11 @@ dependencies {
     implementation project(":anastasis-ui")
     implementation 'net.taler:akono:0.1'
 
+    implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
+
     implementation 'androidx.preference:preference:1.1.1'
     implementation 'com.google.android.material:material:1.2.0'
-    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
 
     // Lists and Selection
     implementation "androidx.recyclerview:recyclerview:1.1.0"
@@ -129,9 +131,6 @@ dependencies {
     implementation "io.noties.markwon:ext-tables:$markwon_version"
     implementation "io.noties.markwon:recycler:$markwon_version"
 
-    // JSON parsing and serialization
-    implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.10.2'
-
     testImplementation 'junit:junit:4.13'
     testImplementation 'org.json:json:20200518'
     androidTestImplementation 'androidx.test:runner:1.2.0'
diff --git a/wallet/proguard-rules.pro b/wallet/proguard-rules.pro
index 147334d..27f3799 100644
--- a/wallet/proguard-rules.pro
+++ b/wallet/proguard-rules.pro
@@ -23,36 +23,3 @@
 -keep class akono.AkonoJni {*;}
 
 -keep class net.taler.wallet.** {*;}
-
-# Jackson
--keep @com.fasterxml.jackson.annotation.JsonIgnoreProperties class * { *; }
--keep @com.fasterxml.jackson.annotation.JsonCreator class * { *; }
--keep @com.fasterxml.jackson.annotation.JsonValue class * { *; }
--keep class com.fasterxml.** { *; }
--keep class org.codehaus.** { *; }
--keepnames class com.fasterxml.jackson.** { *; }
--keepclassmembers public final enum 
com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility { 
-    public static final 
com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility *;
-}
-
--keep class * extends com.fasterxml.** { *; }
--keep class * implements com.fasterxml.** { *; }
-
--keep class *  {
-    @com.fasterxml.** *;
-}
-
-# KotlinX serialization
--keep @kotlinx.serialization.Serializable class * { *; }
-
-# Kotlin reflection
--dontwarn kotlin.reflect.**
--keep class kotlin.** { *; }
--keep class org.jetbrains.annotations.** { *; }
-
-
-# General
--keepattributes 
SourceFile,LineNumberTable,*Annotation*,EnclosingMethod,Signature,Exceptions,InnerClasses
--dontobfuscate
--dontoptimize
--dontshrink
\ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt 
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index 3fc49a9..9e49f54 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -24,18 +24,11 @@ import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.distinctUntilChanged
 import androidx.lifecycle.viewModelScope
-import 
com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 import net.taler.common.Event
 import net.taler.common.assertUiThread
 import net.taler.common.toEvent
-import net.taler.lib.common.Amount
-import net.taler.lib.common.AmountMixin
-import net.taler.lib.common.Timestamp
-import net.taler.lib.common.TimestampMixin
 import net.taler.wallet.backend.WalletBackendApi
 import net.taler.wallet.balances.BalanceItem
 import net.taler.wallet.balances.BalanceResponse
@@ -70,12 +63,6 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     var merchantVersion: String? = null
         private set
 
-    private val mapper = ObjectMapper()
-        .registerModule(KotlinModule())
-        .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
-        .addMixIn(Amount::class.java, AmountMixin::class.java)
-        .addMixIn(Timestamp::class.java, TimestampMixin::class.java)
-
     private val api = WalletBackendApi(app) { payload ->
         if (payload.optString("operation") == "init") {
             val result = payload.getJSONObject("result")
@@ -99,9 +86,9 @@ class MainViewModel(val app: Application) : 
AndroidViewModel(app) {
     }
 
     val withdrawManager = WithdrawManager(api, viewModelScope)
-    val paymentManager = PaymentManager(api, viewModelScope, mapper)
+    val paymentManager = PaymentManager(api, viewModelScope)
     val pendingOperationsManager: PendingOperationsManager = 
PendingOperationsManager(api)
-    val transactionManager: TransactionManager = TransactionManager(api, 
viewModelScope, mapper)
+    val transactionManager: TransactionManager = TransactionManager(api, 
viewModelScope)
     val refundManager = RefundManager(api, viewModelScope)
     val exchangeManager: ExchangeManager = ExchangeManager(api, viewModelScope)
 
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
index 5ca2255..c6261bf 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -26,13 +26,11 @@ import android.os.IBinder
 import android.os.Message
 import android.os.Messenger
 import android.util.Log
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
 import kotlinx.serialization.KSerializer
 import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.JsonConfiguration
+import net.taler.lib.android.CustomClassDiscriminator
 import net.taler.wallet.backend.WalletBackendService.Companion.MSG_COMMAND
 import net.taler.wallet.backend.WalletBackendService.Companion.MSG_NOTIFY
 import net.taler.wallet.backend.WalletBackendService.Companion.MSG_REPLY
@@ -44,14 +42,12 @@ import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicInteger
 import kotlin.coroutines.resume
 import kotlin.coroutines.suspendCoroutine
+import kotlin.reflect.full.companionObjectInstance
 
 class WalletBackendApi(
     private val app: Application,
     private val notificationHandler: ((payload: JSONObject) -> Unit)
 ) {
-    private val json = Json(
-        JsonConfiguration.Stable.copy(ignoreUnknownKeys = true)
-    )
     private var walletBackendMessenger: Messenger? = null
     private val queuedMessages = LinkedList<Message>()
     private val handlers = ConcurrentHashMap<Int, (isError: Boolean, message: 
JSONObject) -> Unit>()
@@ -148,38 +144,25 @@ class WalletBackendApi(
         }
     }
 
-    suspend fun <T> request(
+    suspend inline fun <reified T> request(
         operation: String,
         serializer: KSerializer<T>? = null,
-        args: (JSONObject.() -> JSONObject)? = null
+        noinline args: (JSONObject.() -> JSONObject)? = null
     ): WalletResponse<T> = withContext(Dispatchers.Default) {
-        suspendCoroutine<WalletResponse<T>> { cont ->
-            sendRequest(operation, args?.invoke(JSONObject())) { isError, 
message ->
-                val response = if (isError) {
-                    val error = json.parse(WalletErrorInfo.serializer(), 
message.toString())
-                    WalletResponse.Error<T>(error)
-                } else {
-                    @Suppress("UNCHECKED_CAST") // if serializer is null, T 
must be Unit
-                    val t: T = serializer?.let { json.parse(serializer, 
message.toString()) } ?: Unit as T
-                    WalletResponse.Success(t)
+        suspendCoroutine { cont ->
+            val json = Json {
+                ignoreUnknownKeys = true
+                (T::class.companionObjectInstance as? 
CustomClassDiscriminator)?.let {
+                    classDiscriminator = it.discriminator
                 }
-                cont.resume(response)
             }
-        }
-    }
-
-    suspend inline fun <reified T> request(
-        operation: String,
-        mapper: ObjectMapper,
-        noinline args: (JSONObject.() -> JSONObject)? = null
-    ): WalletResponse<T> = withContext(Dispatchers.Default) {
-        suspendCoroutine<WalletResponse<T>> { cont ->
             sendRequest(operation, args?.invoke(JSONObject())) { isError, 
message ->
                 val response = if (isError) {
-                    val error: WalletErrorInfo = 
mapper.readValue(message.toString())
-                    WalletResponse.Error<T>(error)
+                    val error = 
json.decodeFromString(WalletErrorInfo.serializer(), message.toString())
+                    WalletResponse.Error(error)
                 } else {
-                    val t: T = mapper.readValue(message.toString())
+                    @Suppress("UNCHECKED_CAST") // if serializer is null, T 
must be Unit
+                    val t: T = serializer?.let { 
json.decodeFromString(serializer, message.toString()) } ?: Unit as T
                     WalletResponse.Success(t)
                 }
                 cont.resume(response)
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt 
b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
index ab3d42e..4b39ff8 100644
--- a/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
+++ b/wallet/src/main/java/net/taler/wallet/backend/WalletResponse.kt
@@ -16,23 +16,17 @@
 
 package net.taler.wallet.backend
 
-import com.fasterxml.jackson.core.JsonParser
-import com.fasterxml.jackson.databind.DeserializationContext
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer
-import kotlinx.serialization.Decoder
-import kotlinx.serialization.Encoder
 import kotlinx.serialization.KSerializer
-import kotlinx.serialization.PrimitiveDescriptor
-import kotlinx.serialization.PrimitiveKind.STRING
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.JsonInput
+import kotlinx.serialization.descriptors.PrimitiveKind.STRING
+import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.json.JsonDecoder
 import kotlinx.serialization.json.JsonObject
 import org.json.JSONObject
 
-
 @Serializable
 sealed class WalletResponse<T> {
     @Serializable
@@ -73,7 +67,6 @@ data class WalletErrorInfo(
 
     // Error details, type depends on talerErrorCode
     @Serializable(JSONObjectDeserializer::class)
-    @JsonDeserialize(using = JsonObjectDeserializer::class)
     val details: JSONObject?
 ) {
     val userFacingMsg: String
@@ -96,11 +89,11 @@ data class WalletErrorInfo(
 
 class JSONObjectDeserializer : KSerializer<JSONObject> {
 
-    override val descriptor = PrimitiveDescriptor("JSONObjectDeserializer", 
STRING)
+    override val descriptor = 
PrimitiveSerialDescriptor("JSONObjectDeserializer", STRING)
 
     override fun deserialize(decoder: Decoder): JSONObject {
-        val input = decoder as JsonInput
-        val tree = input.decodeJson() as JsonObject
+        val input = decoder as JsonDecoder
+        val tree = input.decodeJsonElement() as JsonObject
         return JSONObject(tree.toString())
     }
 
@@ -108,10 +101,3 @@ class JSONObjectDeserializer : KSerializer<JSONObject> {
         error("not supported")
     }
 }
-
-class JsonObjectDeserializer : 
StdDeserializer<JSONObject>(JSONObject::class.java) {
-    override fun deserialize(p: JsonParser, ctxt: DeserializationContext): 
JSONObject {
-        val node: JsonNode = p.codec.readTree(p)
-        return JSONObject(node.toString())
-    }
-}
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
index 4924752..befcd83 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentManager.kt
@@ -20,12 +20,10 @@ import android.util.Log
 import androidx.annotation.UiThread
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.readValue
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
-import net.taler.lib.common.Amount
 import net.taler.common.ContractTerms
+import net.taler.lib.common.Amount
 import net.taler.wallet.TAG
 import net.taler.wallet.backend.WalletBackendApi
 import net.taler.wallet.backend.WalletErrorInfo
@@ -34,8 +32,6 @@ import net.taler.wallet.payment.PayStatus.InsufficientBalance
 import net.taler.wallet.payment.PreparePayResponse.AlreadyConfirmedResponse
 import net.taler.wallet.payment.PreparePayResponse.InsufficientBalanceResponse
 import net.taler.wallet.payment.PreparePayResponse.PaymentPossibleResponse
-import org.json.JSONObject
-import java.net.MalformedURLException
 
 val REGEX_PRODUCT_IMAGE = 
Regex("^data:image/(jpeg|png);base64,([A-Za-z0-9+/=]+)$")
 
@@ -63,7 +59,6 @@ sealed class PayStatus {
 class PaymentManager(
     private val api: WalletBackendApi,
     private val scope: CoroutineScope,
-    private val mapper: ObjectMapper
 ) {
 
     private val mPayStatus = MutableLiveData<PayStatus>(PayStatus.None)
@@ -76,7 +71,7 @@ class PaymentManager(
     fun preparePay(url: String) = scope.launch {
         mPayStatus.value = PayStatus.Loading
         mDetailsShown.value = false
-        api.request<PreparePayResponse>("preparePay", mapper) {
+        api.request("preparePay", PreparePayResponse.serializer()) {
             put("talerPayUri", url)
         }.onError {
             handleError("preparePay", it)
@@ -93,20 +88,6 @@ class PaymentManager(
         }
     }
 
-    // TODO validate product images (or leave to wallet-core?)
-    private fun getContractTerms(json: JSONObject): ContractTerms {
-        val terms: ContractTerms = 
mapper.readValue(json.getString("contractTermsRaw"))
-        // validate product images
-        terms.products.forEach { product ->
-            product.image?.let { image ->
-                if (REGEX_PRODUCT_IMAGE.matchEntire(image) == null) {
-                    throw MalformedURLException("Invalid image data URL for 
${product.description}")
-                }
-            }
-        }
-        return terms
-    }
-
     fun confirmPay(proposalId: String, currency: String) = scope.launch {
         api.request("confirmPay", ConfirmPayResult.serializer()) {
             put("proposalId", proposalId)
@@ -128,7 +109,7 @@ class PaymentManager(
 
     internal fun abortProposal(proposalId: String) = scope.launch {
         Log.i(TAG, "aborting proposal")
-        api.request<String>("abortProposal", mapper) {
+        api.request<Unit>("abortProposal") {
             put("proposalId", proposalId)
         }.onError {
             Log.e(TAG, "received error response to abortProposal")
diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
index c490654..2e99806 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt
@@ -16,61 +16,62 @@
 
 package net.taler.wallet.payment
 
-import com.fasterxml.jackson.annotation.JsonTypeInfo
-import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME
-import com.fasterxml.jackson.annotation.JsonTypeName
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import net.taler.common.ContractTerms
+import net.taler.lib.android.CustomClassDiscriminator
 import net.taler.lib.common.Amount
 import net.taler.wallet.transactions.TransactionError
 
-@JsonTypeInfo(use = NAME, property = "status")
-sealed class PreparePayResponse(open val proposalId: String) {
-    @JsonTypeName("payment-possible")
+@Serializable
+sealed class PreparePayResponse {
+    companion object : CustomClassDiscriminator {
+        override val discriminator: String = "status"
+    }
+
+    @Serializable
+    @SerialName("payment-possible")
     data class PaymentPossibleResponse(
-        override val proposalId: String,
+        val proposalId: String,
         val amountRaw: Amount,
         val amountEffective: Amount,
-        val contractTerms: ContractTerms
-    ) : PreparePayResponse(proposalId) {
+        val contractTerms: ContractTerms,
+    ) : PreparePayResponse() {
         fun toPayStatusPrepared() = PayStatus.Prepared(
             contractTerms = contractTerms,
             proposalId = proposalId,
             amountRaw = amountRaw,
-            amountEffective = amountEffective
+            amountEffective = amountEffective,
         )
     }
 
-    @JsonTypeName("insufficient-balance")
+    @Serializable
+    @SerialName("insufficient-balance")
     data class InsufficientBalanceResponse(
-        override val proposalId: String,
+        val proposalId: String,
         val amountRaw: Amount,
-        val contractTerms: ContractTerms
-    ) : PreparePayResponse(proposalId)
+        val contractTerms: ContractTerms,
+    ) : PreparePayResponse()
 
-    @JsonTypeName("already-confirmed")
+    @Serializable
+    @SerialName("already-confirmed")
     data class AlreadyConfirmedResponse(
-        override val proposalId: String,
+        val proposalId: String,
         /**
          * Did the payment succeed?
          */
         val paid: Boolean,
         val amountRaw: Amount,
         val amountEffective: Amount,
-
-        /**
-         * Redirect URL for the fulfillment page, only given if paid==true.
-         */
-        val nextUrl: String?
-    ) : PreparePayResponse(proposalId)
+        val contractTerms: ContractTerms,
+    ) : PreparePayResponse()
 }
 
 @Serializable
 sealed class ConfirmPayResult {
     @Serializable
     @SerialName("done")
-    data class Done(val nextUrl: String) : ConfirmPayResult()
+    data class Done(val contractTerms: ContractTerms) : ConfirmPayResult()
 
     @Serializable
     @SerialName("pending")
diff --git 
a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt 
b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
index 3d00900..99a6ec8 100644
--- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt
@@ -58,7 +58,7 @@ class PromptPaymentFragment : Fragment(), 
ProductImageClickListener {
     }
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
-        paymentManager.payStatus.observe(viewLifecycleOwner, 
this::onPaymentStatusChanged)
+        paymentManager.payStatus.observe(viewLifecycleOwner, 
::onPaymentStatusChanged)
         paymentManager.detailsShown.observe(viewLifecycleOwner, Observer { 
shown ->
             beginDelayedTransition(view as ViewGroup)
             val res = if (shown) R.string.payment_hide_details else 
R.string.payment_show_details
@@ -91,7 +91,7 @@ class PromptPaymentFragment : Fragment(), 
ProductImageClickListener {
         }
     }
 
-    private fun onPaymentStatusChanged(payStatus: PayStatus) {
+    private fun onPaymentStatusChanged(payStatus: PayStatus?) {
         when (payStatus) {
             is PayStatus.Prepared -> {
                 showLoading(false)
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
index f494b05..9dc2d23 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
@@ -104,7 +104,7 @@ internal class TransactionAdapter(
         private fun bindExtraInfo(transaction: Transaction) {
             if (transaction.error != null) {
                 extraInfoView.text =
-                    context.getString(R.string.payment_error, 
transaction.error.text)
+                    context.getString(R.string.payment_error, 
transaction.error!!.text)
                 extraInfoView.setTextColor(red)
                 extraInfoView.visibility = VISIBLE
             } else if (transaction is TransactionWithdrawal && 
!transaction.confirmed) {
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
index f5a4fc9..1103207 100644
--- 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
+++ 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
@@ -145,7 +145,7 @@ class TransactionDetailFragment : Fragment() {
         orderAmountView.text = raw.toString()
         feeView.text = getString(R.string.amount_negative, fee.toString())
         orderSummaryView.text = info.summary
-        if (info.fulfillmentUrl.startsWith("http")) {
+        if (info.fulfillmentUrl?.startsWith("http") == true) {
             val i = Intent().apply {
                 data = Uri.parse(info.fulfillmentUrl)
             }
diff --git 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
index e9b1b71..6b5a79b 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -20,11 +20,11 @@ import androidx.annotation.UiThread
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.switchMap
-import com.fasterxml.jackson.databind.ObjectMapper
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import net.taler.wallet.backend.WalletBackendApi
 import java.util.HashMap
+import java.util.LinkedList
 
 sealed class TransactionsResult {
     class Error(val msg: String) : TransactionsResult()
@@ -33,8 +33,7 @@ sealed class TransactionsResult {
 
 class TransactionManager(
     private val api: WalletBackendApi,
-    private val scope: CoroutineScope,
-    private val mapper: ObjectMapper
+    private val scope: CoroutineScope
 ) {
 
     private val mProgress = MutableLiveData<Boolean>()
@@ -64,14 +63,14 @@ class TransactionManager(
         }
         if (liveData.value == null) mProgress.value = true
 
-        api.request<Transactions>("getTransactions", mapper) {
+        api.request("getTransactions", Transactions.serializer()) {
             if (searchQuery != null) put("search", searchQuery)
             put("currency", currency)
         }.onError {
             liveData.postValue(TransactionsResult.Error(it.userFacingMsg))
             mProgress.postValue(false)
         }.onSuccess { result ->
-            val transactions = result.transactions
+            val transactions = LinkedList(result.transactions)
             // TODO remove when fixed in wallet-core
             val comparator = compareBy<Transaction>(
                 { it.pending },
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt 
b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index 1ed6788..3210093 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -20,42 +20,30 @@ import android.content.Context
 import androidx.annotation.DrawableRes
 import androidx.annotation.LayoutRes
 import androidx.annotation.StringRes
-import com.fasterxml.jackson.annotation.JsonProperty
-import com.fasterxml.jackson.annotation.JsonSubTypes
-import com.fasterxml.jackson.annotation.JsonSubTypes.Type
-import com.fasterxml.jackson.annotation.JsonTypeInfo
-import com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY
-import com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME
-import com.fasterxml.jackson.annotation.JsonTypeName
+import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import net.taler.lib.common.Amount
+import kotlinx.serialization.Transient
 import net.taler.common.ContractMerchant
 import net.taler.common.ContractProduct
+import net.taler.lib.common.Amount
 import net.taler.lib.common.Timestamp
 import net.taler.wallet.R
 import net.taler.wallet.cleanExchange
 import net.taler.wallet.transactions.WithdrawalDetails.ManualTransfer
 import net.taler.wallet.transactions.WithdrawalDetails.TalerBankIntegrationApi
-import java.util.LinkedList
 
-data class Transactions(val transactions: LinkedList<Transaction>)
+@Serializable
+data class Transactions(val transactions: List<Transaction>)
+
+@Serializable
+sealed class Transaction {
+    abstract val transactionId: String
+    abstract val timestamp: Timestamp
+    abstract val pending: Boolean
+    abstract val error: TransactionError?
+    abstract val amountRaw: Amount
+    abstract val amountEffective: Amount
 
-@JsonTypeInfo(use = NAME, include = PROPERTY, property = "type")
-@JsonSubTypes(
-    Type(value = TransactionWithdrawal::class, name = "withdrawal"),
-    Type(value = TransactionPayment::class, name = "payment"),
-    Type(value = TransactionRefund::class, name = "refund"),
-    Type(value = TransactionTip::class, name = "tip"),
-    Type(value = TransactionRefresh::class, name = "refresh")
-)
-abstract class Transaction(
-    val transactionId: String,
-    val timestamp: Timestamp,
-    val pending: Boolean,
-    val error: TransactionError? = null,
-    val amountRaw: Amount,
-    val amountEffective: Amount
-) {
     @get:DrawableRes
     abstract val icon: Int
 
@@ -79,24 +67,27 @@ sealed class AmountType {
 @Serializable
 data class TransactionError(
     private val ec: Int,
-    private val hint: String?
+    private val hint: String? = null,
 ) {
     val text get() = if (hint == null) "$ec" else "$ec $hint"
 }
 
-@JsonTypeName("withdrawal")
+@Serializable
+@SerialName("withdrawal")
 class TransactionWithdrawal(
-    transactionId: String,
-    timestamp: Timestamp,
-    pending: Boolean,
+    override val transactionId: String,
+    override val timestamp: Timestamp,
+    override val pending: Boolean,
     val exchangeBaseUrl: String,
     val withdrawalDetails: WithdrawalDetails,
-    error: TransactionError? = null,
-    amountRaw: Amount,
-    amountEffective: Amount
-) : Transaction(transactionId, timestamp, pending, error, amountRaw, 
amountEffective) {
+    override val error: TransactionError? = null,
+    override val amountRaw: Amount,
+    override val amountEffective: Amount
+) : Transaction() {
     override val icon = R.drawable.transaction_withdrawal
     override val detailPageLayout = R.layout.fragment_transaction_withdrawal
+
+    @Transient
     override val amountType = AmountType.Positive
     override fun getTitle(context: Context) = cleanExchange(exchangeBaseUrl)
     override val generalTitleRes = R.string.withdraw_title
@@ -107,13 +98,10 @@ class TransactionWithdrawal(
                 )
 }
 
-@JsonTypeInfo(use = NAME, include = PROPERTY, property = "type")
-@JsonSubTypes(
-    Type(value = TalerBankIntegrationApi::class, name = 
"taler-bank-integration-api"),
-    Type(value = ManualTransfer::class, name = "manual-transfer")
-)
+@Serializable
 sealed class WithdrawalDetails {
-    @JsonTypeName("manual-transfer")
+    @Serializable
+    @SerialName("manual-transfer")
     class ManualTransfer(
         /**
          * Payto URIs that the exchange supports.
@@ -123,7 +111,8 @@ sealed class WithdrawalDetails {
         val exchangePaytoUris: List<String>
     ) : WithdrawalDetails()
 
-    @JsonTypeName("taler-bank-integration-api")
+    @Serializable
+    @SerialName("taler-bank-integration-api")
     class TalerBankIntegrationApi(
         /**
          * Set to true if the bank has confirmed the withdrawal, false if not.
@@ -136,71 +125,88 @@ sealed class WithdrawalDetails {
         /**
          * If the withdrawal is unconfirmed, this can include a URL for 
user-initiated confirmation.
          */
-        val bankConfirmationUrl: String?
+        val bankConfirmationUrl: String? = null,
     ) : WithdrawalDetails()
 }
 
-@JsonTypeName("payment")
+@Serializable
+@SerialName("payment")
 class TransactionPayment(
-    transactionId: String,
-    timestamp: Timestamp,
-    pending: Boolean,
+    override val transactionId: String,
+    override val timestamp: Timestamp,
+    override val pending: Boolean,
     val info: TransactionInfo,
     val status: PaymentStatus,
-    error: TransactionError? = null,
-    amountRaw: Amount,
-    amountEffective: Amount
-) : Transaction(transactionId, timestamp, pending, error, amountRaw, 
amountEffective) {
+    override val error: TransactionError? = null,
+    override val amountRaw: Amount,
+    override val amountEffective: Amount
+) : Transaction() {
     override val icon = R.drawable.ic_cash_usd_outline
     override val detailPageLayout = R.layout.fragment_transaction_payment
+
+    @Transient
     override val amountType = AmountType.Negative
     override fun getTitle(context: Context) = info.merchant.name
     override val generalTitleRes = R.string.payment_title
 }
 
+@Serializable
 class TransactionInfo(
     val orderId: String,
     val merchant: ContractMerchant,
     val summary: String,
-    @get:JsonProperty("summary_i18n")
-    val summaryI18n: Map<String, String>?,
+    @SerialName("summary_i18n")
+    val summaryI18n: Map<String, String>? = null,
     val products: List<ContractProduct>,
-    val fulfillmentUrl: String
+    val fulfillmentUrl: String? = null,
+    /**
+     * Message shown to the user after the payment is complete.
+     * TODO actually show this
+     */
+    val fulfillmentMessage: String? = null,
+    /**
+     * Map from IETF BCP 47 language tags to localized fulfillment messages
+     */
+    val fulfillmentMessage_i18n: Map<String, String>? = null,
 )
 
+@Serializable
 enum class PaymentStatus {
-    @JsonProperty("aborted")
+    @SerialName("aborted")
     Aborted,
 
-    @JsonProperty("failed")
+    @SerialName("failed")
     Failed,
 
-    @JsonProperty("paid")
+    @SerialName("paid")
     Paid,
 
-    @JsonProperty("accepted")
+    @SerialName("accepted")
     Accepted
 }
 
-@JsonTypeName("refund")
+@Serializable
+@SerialName("refund")
 class TransactionRefund(
-    transactionId: String,
-    timestamp: Timestamp,
-    pending: Boolean,
+    override val transactionId: String,
+    override val timestamp: Timestamp,
+    override val pending: Boolean,
     val refundedTransactionId: String,
     val info: TransactionInfo,
     /**
      * Part of the refund that couldn't be applied because the refund 
permissions were expired
      */
     val amountInvalid: Amount? = null,
-    error: TransactionError? = null,
-    @JsonProperty("amountEffective") // TODO remove when fixed in wallet-core
-    amountRaw: Amount,
-    @JsonProperty("amountRaw") // TODO remove when fixed in wallet-core
-    amountEffective: Amount
-) : Transaction(transactionId, timestamp, pending, error, amountRaw, 
amountEffective) {
+    override val error: TransactionError? = null,
+    @SerialName("amountEffective") // TODO remove when fixed in wallet-core
+    override val amountRaw: Amount,
+    @SerialName("amountRaw") // TODO remove when fixed in wallet-core
+    override val amountEffective: Amount
+) : Transaction() {
     override val icon = R.drawable.transaction_refund
     override val detailPageLayout = R.layout.fragment_transaction_payment
+
+    @Transient
     override val amountType = AmountType.Positive
     override fun getTitle(context: Context): String {
         return context.getString(R.string.transaction_refund_from, 
info.merchant.name)
@@ -209,20 +215,23 @@ class TransactionRefund(
     override val generalTitleRes = R.string.refund_title
 }
 
-@JsonTypeName("tip")
+@Serializable
+@SerialName("tip")
 class TransactionTip(
-    transactionId: String,
-    timestamp: Timestamp,
-    pending: Boolean,
+    override val transactionId: String,
+    override val timestamp: Timestamp,
+    override val pending: Boolean,
     // TODO status: TipStatus,
     val exchangeBaseUrl: String,
     val merchant: ContractMerchant,
-    error: TransactionError? = null,
-    amountRaw: Amount,
-    amountEffective: Amount
-) : Transaction(transactionId, timestamp, pending, error, amountRaw, 
amountEffective) {
+    override val error: TransactionError? = null,
+    override val amountRaw: Amount,
+    override val amountEffective: Amount
+) : Transaction() {
     override val icon = R.drawable.transaction_tip_accepted // TODO different 
when declined
     override val detailPageLayout = R.layout.fragment_transaction_payment
+
+    @Transient
     override val amountType = AmountType.Positive
     override fun getTitle(context: Context): String {
         return context.getString(R.string.transaction_tip_from, merchant.name)
@@ -231,18 +240,21 @@ class TransactionTip(
     override val generalTitleRes = R.string.tip_title
 }
 
-@JsonTypeName("refresh")
+@Serializable
+@SerialName("refresh")
 class TransactionRefresh(
-    transactionId: String,
-    timestamp: Timestamp,
-    pending: Boolean,
+    override val transactionId: String,
+    override val timestamp: Timestamp,
+    override val pending: Boolean,
     val exchangeBaseUrl: String,
-    error: TransactionError? = null,
-    amountRaw: Amount,
-    amountEffective: Amount
-) : Transaction(transactionId, timestamp, pending, error, amountRaw, 
amountEffective) {
+    override val error: TransactionError? = null,
+    override val amountRaw: Amount,
+    override val amountEffective: Amount
+) : Transaction() {
     override val icon = R.drawable.transaction_refresh
     override val detailPageLayout = R.layout.fragment_transaction_withdrawal
+
+    @Transient
     override val amountType = AmountType.Negative
     override fun getTitle(context: Context): String {
         return context.getString(R.string.transaction_refresh)
diff --git 
a/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt 
b/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt
index d8f28c5..4872149 100644
--- a/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt
+++ b/wallet/src/test/java/net/taler/wallet/backend/WalletResponseTest.kt
@@ -16,36 +16,21 @@
 
 package net.taler.wallet.backend
 
-import com.fasterxml.jackson.databind.DeserializationFeature
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.fasterxml.jackson.module.kotlin.KotlinModule
-import com.fasterxml.jackson.module.kotlin.readValue
 import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.JsonConfiguration
-import net.taler.lib.common.Amount
-import net.taler.lib.common.AmountMixin
-import net.taler.lib.common.Timestamp
-import net.taler.lib.common.TimestampMixin
 import net.taler.wallet.balances.BalanceResponse
 import org.junit.Assert.assertEquals
 import org.junit.Test
 
 class WalletResponseTest {
 
-    private val json = Json(
-        JsonConfiguration.Stable.copy(ignoreUnknownKeys = true)
-    )
-
-    private val mapper = ObjectMapper()
-        .registerModule(KotlinModule())
-        .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
-        .addMixIn(Amount::class.java, AmountMixin::class.java)
-        .addMixIn(Timestamp::class.java, TimestampMixin::class.java)
+    private val json = Json {
+        ignoreUnknownKeys = true
+    }
 
     @Test
     fun testBalanceResponse() {
         val serializer = 
WalletResponse.Success.serializer(BalanceResponse.serializer())
-        val response = json.parse(
+        val response = json.decodeFromString(
             serializer, """
             {
               "type": "response",
@@ -82,9 +67,7 @@ class WalletResponseTest {
                 "message":"unexpected exception: Error: BUG: invariant 
violation (purchase status)"
             }
         """.trimIndent()
-        val info = json.parse(WalletErrorInfo.serializer(), infoJson)
-        val infoJackson: WalletErrorInfo = mapper.readValue(infoJson)
+        val info = json.decodeFromString(WalletErrorInfo.serializer(), 
infoJson)
         println(info.userFacingMsg)
-        assertEquals(info.userFacingMsg, infoJackson.userFacingMsg)
     }
 }

-- 
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]