/* * 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 */ package net.taler.wallet.transactions import androidx.annotation.DrawableRes import androidx.annotation.LayoutRes import androidx.annotation.StringRes import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY 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 net.taler.common.Amount import net.taler.common.Timestamp import net.taler.wallet.R import org.json.JSONObject enum class ReserveType { /** * Manually created. */ @JsonProperty("manual") MANUAL, /** * Withdrawn from a bank that has "tight" Taler integration */ @JsonProperty("taler-bank-withdraw") @Suppress("unused") TALER_BANK_WITHDRAW, } @JsonInclude(NON_EMPTY) class ReserveCreationDetail(val type: ReserveType, val bankUrl: String?) enum class RefreshReason { @JsonProperty("manual") @Suppress("unused") MANUAL, @JsonProperty("pay") PAY, @JsonProperty("refund") @Suppress("unused") REFUND, @JsonProperty("abort-pay") @Suppress("unused") ABORT_PAY, @JsonProperty("recoup") @Suppress("unused") RECOUP, @JsonProperty("backup-restored") @Suppress("unused") BACKUP_RESTORED } @JsonInclude(NON_EMPTY) class ReserveShortInfo( /** * The exchange that the reserve will be at. */ val exchangeBaseUrl: String, /** * Key to query more details */ val reservePub: String, /** * Detail about how the reserve has been created. */ val reserveCreationDetail: ReserveCreationDetail ) typealias Transactions = ArrayList @JsonTypeInfo( use = NAME, include = PROPERTY, property = "type", defaultImpl = UnknownTransaction::class ) /** missing: AuditorComplaintSent = "auditor-complained-sent", AuditorComplaintProcessed = "auditor-complaint-processed", AuditorTrustAdded = "auditor-trust-added", AuditorTrustRemoved = "auditor-trust-removed", ExchangeTermsAccepted = "exchange-terms-accepted", ExchangePolicyChanged = "exchange-policy-changed", ExchangeTrustAdded = "exchange-trust-added", ExchangeTrustRemoved = "exchange-trust-removed", FundsDepositedToSelf = "funds-deposited-to-self", FundsRecouped = "funds-recouped", ReserveCreated = "reserve-created", */ @JsonSubTypes( Type(value = ExchangeAddedEvent::class, name = "exchange-added"), Type(value = ExchangeUpdatedEvent::class, name = "exchange-updated"), Type(value = ReserveBalanceUpdatedTransaction::class, name = "reserve-balance-updated"), Type(value = WithdrawTransaction::class, name = "withdrawn"), Type(value = OrderAcceptedTransaction::class, name = "order-accepted"), Type(value = OrderRefusedTransaction::class, name = "order-refused"), Type(value = OrderRedirectedTransaction::class, name = "order-redirected"), Type(value = PaymentTransaction::class, name = "payment-sent"), Type(value = PaymentAbortedTransaction::class, name = "payment-aborted"), Type(value = TipAcceptedTransaction::class, name = "tip-accepted"), Type(value = TipDeclinedTransaction::class, name = "tip-declined"), Type(value = RefundTransaction::class, name = "refund"), Type(value = RefreshTransaction::class, name = "refreshed") ) @JsonIgnoreProperties( value = [ "eventId" ] ) abstract class Transaction( val timestamp: Timestamp, @get:LayoutRes open val layout: Int = R.layout.transaction_row, @get:LayoutRes open val detailPageLayout: Int = 0, @get:StringRes open val title: Int = 0, @get:DrawableRes open val icon: Int = R.drawable.ic_account_balance, open val showToUser: Boolean = false ) { open lateinit var json: JSONObject } class UnknownTransaction(timestamp: Timestamp) : Transaction(timestamp) { override val title = R.string.transaction_unknown } @JsonTypeName("exchange-added") class ExchangeAddedEvent( timestamp: Timestamp, val exchangeBaseUrl: String, val builtIn: Boolean ) : Transaction(timestamp) { override val title = R.string.history_event_exchange_added } @JsonTypeName("exchange-updated") class ExchangeUpdatedEvent( timestamp: Timestamp, val exchangeBaseUrl: String ) : Transaction(timestamp) { override val title = R.string.history_event_exchange_updated } @JsonTypeName("reserve-balance-updated") class ReserveBalanceUpdatedTransaction( timestamp: Timestamp, /** * Condensed information about the reserve. */ val reserveShortInfo: ReserveShortInfo, /** * Amount currently left in the reserve. */ val reserveBalance: Amount, /** * Amount we expected to be in the reserve at that time, * considering ongoing withdrawals from that reserve. */ val reserveAwaitedAmount: Amount, /** * Amount that hasn't been withdrawn yet. */ val reserveUnclaimedAmount: Amount ) : Transaction(timestamp) { override val title = R.string.transaction_reserve_balance_updated } @JsonTypeName("withdrawn") class WithdrawTransaction( timestamp: Timestamp, /** * Exchange that was withdrawn from. */ val exchangeBaseUrl: String, /** * Unique identifier for the withdrawal session, can be used to * query more detailed information from the wallet. */ val withdrawalGroupId: String, val withdrawalSource: WithdrawalSource, /** * Amount that has been subtracted from the reserve's balance * for this withdrawal. */ val amountWithdrawnRaw: Amount, /** * Amount that actually was added to the wallet's balance. */ val amountWithdrawnEffective: Amount ) : Transaction(timestamp) { override val layout = R.layout.transaction_in override val detailPageLayout = R.layout.fragment_event_withdraw override val title = R.string.transaction_withdrawal override val icon = R.drawable.transaction_withdrawal override val showToUser = true } @JsonTypeName("order-accepted") class OrderAcceptedTransaction( timestamp: Timestamp, /** * Condensed info about the order. */ val orderShortInfo: OrderShortInfo ) : Transaction(timestamp) { override val icon = R.drawable.ic_add_circle override val title = R.string.transaction_order_accepted } @JsonTypeName("order-refused") class OrderRefusedTransaction( timestamp: Timestamp, /** * Condensed info about the order. */ val orderShortInfo: OrderShortInfo ) : Transaction(timestamp) { override val icon = R.drawable.ic_cancel override val title = R.string.transaction_order_refused } @JsonTypeName("payment-sent") class PaymentTransaction( timestamp: Timestamp, /** * Condensed info about the order that we already paid for. */ val orderShortInfo: OrderShortInfo, /** * Set to true if the payment has been previously sent * to the merchant successfully, possibly with a different session ID. */ val replay: Boolean, /** * Number of coins that were involved in the payment. */ val numCoins: Int, /** * Amount that was paid, including deposit and wire fees. */ val amountPaidWithFees: Amount, /** * Session ID that the payment was (re-)submitted under. */ val sessionId: String? ) : Transaction(timestamp) { override val layout = R.layout.transaction_out override val detailPageLayout = R.layout.fragment_event_paid override val title = R.string.transaction_payment override val icon = R.drawable.ic_cash_usd_outline override val showToUser = true } @JsonTypeName("payment-aborted") class PaymentAbortedTransaction( timestamp: Timestamp, /** * Condensed info about the order that we already paid for. */ val orderShortInfo: OrderShortInfo, /** * Amount that was lost due to refund and refreshing fees. */ val amountLost: Amount ) : Transaction(timestamp) { override val layout = R.layout.transaction_out override val title = R.string.transaction_payment_aborted override val icon = R.drawable.transaction_payment_aborted override val showToUser = true } @JsonTypeName("refreshed") class RefreshTransaction( timestamp: Timestamp, /** * Amount that is now available again because it has * been refreshed. */ val amountRefreshedEffective: Amount, /** * Amount that we spent for refreshing. */ val amountRefreshedRaw: Amount, /** * Why was the refreshing done? */ val refreshReason: RefreshReason, val numInputCoins: Int, val numRefreshedInputCoins: Int, val numOutputCoins: Int, /** * Identifier for a refresh group, contains one or * more refresh session IDs. */ val refreshGroupId: String ) : Transaction(timestamp) { override val layout = R.layout.transaction_out override val icon = R.drawable.transaction_refresh override val title = R.string.transaction_refresh override val showToUser = !(amountRefreshedRaw - amountRefreshedEffective).isZero() } @JsonTypeName("order-redirected") class OrderRedirectedTransaction( timestamp: Timestamp, /** * Condensed info about the new order that contains a * product (identified by the fulfillment URL) that we've already paid for. */ val newOrderShortInfo: OrderShortInfo, /** * Condensed info about the order that we already paid for. */ val alreadyPaidOrderShortInfo: OrderShortInfo ) : Transaction(timestamp) { override val icon = R.drawable.ic_directions override val title = R.string.transaction_order_redirected } @JsonTypeName("tip-accepted") class TipAcceptedTransaction( timestamp: Timestamp, /** * Unique identifier for the tip to query more information. */ val tipId: String, /** * Raw amount of the tip, without extra fees that apply. */ val tipRaw: Amount ) : Transaction(timestamp) { override val icon = R.drawable.transaction_tip_accepted override val title = R.string.transaction_tip_accepted override val layout = R.layout.transaction_in override val showToUser = true } @JsonTypeName("tip-declined") class TipDeclinedTransaction( timestamp: Timestamp, /** * Unique identifier for the tip to query more information. */ val tipId: String, /** * Raw amount of the tip, without extra fees that apply. */ val tipAmount: Amount ) : Transaction(timestamp) { override val icon = R.drawable.transaction_tip_declined override val title = R.string.transaction_tip_declined override val layout = R.layout.transaction_in override val showToUser = true } @JsonTypeName("refund") class RefundTransaction( timestamp: Timestamp, val orderShortInfo: OrderShortInfo, /** * Unique identifier for this refund. * (Identifies multiple refund permissions that were obtained at once.) */ val refundGroupId: String, /** * Part of the refund that couldn't be applied because * the refund permissions were expired. */ val amountRefundedInvalid: Amount, /** * Amount that has been refunded by the merchant. */ val amountRefundedRaw: Amount, /** * Amount will be added to the wallet's balance after fees and refreshing. */ val amountRefundedEffective: Amount ) : Transaction(timestamp) { override val icon = R.drawable.transaction_refund override val title = R.string.transaction_refund override val layout = R.layout.transaction_in override val detailPageLayout = R.layout.fragment_event_paid override val showToUser = true } @JsonTypeInfo( use = NAME, include = PROPERTY, property = "type" ) @JsonSubTypes( Type(value = WithdrawalSourceReserve::class, name = "reserve") ) abstract class WithdrawalSource @Suppress("unused") @JsonTypeName("tip") class WithdrawalSourceTip( val tipId: String ) : WithdrawalSource() @JsonTypeName("reserve") class WithdrawalSourceReserve( val reservePub: String ) : WithdrawalSource() data class OrderShortInfo( /** * Wallet-internal identifier of the proposal. */ val proposalId: String, /** * Order ID, uniquely identifies the order within a merchant instance. */ val orderId: String, /** * Base URL of the merchant. */ val merchantBaseUrl: String, /** * Amount that must be paid for the contract. */ val amount: Amount, /** * Summary of the proposal, given by the merchant. */ val summary: String )