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..a1eb5d3d 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.MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED
+import net.taler.wallet.backend.TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.compose.ErrorComposable
@@ -111,6 +114,8 @@ fun TransactionPaymentComposable(
) {
TransactionStateComposable(state = t.txState)
+ PaymentPausedReason(t)
+
Text(
modifier = Modifier.padding(16.dp),
text = t.timestamp.ms.toAbsoluteTime(LocalContext.current).toString(),
@@ -156,6 +161,38 @@ fun TransactionPaymentComposable(
}
}
+/**
+ * Explains why wallet-core paused a payment: the merchant's exchange refused
+ * the deposit for legal reasons (e.g. missing KYC of the merchant) or because
+ * of a transaction limit. The payment can be resumed once that is resolved.
+ */
+@Composable
+private fun PaymentPausedReason(t: TransactionPayment) {
+ if (t.txState.major != TransactionMajorState.Suspended) return
+ val reason = when (t.error?.code) {
+ MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED -> R.string.payment_paused_legal
+ MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_TRANSACTION_LIMIT_VIOLATION -> R.string.payment_paused_limit
+ else -> return
+ }
+ Column(
+ modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
+ horizontalAlignment = CenterHorizontally,
+ ) {
+ Text(
+ text = stringResource(reason),
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.error,
+ textAlign = TextAlign.Center,
+ )
+ Text(
+ modifier = Modifier.padding(top = 8.dp),
+ text = stringResource(R.string.payment_paused_resume_hint),
+ style = MaterialTheme.typography.bodyMedium,
+ textAlign = TextAlign.Center,
+ )
+ }
+}
+
@Composable
fun TransactionPaymentPrompt(
payStatus: PayStatus,
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt
index f654604f..d80c07e8 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt
@@ -101,7 +101,11 @@ fun TransitionsComposable(
FlowRow(horizontalArrangement = Center) {
t.txActions.forEach {
- if (it in arrayOf(Resume, Suspend, Retry)) {
+ // Resume stays visible outside developer mode: wallet-core itself
+ // suspends transactions (e.g. a payment the merchant's exchange
+ // refused for legal reasons), and resuming is then the only way
+ // to complete them.
+ if (it in arrayOf(Suspend, Retry)) {
if (devMode) TransitionComposable(it, onTransition)
} else {
TransitionComposable(it, onTransition)
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 89750709..bd2c7fd7 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -319,6 +319,9 @@ 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.
+ The merchant cannot process this payment right now due to legal requirements.
+ The merchant cannot process this payment right now because a payment service transaction limit was reached.
+ After the merchant resolves the issue, resume this payment to retry with the same funds.
Error creating order
Payment