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