taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit 97bd4d552971343f839b6f49543b1eecf02240aa
parent fe8a23b42341a4732f2f5967415068c56abd6adf
Author: Iván Ávalos <avalos@disroot.org>
Date:   Fri, 28 Mar 2025 15:10:09 +0100

[wallet] render causeHint in p2p push payments

Diffstat:
Mwallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt | 17+++--------------
Mwallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt | 24+++++++++++++++++++++++-
Mwallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt | 14++------------
Mwallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt | 6++++--
Mwallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt | 24+++++++++++++++++++-----
Mwallet/src/main/java/net/taler/wallet/peer/PeerManager.kt | 52++++++++++++++++++++++------------------------------
6 files changed, 73 insertions(+), 64 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/PayTemplateComposable.kt @@ -81,21 +81,10 @@ fun PayTemplateComposable( is PayStatus.AlreadyPaid -> PayTemplateError(stringResource(R.string.payment_already_paid)) is PayStatus.InsufficientBalance -> { var errorMsg = stringResource(R.string.payment_balance_insufficient) - when(p.balanceDetails.causeHint) { - null -> null - Unknown -> null - MerchantAcceptInsufficient -> R.string.payment_balance_insufficient_hint_merchant_accept_insufficient - MerchantDepositInsufficient -> R.string.payment_balance_insufficient_hint_merchant_deposit_insufficient - AgeRestricted -> R.string.payment_balance_insufficient_hint_age_restricted - WalletBalanceMaterialInsufficient -> R.string.payment_balance_insufficient_hint_wallet_balance_material_insufficient - WalletBalanceAvailableInsufficient -> null // "normal case" - ExchangeMissingGlobalFees -> R.string.payment_balance_insufficient_hint_exchange_missing_global_fees - FeesNotCovered -> R.string.payment_balance_insufficient_hint_fees_not_covered - }?.let { hintRes -> - errorMsg += "\n\n" - errorMsg += stringResource(hintRes) + p.balanceDetails.causeHint?.stringResId()?.let { + errorMsg += "\n\n" + errorMsg += stringResource(it) } - PayTemplateError(errorMsg) } is PayStatus.Pending -> { diff --git a/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt b/wallet/src/main/java/net/taler/wallet/payment/PaymentResponses.kt @@ -16,13 +16,23 @@ package net.taler.wallet.payment +import androidx.annotation.StringRes import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonClassDiscriminator import net.taler.common.Amount import net.taler.common.ContractTerms +import net.taler.wallet.R import net.taler.wallet.backend.TalerErrorInfo +import net.taler.wallet.payment.InsufficientBalanceHint.AgeRestricted +import net.taler.wallet.payment.InsufficientBalanceHint.ExchangeMissingGlobalFees +import net.taler.wallet.payment.InsufficientBalanceHint.FeesNotCovered +import net.taler.wallet.payment.InsufficientBalanceHint.MerchantAcceptInsufficient +import net.taler.wallet.payment.InsufficientBalanceHint.MerchantDepositInsufficient +import net.taler.wallet.payment.InsufficientBalanceHint.Unknown +import net.taler.wallet.payment.InsufficientBalanceHint.WalletBalanceAvailableInsufficient +import net.taler.wallet.payment.InsufficientBalanceHint.WalletBalanceMaterialInsufficient @OptIn(ExperimentalSerializationApi::class) @Serializable @@ -120,7 +130,19 @@ enum class InsufficientBalanceHint { * balance. */ @SerialName("fees-not-covered") - FeesNotCovered, + FeesNotCovered; +} + +@StringRes +fun InsufficientBalanceHint.stringResId(): Int? = when(this) { + Unknown -> null + MerchantAcceptInsufficient -> R.string.payment_balance_insufficient_hint_merchant_accept_insufficient + MerchantDepositInsufficient -> R.string.payment_balance_insufficient_hint_merchant_deposit_insufficient + AgeRestricted -> R.string.payment_balance_insufficient_hint_age_restricted + WalletBalanceMaterialInsufficient -> R.string.payment_balance_insufficient_hint_wallet_balance_material_insufficient + WalletBalanceAvailableInsufficient -> null // "normal case" + ExchangeMissingGlobalFees -> R.string.payment_balance_insufficient_hint_exchange_missing_global_fees + FeesNotCovered -> R.string.payment_balance_insufficient_hint_fees_not_covered } @Serializable diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt @@ -138,18 +138,8 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { payStatus.balanceDetails.balanceAvailable.toString(), ) ui.details.errorView.fadeIn() - when(payStatus.balanceDetails.causeHint) { - null -> null - Unknown -> null - MerchantAcceptInsufficient -> R.string.payment_balance_insufficient_hint_merchant_accept_insufficient - MerchantDepositInsufficient -> R.string.payment_balance_insufficient_hint_merchant_deposit_insufficient - AgeRestricted -> R.string.payment_balance_insufficient_hint_age_restricted - WalletBalanceMaterialInsufficient -> R.string.payment_balance_insufficient_hint_wallet_balance_material_insufficient - WalletBalanceAvailableInsufficient -> null // "normal case" - ExchangeMissingGlobalFees -> R.string.payment_balance_insufficient_hint_exchange_missing_global_fees - FeesNotCovered -> R.string.payment_balance_insufficient_hint_fees_not_covered - }?.let { hintRes -> - ui.details.errorHintView.setText(hintRes) + payStatus.balanceDetails.causeHint?.stringResId()?.let { + ui.details.errorHintView.setText(it) ui.details.errorHintView.fadeIn() } } diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt @@ -51,6 +51,7 @@ import net.taler.wallet.cleanExchange import net.taler.wallet.compose.AmountCurrencyField import net.taler.wallet.compose.TalerSurface import net.taler.wallet.exchanges.ExchangeTosStatus +import net.taler.wallet.payment.stringResId import net.taler.wallet.peer.CheckFeeResult.InsufficientBalance import net.taler.wallet.peer.CheckFeeResult.None import net.taler.wallet.peer.CheckFeeResult.Success @@ -154,8 +155,9 @@ fun OutgoingPushIntroComposable( ) } - is InsufficientBalance -> if (res.maxAmountEffective != null) { - Text(stringResource(R.string.payment_balance_insufficient_max, res.maxAmountEffective)) + is InsufficientBalance -> { + Text(stringResource(res.causeHint?.stringResId() + ?: R.string.payment_balance_insufficient)) } else -> {} diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt @@ -16,10 +16,13 @@ package net.taler.wallet.peer +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.taler.common.Amount +import net.taler.common.Timestamp import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.exchanges.ExchangeTosStatus +import net.taler.wallet.payment.PaymentInsufficientBalanceDetails sealed class OutgoingState @@ -65,11 +68,22 @@ data class InitiatePeerPullPaymentResponse( ) @Serializable -data class CheckPeerPushDebitResponse( - val amountRaw: Amount, - val amountEffective: Amount, - val exchangeBaseUrl: String, -) +sealed class CheckPeerPushDebitResponse { + @Serializable + @SerialName("ok") + data class CheckPeerPushDebitOkResponse( + val amountRaw: Amount, + val amountEffective: Amount, + val exchangeBaseUrl: String, + val maxExpirationDate: Timestamp, + ) : CheckPeerPushDebitResponse() + + @Serializable + @SerialName("insufficient-balance") + data class CheckPeerPushDebitInsufficientBalanceResponse( + val insufficientBalanceDetails: PaymentInsufficientBalanceDetails, + ) : CheckPeerPushDebitResponse() +} @Serializable data class InitiatePeerPushDebitResponse( diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt @@ -27,13 +27,10 @@ import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive import net.taler.common.Amount import net.taler.common.Timestamp import net.taler.wallet.TAG import net.taler.wallet.backend.BackendManager -import net.taler.wallet.backend.TalerErrorCode.WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.balances.ScopeInfo @@ -41,8 +38,10 @@ import net.taler.wallet.cleanExchange import net.taler.wallet.exchanges.ExchangeItem import net.taler.wallet.exchanges.ExchangeManager import net.taler.wallet.exchanges.ExchangeTosStatus +import net.taler.wallet.payment.InsufficientBalanceHint import org.json.JSONObject import java.util.concurrent.TimeUnit.HOURS +import net.taler.wallet.peer.CheckPeerPushDebitResponse.* const val MAX_LENGTH_SUBJECT = 100 val DEFAULT_EXPIRY = ExpirationOption.DAYS_1 @@ -59,6 +58,7 @@ sealed class CheckFeeResult { data class InsufficientBalance( val maxAmountEffective: Amount?, val maxAmountRaw: Amount?, + val causeHint: InsufficientBalanceHint? = null, override val maxDepositAmountEffective: Amount? = null, override val maxDepositAmountRaw: Amount? = null, ): CheckFeeResult() @@ -153,37 +153,29 @@ class PeerManager( maxDepositAmountEffective = max?.effectiveAmount, maxDepositAmountRaw = max?.rawAmount, ) - api.request("checkPeerPushDebit", CheckPeerPushDebitResponse.serializer()) { + api.request("checkPeerPushDebitV2", CheckPeerPushDebitResponse.serializer()) { exchangeBaseUrl?.let { put("exchangeBaseUrl", it) } put("amount", amount.toJSONString()) - }.onSuccess { - response = CheckFeeResult.Success( - amountRaw = it.amountRaw, - amountEffective = it.amountEffective, - maxDepositAmountEffective = max?.effectiveAmount, - maxDepositAmountRaw = max?.rawAmount, - exchangeBaseUrl = it.exchangeBaseUrl, - ) - }.onError { error -> - Log.e(TAG, "got checkPeerPushDebit error result $error") - if (error.code == WALLET_PEER_PUSH_PAYMENT_INSUFFICIENT_BALANCE) { - error.extra["insufficientBalanceDetails"]?.let { details -> - val maxAmountRaw = details.jsonObject["balanceAvailable"]?.let { amount -> - Amount.fromJSONString(amount.jsonPrimitive.content) - } - - val maxAmountEffective = details.jsonObject["maxEffectiveSpendAmount"]?.let { amount -> - Amount.fromJSONString(amount.jsonPrimitive.content) - } ?: maxAmountRaw + }.onSuccess { res -> + response = when (val r = res) { + is CheckPeerPushDebitOkResponse -> CheckFeeResult.Success( + amountRaw = r.amountRaw, + amountEffective = r.amountEffective, + maxDepositAmountEffective = max?.effectiveAmount, + maxDepositAmountRaw = max?.rawAmount, + exchangeBaseUrl = r.exchangeBaseUrl, + ) - response = CheckFeeResult.InsufficientBalance( - maxAmountEffective = maxAmountEffective, - maxAmountRaw = maxAmountRaw, - maxDepositAmountEffective = max?.effectiveAmount, - maxDepositAmountRaw = max?.rawAmount, - ) - } + is CheckPeerPushDebitInsufficientBalanceResponse -> CheckFeeResult.InsufficientBalance( + maxAmountEffective = r.insufficientBalanceDetails.maxEffectiveSpendAmount, + maxAmountRaw = r.insufficientBalanceDetails.balanceAvailable, + maxDepositAmountEffective = max?.effectiveAmount, + maxDepositAmountRaw = max?.rawAmount, + causeHint = r.insufficientBalanceDetails.causeHint, + ) } + }.onError { error -> + Log.e(TAG, "got checkPeerPushDebit error result $error") } return response