ContractTerms.kt (9223B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2020 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.common 18 19 import kotlinx.serialization.DeserializationStrategy 20 import kotlinx.serialization.ExperimentalSerializationApi 21 import kotlinx.serialization.EncodeDefault 22 import kotlinx.serialization.SerialName 23 import kotlinx.serialization.Serializable 24 import kotlinx.serialization.json.JsonClassDiscriminator 25 import kotlinx.serialization.json.JsonContentPolymorphicSerializer 26 import kotlinx.serialization.json.JsonElement 27 import kotlinx.serialization.json.intOrNull 28 import kotlinx.serialization.json.jsonObject 29 import kotlinx.serialization.json.jsonPrimitive 30 31 @Serializable 32 enum class ContractVersion(val version: Int) { 33 V0(0), 34 V1(1), 35 } 36 37 @OptIn(ExperimentalSerializationApi::class) 38 @Serializable(with = ContractTermsSerializer::class) 39 sealed class ContractTerms { 40 abstract val version: ContractVersion 41 abstract val summary: String 42 abstract val summaryI18n: Map<String, String>? 43 abstract val orderId: String 44 abstract val fulfillmentUrl: String? 45 abstract val fulfillmentMessage: String? 46 abstract val fulfillmentMessageI18n: Map <String, String>? 47 abstract val products: List<ContractProduct>? 48 abstract val refundDeadline: Timestamp? 49 abstract val payDeadline: Timestamp? 50 abstract val wireTransferDeadline: Timestamp? 51 abstract val merchantBaseUrl: String 52 abstract val merchant: Merchant 53 abstract val exchanges: List<Exchange> 54 abstract val deliveryLocation: Location? 55 abstract val deliveryDate: Timestamp? 56 57 @Serializable 58 @JsonClassDiscriminator("version") 59 data class V0 ( 60 override val summary: String, 61 62 @SerialName("summary_i18n") 63 override val summaryI18n: Map<String, String>? = null, 64 65 @SerialName("order_id") 66 override val orderId: String, 67 68 @SerialName("fulfillment_url") 69 override val fulfillmentUrl: String? = null, 70 71 @SerialName("fulfillment_message") 72 override val fulfillmentMessage: String? = null, 73 74 @SerialName("fulfillment_message_i18n") 75 override val fulfillmentMessageI18n: Map<String, String>? = null, 76 77 override val products: List<ContractProduct>? = null, 78 79 @SerialName("refund_deadline") 80 override val refundDeadline: Timestamp? = null, 81 82 @SerialName("pay_deadline") 83 override val payDeadline: Timestamp? = null, 84 85 @SerialName("wire_transfer_deadline") 86 override val wireTransferDeadline: Timestamp? = null, 87 88 @SerialName("merchant_base_url") 89 override val merchantBaseUrl: String, 90 91 override val merchant: Merchant, 92 93 override val exchanges: List<Exchange> = listOf(), 94 95 @SerialName("delivery_location") 96 override val deliveryLocation: Location? = null, 97 98 @SerialName("delivery_date") 99 override val deliveryDate: Timestamp? = null, 100 101 val amount: Amount, 102 103 @SerialName("max_fee") 104 val maxFee: Amount, 105 ) : ContractTerms() { 106 override val version: ContractVersion = ContractVersion.V0 107 } 108 109 @Serializable 110 @JsonClassDiscriminator("version") 111 data class V1 ( 112 override val summary: String, 113 114 @SerialName("summary_i18n") 115 override val summaryI18n: Map<String, String>? = null, 116 117 @SerialName("order_id") 118 override val orderId: String, 119 120 @SerialName("fulfillment_url") 121 override val fulfillmentUrl: String? = null, 122 123 @SerialName("fulfillment_message") 124 override val fulfillmentMessage: String? = null, 125 126 @SerialName("fulfillment_message_i18n") 127 override val fulfillmentMessageI18n: Map<String, String>? = null, 128 129 override val products: List<ContractProduct>? = null, 130 131 @SerialName("refund_deadline") 132 override val refundDeadline: Timestamp? = null, 133 134 @SerialName("pay_deadline") 135 override val payDeadline: Timestamp? = null, 136 137 @SerialName("wire_transfer_deadline") 138 override val wireTransferDeadline: Timestamp? = null, 139 140 @SerialName("merchant_base_url") 141 override val merchantBaseUrl: String, 142 143 override val merchant: Merchant, 144 145 override val exchanges: List<Exchange> = listOf(), 146 147 @SerialName("delivery_location") 148 override val deliveryLocation: Location? = null, 149 150 @SerialName("delivery_date") 151 override val deliveryDate: Timestamp? = null, 152 153 val choices: List<ContractChoice>, 154 155 @SerialName("token_families") 156 val tokenFamilies: Map<String, ContractTokenFamily>, 157 ) : ContractTerms() { 158 override val version: ContractVersion = ContractVersion.V1 159 } 160 } 161 162 @Serializable 163 data class Merchant( 164 val name: String, 165 val email: String? = null, 166 val website: String? = null, 167 val logo: String? = null, 168 val address: Location? = null, 169 val jurisdiction: Location? = null 170 ) 171 172 @Serializable 173 data class Location( 174 val country: String? = null, 175 @SerialName("country_subdivision") 176 val countrySubdivision: String? = null, 177 val district: String? = null, 178 val town: String? = null, 179 @SerialName("town_location") 180 val townLocation: String? = null, 181 @SerialName("post_code") 182 val postCode: String? = null, 183 val street: String? = null, 184 @SerialName("building_name") 185 val buildingName: String? = null, 186 @SerialName("building_number") 187 val buildingNumber: String? = null, 188 @SerialName("address_lines") 189 val addressLines: List<String>? = null, 190 ) 191 192 @Serializable 193 data class ContractProduct( 194 @SerialName("product_id") 195 override val productId: String? = null, 196 @SerialName("product_name") 197 override val productName: String? = null, 198 override val description: String, 199 @SerialName("description_i18n") 200 override val descriptionI18n: Map<String, String>? = null, 201 override val price: Amount? = null, 202 @SerialName("prices_are_net") 203 @EncodeDefault 204 val pricesAreNet: Boolean = false, 205 @SerialName("delivery_location") 206 override val location: String? = null, 207 override val image: String? = null, 208 override val taxes: Set<Tax>? = null, 209 val quantity: Int = 1, 210 ) : OrderProduct() { 211 val totalPrice: Amount? by lazy { 212 price?.let { price * quantity } 213 } 214 } 215 216 @Serializable 217 data class Exchange( 218 val url: String, 219 ) 220 221 @Serializable 222 data class Tax( 223 val name: String, 224 val tax: Amount, 225 ) 226 227 @Serializable 228 data class ContractChoice( 229 val amount: Amount, 230 val description: String? = null, 231 @SerialName("description_i18n") 232 val descriptionI18n: Map<String, String>? = null, 233 val inputs: List<ContractInput>, 234 val outputs: List<ContractOutput>, 235 @SerialName("max_fee") 236 val maxFee: Amount, 237 ) 238 239 @Serializable 240 @OptIn(ExperimentalSerializationApi::class) 241 @JsonClassDiscriminator("type") 242 sealed class ContractInput { 243 @Serializable 244 @SerialName("token") 245 data class Token( 246 @SerialName("token_family_slug") 247 val tokenFamilySlug: String, 248 val count: Int = 1, 249 ): ContractInput() 250 } 251 252 @Serializable 253 @OptIn(ExperimentalSerializationApi::class) 254 @JsonClassDiscriminator("type") 255 sealed class ContractOutput { 256 @Serializable 257 @SerialName("token") 258 data class Token( 259 @SerialName("token_family_slug") 260 val tokenFamilySlug: String, 261 val count: Int = 1, 262 ): ContractOutput() 263 264 @Serializable 265 @SerialName("tax-receipt") 266 data class TaxReceipt( 267 val amount: Amount, 268 @SerialName("donau_urls") 269 val donauUrls: List<String>, 270 ): ContractOutput() 271 } 272 273 @Serializable 274 data class ContractTokenFamily( 275 val name: String, 276 val description: String, 277 val descriptionI18n: Map<String, String>? = null, 278 val details: ContractTokenDetails, 279 val critical: Boolean, 280 ) 281 282 @OptIn(ExperimentalSerializationApi::class) 283 @Serializable 284 @JsonClassDiscriminator("class") 285 sealed class ContractTokenDetails { 286 @Serializable 287 @SerialName("subscription") 288 data object Subscription: ContractTokenDetails() 289 290 @Serializable 291 @SerialName("discount") 292 data object Discount: ContractTokenDetails() 293 } 294 295 object ContractTermsSerializer : JsonContentPolymorphicSerializer<ContractTerms>(ContractTerms::class) { 296 override fun selectDeserializer(element: JsonElement): DeserializationStrategy<ContractTerms> { 297 return when(val type = element.jsonObject["version"]?.jsonPrimitive?.intOrNull) { 298 null, 0 -> ContractTerms.V0.serializer() 299 1 -> ContractTerms.V1.serializer() 300 else -> error("unknown contract version $type") 301 } 302 } 303 }