Orders.kt
/*
* 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.merchantlib
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonClassDiscriminator
import kotlinx.serialization.json.JsonObject
import net.taler.common.Amount
import net.taler.common.ExternalPayment
import net.taler.common.Location
import net.taler.common.Merchant
import net.taler.common.RelativeTime
import net.taler.common.Timestamp
@Serializable
data class PostOrderRequest(
@SerialName("order")
val contractTerms: net.taler.common.Order,
@SerialName("refund_delay")
val refundDelay: RelativeTime? = null,
@SerialName("payment_target")
val paymentTarget: String? = null,
@SerialName("session_id")
val sessionId: String? = null,
@SerialName("inventory_products")
val inventoryProducts: List<MinimalInventoryProduct>? = null,
@SerialName("lock_uuids")
val lockUuids: List<String>? = null,
@SerialName("create_token")
val createToken: Boolean = true,
@SerialName("otp_id")
val otpId: String? = null,
)
@Serializable
data class MinimalInventoryProduct(
@SerialName("product_id")
val productId: String,
val quantity: Int? = null,
@SerialName("unit_quantity")
val unitQuantity: String? = null,
@SerialName("product_money_pot")
val productMoneyPot: Int? = null,
)
/**
* Body of `POST /private/orders/$ORDER_ID/collect`, which completes an order whose
* Taler amount is zero without a wallet (DD 96). [choiceIndex] is only for v1 orders.
*/
@Serializable
data class CollectOrderRequest(
@SerialName("choice_index")
val choiceIndex: Int? = null,
)
@Serializable
data class PostOrderResponse(
@SerialName("order_id")
val orderId: String
)
@OptIn(ExperimentalSerializationApi::class)
@Serializable
@JsonClassDiscriminator("order_status")
sealed class CheckPaymentResponse {
abstract val paid: Boolean
abstract val paymentDetails: OrderPaymentDetails?
@Serializable
@SerialName("unpaid")
data class Unpaid(
override val paid: Boolean = false,
@SerialName("taler_pay_uri")
val talerPayUri: String,
@SerialName("already_paid_order_id")
val alreadyPaidOrderId: String? = null,
@SerialName("proto_contract_terms")
override val paymentDetails: OrderPaymentDetails? = null,
) : CheckPaymentResponse()
@Serializable
@SerialName("claimed")
data class Claimed(
override val paid: Boolean = false,
@SerialName("contract_terms")
override val paymentDetails: OrderPaymentDetails? = null,
) : CheckPaymentResponse()
@Serializable
@SerialName("paid")
data class Paid(
override val paid: Boolean = true,
val refunded: Boolean,
@SerialName("refund_pending")
val refundPending: Boolean = false,
@SerialName("refund_amount")
val refundAmount: Amount? = null,
@SerialName("contract_terms")
override val paymentDetails: OrderPaymentDetails? = null,
@SerialName("refunds_external")
val refundsExternal: List<ExternalRefundInfo> = emptyList(),
) : CheckPaymentResponse()
}
/** Common payment fields shared by v0, v1 and unclaimed contract terms. */
@Serializable
data class OrderPaymentDetails(
@SerialName("amount_external")
val amountExternal: List<ExternalPayment> = emptyList(),
val summary: String? = null,
@SerialName("summary_i18n")
val summaryI18n: Map<String, String>? = null,
val amount: Amount? = null,
val timestamp: Timestamp? = null,
val merchant: Merchant? = null,
val products: List<OrderDetailProduct> = emptyList(),
@SerialName("fulfillment_url")
val fulfillmentUrl: String? = null,
@SerialName("fulfillment_message")
val fulfillmentMessage: String? = null,
@SerialName("fulfillment_message_i18n")
val fulfillmentMessageI18n: Map<String, String>? = null,
@SerialName("public_reorder_url")
val publicReorderUrl: String? = null,
@SerialName("delivery_location")
val deliveryLocation: Location? = null,
@SerialName("delivery_date")
val deliveryDate: Timestamp? = null,
@SerialName("max_pickup_time")
val maxPickupTime: Timestamp? = null,
@SerialName("refund_deadline")
val refundDeadline: Timestamp? = null,
@SerialName("pay_deadline")
val payDeadline: Timestamp? = null,
val extra: JsonObject? = null,
)
/** Read-only ProductSold fields. Backend prices already cover the entire quantity. */
@Serializable
data class OrderDetailProduct(
val description: String,
@SerialName("product_name")
val productName: String? = null,
@SerialName("description_i18n")
val descriptionI18n: Map<String, String>? = null,
val quantity: Int = 1,
@SerialName("unit_quantity")
val unitQuantity: String? = null,
val unit: String? = null,
val price: Amount? = null,
val prices: List<Amount> = emptyList(),
@SerialName("prices_are_net")
val pricesAreNet: Boolean = false,
val image: String? = null,
) {
fun priceForCurrency(currency: String?): Amount? =
prices.firstOrNull { it.currency == currency }
?: price?.takeIf { currency == null || it.currency == currency }
}
@Serializable
data class ExternalRefundInfo(
val id: String,
val method: String,
val amount: Amount,
val reason: String,
val timestamp: Timestamp,
@SerialName("payment_id")
val paymentId: String? = null,
)