diff --git a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt index 984a97f8..8e9fb763 100644 --- a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt +++ b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -47,6 +48,8 @@ import net.taler.lib.android.toAbsoluteTime import net.taler.wallet.BottomInsetsSpacer import net.taler.wallet.R import net.taler.wallet.backend.TalerErrorCode +import net.taler.wallet.backend.TalerErrorCode.WALLET_MERCHANT_ORDER_NOT_FOUND +import net.taler.wallet.backend.TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED import net.taler.wallet.backend.TalerErrorInfo import net.taler.wallet.balances.ScopeInfo import net.taler.wallet.compose.ErrorComposable @@ -111,33 +114,41 @@ fun TransactionPaymentComposable( ) { TransactionStateComposable(state = t.txState) + // A claim that failed permanently (e.g. the order was already claimed + // by another wallet) has no contract, hence no amounts, but a reason. + if (t.info == null && t.failReason != null) { + ClaimFailureReason(t.failReason) + } + Text( modifier = Modifier.padding(16.dp), text = t.timestamp.ms.toAbsoluteTime(LocalContext.current).toString(), style = MaterialTheme.typography.bodyLarge, ) - TransactionAmountComposable( - label = stringResource(id = R.string.transaction_order_total), - amount = t.amountRaw, - amountType = AmountType.Neutral, - ) + if (t.info != null) { + TransactionAmountComposable( + label = stringResource(id = R.string.transaction_order_total), + amount = t.amountRaw, + amountType = AmountType.Neutral, + ) + + if (t.amountEffective > t.amountRaw) { + val fee = t.amountEffective - t.amountRaw + TransactionAmountComposable( + label = stringResource(id = R.string.amount_fee), + amount = fee, + amountType = AmountType.Negative, + ) + } - if (t.amountEffective > t.amountRaw) { - val fee = t.amountEffective - t.amountRaw TransactionAmountComposable( - label = stringResource(id = R.string.amount_fee), - amount = fee, + label = stringResource(id = R.string.transaction_paid), + amount = t.amountEffective, amountType = AmountType.Negative, ) } - TransactionAmountComposable( - label = stringResource(id = R.string.transaction_paid), - amount = t.amountEffective, - amountType = AmountType.Negative, - ) - if (t.posConfirmation != null) PayTotpComposable( totpString = t.posConfirmation, enableNfc = t.posConfirmationViaNfc == true, @@ -156,6 +167,34 @@ fun TransactionPaymentComposable( } } +@Composable +private fun ClaimFailureReason(failReason: TalerErrorInfo) { + val (message, hint) = when (failReason.code) { + WALLET_ORDER_ALREADY_CLAIMED -> stringResource(R.string.payment_claimed_by_other_wallet) to + stringResource(R.string.payment_claimed_by_other_wallet_hint) + WALLET_MERCHANT_ORDER_NOT_FOUND -> stringResource(R.string.payment_order_not_found) to + stringResource(R.string.payment_order_not_found_hint) + else -> failReason.userFacingMsg to null + } + Column( + modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp), + horizontalAlignment = CenterHorizontally, + ) { + Text( + text = message, + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.error, + textAlign = TextAlign.Center, + ) + if (hint != null) Text( + modifier = Modifier.padding(top = 8.dp), + text = hint, + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + ) + } +} + @Composable fun TransactionPaymentPrompt( payStatus: PayStatus, diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt index d5d397c5..73d1134b 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt @@ -448,6 +448,7 @@ data class TransactionPayment( override val txActions: List, val info: TransactionInfo? = null, override val error: TalerErrorInfo? = null, + val failReason: TalerErrorInfo? = null, override val amountRaw: Amount, override val amountEffective: Amount, override val scopes: List, diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index 89750709..93cbf349 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -319,6 +319,10 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish Insufficient tokens Unexpected merchant %1$s, your token could be stolen. Merchant %1$s is untrusted, cannot spend this token. + This order was already claimed by another wallet. + Return to the merchant and request a new order. + The merchant order no longer exists or has expired. + Return to the merchant and create a new order. Error creating order Payment