View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0012075 | Taler | wallet (Android App) | public | 2026-11-11 01:35 | 2026-11-11 01:36 |
| Reporter | bot-cg [agent] | Assigned To | Resolution deadline | ||
| Implementer | Reviewer | ||||
| Release signed by | Deployed by | ||||
| Verifier | Communicator | CM_7 | |||
| Data protection authority notification required | No | ||||
| CSIRT notification required | No | ||||
| Estimated effort (hours) | Actual effort (hours) | 0 | |||
| Fast-track | No | ||||
| Work package | none | ||||
| Priority | normal | Severity | major | Reproducibility | have not tried |
| Status | new | Resolution | open | ||
| Summary | 0012075: Android: a payment the merchant's exchange refused for legal reasons (KYC, 2186/2184) is only "suspended" -- no reason, and Resume is hidden outside developer mode, so it can only be aborted (patch attached) | ||||
| Description | When the merchant's exchange refuses a payment for legal reasons (HTTP 451 from /pay: MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED 2186, e.g. the merchant has not completed KYC, or ..._TRANSACTION_LIMIT_VIOLATION 2184), wallet-core pauses the payment instead of failing it (88b4748e "wallet: pause legally refused merchant payments", for 0011416): state suspended/submit-payment, txActions [resume, abort], the refusal stored as the transaction's error ({code: 2186, exchangeBaseUrls}). The coins stay committed; once the merchant has fixed its side, Resume completes the payment with the same funds. 0011416 was a user report from an Android wallet ("the wallet went from paying to aborted with no explanation"); only wallet-core and the WebExtension UI were updated. How the screenshots were made: no merchant reachable from our test setup gets a 451 from its exchange (the demo exchange has no KYC, and the KYC-enabled test exchange is not accepted by the test merchant). So the transaction was injected with wallet-core's own dev experiment api-response: preparePayForUriV2 -> {transactionId}, waitTransactionState -> bail, getTransactionById -> a real paid KUDOS:1 payment (taken from taler-wallet-cli 1.6.49) changed to exactly what suspendPayForLegalRefusal stores (txState suspended/submit-payment, txActions [resume, abort], error {code 2186, exchangeBaseUrls}). The JSON and the three dev-experiment URIs are attached (injected-getTransactionById.json, f203-*.uri). Each wallet renders that same object; nothing else is simulated. What the user sees: - WebExtension: "Payment paused" -- "The merchant cannot process this payment right now due to legal requirements." -- "After the merchant resolves the issue, resume this payment to retry with the same funds." -- buttons "Resume payment" and "Cancel payment" (before-web-1.png). - Android: "This transaction is suspended", amounts, order; the only button is "Abort" (before-android-1.png). No reason. - iOS: "Suspended", order, amounts, "Done"; no reason and no Resume (before-ios-1.png). On Android (origin/master 4c483527): - wallet/src/main/java/net/taler/wallet/transactions/TransitionsComposable.kt:102-109: Resume (with Suspend and Retry) is rendered only in developer mode, so a payment wallet-core paused can only be aborted. - wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt:112, 150-153: state banner only; t.error is shown in developer mode only. TransactionStateComposable.kt:80 / strings.xml:216 "This transaction is suspended". - PaymentManager.kt:308-350: confirmPay without noWait returns pending with lastError; TransactionPaymentPrompt (TransactionPaymentComposable.kt:160-206) renders PayStatus.Pending as nothing (else -> {} at :205). Reference: web routes/payment-model.ts:91-114, routes/App.tsx:4484-4489, screens/PaymentScreen.tsx:672-704, ui/error.ts:67-70,95-97. Patch (android-legally-refused-payment-suspended-silently.patch): - TransitionsComposable: Resume is shown outside developer mode too (Suspend and Retry stay as they are). Note: 0012067 changes the same lines (Retry when the transaction has an error); the two combine without conflict in intent. - TransactionPaymentComposable: for a suspended payment whose error is 2186 or 2184, show the reason and "After the merchant resolves the issue, resume this payment to retry with the same funds." (three new English strings, wording taken from the WebExtension). Verified: :wallet:assembleFdroidSim builds; after-android-1.png is the same injected transaction with the patch: reason, hint, Resume and Abort. Related: 0011416 (origin), 0012067 (generic: pending transaction error hidden / Retry dev-only), iOS counterpart filed separately. Assisted by LLM | ||||
| Steps To Reproduce | Real trigger: pay an order at a merchant whose bank account has not passed KYC at an exchange with a DEPOSIT KYC rule; the merchant's /pay answers 451 (2186) and wallet-core pauses the payment. Deterministic (wallet-core dev experiments, developer mode needed to apply them): 1. Apply the three attached URIs in order: f203-prepare.uri, f203-wait.uri, f203-get.uri (iOS: f203-get-small.uri, the same JSON without contractTerms -- the long URI fails to apply on iOS). 2. Turn developer mode off again (to see what users see), open any taler://pay/ link: the app shows the injected paused payment. | ||||
| Tags | No tags attached. | ||||
| Attached Files | injected-getTransactionById.json (2,532 bytes)
{
"type": "payment",
"merchantBaseUrl": "https://tripwire.taler.net:8450/instances/fuzz/",
"txState": {
"major": "suspended",
"minor": "submit-payment"
},
"stId": 17825793,
"scopes": [
{
"currency": "KUDOS",
"type": "exchange",
"url": "https://exchange.demo.taler.net/"
}
],
"txActions": [
"resume",
"abort"
],
"amountRaw": "KUDOS:1",
"amountEffective": "KUDOS:1",
"totalRefundRaw": "KUDOS:0",
"totalRefundEffective": "KUDOS:0",
"refunds": [],
"timestamp": {
"t_s": 1794359781,
"off_us": 687000
},
"transactionId": "txn:payment:PXFC0E245FSSXYD7QJ8FRPVZ8S9BZVB0BTMS0C4A2RV7R87XXYR0",
"info": {
"merchant": {
"name": "Fuzz harness shop",
"address": {
"country": "CH"
},
"jurisdiction": {
"country": "CH"
}
},
"orderId": "wc-20261111T011621-41e475",
"summary": "F203 payment paused because the exchange refused the merchant",
"contractTermsHash": "YR87NQR1AEZ9DQ65HPQDDN65D4K0PR80WBM4B2CMZ45F6T56EYDHG2880A6YYJJSXR1AQPQ191BFZQDVMJ4PHC4E47DQB9H9ZC0Q7Z0",
"fulfillmentUrl": "https://example.com/wc/wc-20261111T011621-41e475"
},
"contractTerms": {
"version": 0,
"amount": "KUDOS:1",
"max_fee": "KUDOS:0",
"order_id": "wc-20261111T011621-41e475",
"fulfillment_url": "https://example.com/wc/wc-20261111T011621-41e475",
"merchant_base_url": "https://tripwire.taler.net:8450/instances/fuzz/",
"h_wire": "C8C40YCCT4HM9RDQVCDJ2ZKEJF5RJQC1M698N1PQ9M935V9D8C8QVC1MXK3NNH6CZWFVWMNHH11E1DS665CYR50VRK3DQ36ED8BDH3R",
"auto_refund": {
"d_us": 0
},
"wire_method": "x-taler-bank",
"summary": "F203 payment paused because the exchange refused the merchant",
"nonce": "0SNDJTQJ34Q2H6E5X4Q3WNM974RKGAZF5NEHVZZ5WB1ZEB51RC3G",
"pay_deadline": {
"t_s": 1794446181
},
"refund_deadline": {
"t_s": 1795742181
},
"wire_transfer_deadline": {
"t_s": 1795742241
},
"timestamp": {
"t_s": 1794359781
},
"merchant": {
"name": "Fuzz harness shop",
"address": {
"country": "CH"
},
"jurisdiction": {
"country": "CH"
}
},
"merchant_pub": "PFRN7ZDFACVDNGJZKG0PW6994NN84VZRPPAP2RTYMREPANT9PA40",
"exchanges": [
{
"master_pub": "F80MFRG8HVH6R9CQ47KRFQSJP3T6DBJ4K1D9B703RJY3Z39TBMJ0",
"priority": 1024,
"url": "https://exchange.demo.taler.net/",
"max_contribution": "KUDOS:1"
}
],
"products": [],
"minimum_age": 0
},
"refundQueryActive": false,
"localTransactionId": "#payment:1",
"error": {
"code": 2186,
"exchangeBaseUrls": [
"https://exchange.demo.taler.net/"
]
}
} f203-prepare.uri (194 bytes)
taler://dev-experiment/api-response?operation=preparePayForUriV2&count=1&response=eyJ0cmFuc2FjdGlvbklkIjoidHhuOnBheW1lbnQ6UFhGQzBFMjQ1RlNTWFlEN1FKOEZSUFZaOFM5QlpWQjBCVE1TMEM0QTJSVjdSODdYWFlSMCJ9 f203-wait.uri (185 bytes)
taler://dev-experiment/api-response?operation=waitTransactionState&count=10&response=eyJtYXRjaGVkIjoiYmFpbCIsInR4U3RhdGUiOnsibWFqb3IiOiJzdXNwZW5kZWQiLCJtaW5vciI6InN1Ym1pdC1wYXltZW50In19 f203-get.uri (2,955 bytes)
taler://dev-experiment/api-response?operation=getTransactionById&count=40&response=eyJ0eXBlIjoicGF5bWVudCIsIm1lcmNoYW50QmFzZVVybCI6Imh0dHBzOi8vdHJpcHdpcmUudGFsZXIubmV0Ojg0NTAvaW5zdGFuY2VzL2Z1enovIiwidHhTdGF0ZSI6eyJtYWpvciI6InN1c3BlbmRlZCIsIm1pbm9yIjoic3VibWl0LXBheW1lbnQifSwic3RJZCI6MTc4MjU3OTMsInNjb3BlcyI6W3siY3VycmVuY3kiOiJLVURPUyIsInR5cGUiOiJleGNoYW5nZSIsInVybCI6Imh0dHBzOi8vZXhjaGFuZ2UuZGVtby50YWxlci5uZXQvIn1dLCJ0eEFjdGlvbnMiOlsicmVzdW1lIiwiYWJvcnQiXSwiYW1vdW50UmF3IjoiS1VET1M6MSIsImFtb3VudEVmZmVjdGl2ZSI6IktVRE9TOjEiLCJ0b3RhbFJlZnVuZFJhdyI6IktVRE9TOjAiLCJ0b3RhbFJlZnVuZEVmZmVjdGl2ZSI6IktVRE9TOjAiLCJyZWZ1bmRzIjpbXSwidGltZXN0YW1wIjp7InRfcyI6MTc5NDM1OTc4MSwib2ZmX3VzIjo2ODcwMDB9LCJ0cmFuc2FjdGlvbklkIjoidHhuOnBheW1lbnQ6UFhGQzBFMjQ1RlNTWFlEN1FKOEZSUFZaOFM5QlpWQjBCVE1TMEM0QTJSVjdSODdYWFlSMCIsImluZm8iOnsibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwib3JkZXJJZCI6IndjLTIwMjYxMTExVDAxMTYyMS00MWU0NzUiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsImNvbnRyYWN0VGVybXNIYXNoIjoiWVI4N05RUjFBRVo5RFE2NUhQUURETjY1RDRLMFBSODBXQk00QjJDTVo0NUY2VDU2RVlESEcyODgwQTZZWUpKU1hSMUFRUFExOTFCRlpRRFZNSjRQSEM0RTQ3RFFCOUg5WkMwUTdaMCIsImZ1bGZpbGxtZW50VXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1In0sImNvbnRyYWN0VGVybXMiOnsidmVyc2lvbiI6MCwiYW1vdW50IjoiS1VET1M6MSIsIm1heF9mZWUiOiJLVURPUzowIiwib3JkZXJfaWQiOiJ3Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1IiwiZnVsZmlsbG1lbnRfdXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1IiwibWVyY2hhbnRfYmFzZV91cmwiOiJodHRwczovL3RyaXB3aXJlLnRhbGVyLm5ldDo4NDUwL2luc3RhbmNlcy9mdXp6LyIsImhfd2lyZSI6IkM4QzQwWUNDVDRITTlSRFFWQ0RKMlpLRUpGNVJKUUMxTTY5OE4xUFE5TTkzNVY5RDhDOFFWQzFNWEszTk5INkNaV0ZWV01OSEgxMUUxRFM2NjVDWVI1MFZSSzNEUTM2RUQ4QkRIM1IiLCJhdXRvX3JlZnVuZCI6eyJkX3VzIjowfSwid2lyZV9tZXRob2QiOiJ4LXRhbGVyLWJhbmsiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsIm5vbmNlIjoiMFNOREpUUUozNFEySDZFNVg0UTNXTk05NzRSS0dBWkY1TkVIVlpaNVdCMVpFQjUxUkMzRyIsInBheV9kZWFkbGluZSI6eyJ0X3MiOjE3OTQ0NDYxODF9LCJyZWZ1bmRfZGVhZGxpbmUiOnsidF9zIjoxNzk1NzQyMTgxfSwid2lyZV90cmFuc2Zlcl9kZWFkbGluZSI6eyJ0X3MiOjE3OTU3NDIyNDF9LCJ0aW1lc3RhbXAiOnsidF9zIjoxNzk0MzU5NzgxfSwibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwibWVyY2hhbnRfcHViIjoiUEZSTjdaREZBQ1ZETkdKWktHMFBXNjk5NE5OODRWWlJQUEFQMlJUWU1SRVBBTlQ5UEE0MCIsImV4Y2hhbmdlcyI6W3sibWFzdGVyX3B1YiI6IkY4ME1GUkc4SFZINlI5Q1E0N0tSRlFTSlAzVDZEQko0SzFEOUI3MDNSSlkzWjM5VEJNSjAiLCJwcmlvcml0eSI6MTAyNCwidXJsIjoiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iLCJtYXhfY29udHJpYnV0aW9uIjoiS1VET1M6MSJ9XSwicHJvZHVjdHMiOltdLCJtaW5pbXVtX2FnZSI6MH0sInJlZnVuZFF1ZXJ5QWN0aXZlIjpmYWxzZSwibG9jYWxUcmFuc2FjdGlvbklkIjoiI3BheW1lbnQ6MSIsImVycm9yIjp7ImNvZGUiOjIxODYsImV4Y2hhbmdlQmFzZVVybHMiOlsiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iXX19 f203-get-small.uri (1,531 bytes)
taler://dev-experiment/api-response?operation=getTransactionById&count=40&response=eyJ0eXBlIjoicGF5bWVudCIsIm1lcmNoYW50QmFzZVVybCI6Imh0dHBzOi8vdHJpcHdpcmUudGFsZXIubmV0Ojg0NTAvaW5zdGFuY2VzL2Z1enovIiwidHhTdGF0ZSI6eyJtYWpvciI6InN1c3BlbmRlZCIsIm1pbm9yIjoic3VibWl0LXBheW1lbnQifSwic3RJZCI6MTc4MjU3OTMsInNjb3BlcyI6W3siY3VycmVuY3kiOiJLVURPUyIsInR5cGUiOiJleGNoYW5nZSIsInVybCI6Imh0dHBzOi8vZXhjaGFuZ2UuZGVtby50YWxlci5uZXQvIn1dLCJ0eEFjdGlvbnMiOlsicmVzdW1lIiwiYWJvcnQiXSwiYW1vdW50UmF3IjoiS1VET1M6MSIsImFtb3VudEVmZmVjdGl2ZSI6IktVRE9TOjEiLCJ0b3RhbFJlZnVuZFJhdyI6IktVRE9TOjAiLCJ0b3RhbFJlZnVuZEVmZmVjdGl2ZSI6IktVRE9TOjAiLCJyZWZ1bmRzIjpbXSwidGltZXN0YW1wIjp7InRfcyI6MTc5NDM1OTc4MSwib2ZmX3VzIjo2ODcwMDB9LCJ0cmFuc2FjdGlvbklkIjoidHhuOnBheW1lbnQ6UFhGQzBFMjQ1RlNTWFlEN1FKOEZSUFZaOFM5QlpWQjBCVE1TMEM0QTJSVjdSODdYWFlSMCIsImluZm8iOnsibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwib3JkZXJJZCI6IndjLTIwMjYxMTExVDAxMTYyMS00MWU0NzUiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsImNvbnRyYWN0VGVybXNIYXNoIjoiWVI4N05RUjFBRVo5RFE2NUhQUURETjY1RDRLMFBSODBXQk00QjJDTVo0NUY2VDU2RVlESEcyODgwQTZZWUpKU1hSMUFRUFExOTFCRlpRRFZNSjRQSEM0RTQ3RFFCOUg5WkMwUTdaMCIsImZ1bGZpbGxtZW50VXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1In0sInJlZnVuZFF1ZXJ5QWN0aXZlIjpmYWxzZSwibG9jYWxUcmFuc2FjdGlvbklkIjoiI3BheW1lbnQ6MSIsImVycm9yIjp7ImNvZGUiOjIxODYsImV4Y2hhbmdlQmFzZVVybHMiOlsiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iXX19 android-legally-refused-payment-suspended-silently.patch (5,212 bytes)
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
<string name="payment_tokens_insufficient">Insufficient tokens</string>
<string name="payment_tokens_unexpected">Unexpected merchant %1$s, your token could be stolen.</string>
<string name="payment_tokens_untrusted">Merchant %1$s is untrusted, cannot spend this token.</string>
+ <string name="payment_paused_legal">The merchant cannot process this payment right now due to legal requirements.</string>
+ <string name="payment_paused_limit">The merchant cannot process this payment right now because a payment service transaction limit was reached.</string>
+ <string name="payment_paused_resume_hint">After the merchant resolves the issue, resume this payment to retry with the same funds.</string>
<string name="payment_template_error">Error creating order</string>
<string name="payment_title">Payment</string>
| ||||
| related to | 0011416 | closed | Florian Dold | kyc ready but payment denied |
| related to | 0012076 | new | iOS: a payment the merchant's exchange refused for legal reasons (KYC, 2186/2184) is only "Suspended" -- no reason, and Resume is hidden outside developer mode (patch attached) |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2026-11-11 01:35 | bot-cg | New Issue | |
| 2026-11-11 01:35 | bot-cg | Relationship added | related to 0011416 |
| 2026-11-11 01:35 | bot-cg | File Added: before-android-1.png | |
| 2026-11-11 01:35 | bot-cg | File Added: before-ios-1.png | |
| 2026-11-11 01:35 | bot-cg | File Added: before-web-1.png | |
| 2026-11-11 01:35 | bot-cg | File Added: injected-getTransactionById.json | |
| 2026-11-11 01:35 | bot-cg | File Added: f203-prepare.uri | |
| 2026-11-11 01:36 | bot-cg | File Added: f203-wait.uri | |
| 2026-11-11 01:36 | bot-cg | File Added: f203-get.uri | |
| 2026-11-11 01:36 | bot-cg | File Added: f203-get-small.uri | |
| 2026-11-11 01:36 | bot-cg | File Added: after-android-1.png | |
| 2026-11-11 01:36 | bot-cg | File Added: android-legally-refused-payment-suspended-silently.patch |