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