View Issue Details

IDProjectCategoryView StatusLast Update
0012076Talerwallet (iOS App)public2026-11-11 01:36
Reporterbot-cg [agent] Assigned ToResolution deadline
ImplementerReviewer 
Release signed byDeployed by 
VerifierCommunicatorCM_7  
Data protection authority notification requiredNo  
CSIRT notification requiredNo  
Estimated effort (hours)Actual effort (hours)0 
Fast-trackNo
Work packagenone
PrioritynormalSeveritymajorReproducibilityhave not tried
Status newResolutionopen 
Summary0012076: 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)
DescriptionWhen 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 iOS (origin/master 674b7915):
- TalerWallet1/Views/Transactions/TransactionSummaryList.swift:320: suspendResume() only if developerMode && withActions; from the history only Abort is offered (abortFailDelete, :347).
- TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift:96-114: shows failReason and abortReason only; common.error (Model/Transaction.swift:367) is never rendered for payments.
- TalerWallet1/Model/Transaction.swift:114: the state text "Suspended" is an unlocalized literal.
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 (ios-legally-refused-payment-suspended-silently.patch):
- TransactionSummaryList: outside developer mode, a resumable transaction gets a Resume button (suspendResume(common, resumeOnly: true)); developer mode keeps both buttons as before.
- PaidTransactionDetailV: for a suspended payment with error 2186/2184, show the reason and the resume hint (three new English entries in Localizable.xcstrings, wording taken from the WebExtension).
Verified: GNU_Taler Release builds for the simulator; after-ios-1.png is the same injected transaction with the patch: "Resume" button, reason and hint.

Related: 0011416 (origin), 0012068 (generic: pending transaction error hidden, no Retry), 0012075 (Android counterpart).

Assisted by LLM
Steps To ReproduceReal 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.
TagsNo tags attached.
Attached Files
before-android-1.png (90,344 bytes)   
before-android-1.png (90,344 bytes)   
before-ios-1.png (184,777 bytes)   
before-ios-1.png (184,777 bytes)   
before-web-1.png (83,121 bytes)   
before-web-1.png (83,121 bytes)   
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-prepare.uri (194 bytes)   
f203-wait.uri (185 bytes)   
taler://dev-experiment/api-response?operation=waitTransactionState&count=10&response=eyJtYXRjaGVkIjoiYmFpbCIsInR4U3RhdGUiOnsibWFqb3IiOiJzdXNwZW5kZWQiLCJtaW5vciI6InN1Ym1pdC1wYXltZW50In19
f203-wait.uri (185 bytes)   
f203-get.uri (2,955 bytes)   
taler://dev-experiment/api-response?operation=getTransactionById&count=40&response=eyJ0eXBlIjoicGF5bWVudCIsIm1lcmNoYW50QmFzZVVybCI6Imh0dHBzOi8vdHJpcHdpcmUudGFsZXIubmV0Ojg0NTAvaW5zdGFuY2VzL2Z1enovIiwidHhTdGF0ZSI6eyJtYWpvciI6InN1c3BlbmRlZCIsIm1pbm9yIjoic3VibWl0LXBheW1lbnQifSwic3RJZCI6MTc4MjU3OTMsInNjb3BlcyI6W3siY3VycmVuY3kiOiJLVURPUyIsInR5cGUiOiJleGNoYW5nZSIsInVybCI6Imh0dHBzOi8vZXhjaGFuZ2UuZGVtby50YWxlci5uZXQvIn1dLCJ0eEFjdGlvbnMiOlsicmVzdW1lIiwiYWJvcnQiXSwiYW1vdW50UmF3IjoiS1VET1M6MSIsImFtb3VudEVmZmVjdGl2ZSI6IktVRE9TOjEiLCJ0b3RhbFJlZnVuZFJhdyI6IktVRE9TOjAiLCJ0b3RhbFJlZnVuZEVmZmVjdGl2ZSI6IktVRE9TOjAiLCJyZWZ1bmRzIjpbXSwidGltZXN0YW1wIjp7InRfcyI6MTc5NDM1OTc4MSwib2ZmX3VzIjo2ODcwMDB9LCJ0cmFuc2FjdGlvbklkIjoidHhuOnBheW1lbnQ6UFhGQzBFMjQ1RlNTWFlEN1FKOEZSUFZaOFM5QlpWQjBCVE1TMEM0QTJSVjdSODdYWFlSMCIsImluZm8iOnsibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwib3JkZXJJZCI6IndjLTIwMjYxMTExVDAxMTYyMS00MWU0NzUiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsImNvbnRyYWN0VGVybXNIYXNoIjoiWVI4N05RUjFBRVo5RFE2NUhQUURETjY1RDRLMFBSODBXQk00QjJDTVo0NUY2VDU2RVlESEcyODgwQTZZWUpKU1hSMUFRUFExOTFCRlpRRFZNSjRQSEM0RTQ3RFFCOUg5WkMwUTdaMCIsImZ1bGZpbGxtZW50VXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1In0sImNvbnRyYWN0VGVybXMiOnsidmVyc2lvbiI6MCwiYW1vdW50IjoiS1VET1M6MSIsIm1heF9mZWUiOiJLVURPUzowIiwib3JkZXJfaWQiOiJ3Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1IiwiZnVsZmlsbG1lbnRfdXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1IiwibWVyY2hhbnRfYmFzZV91cmwiOiJodHRwczovL3RyaXB3aXJlLnRhbGVyLm5ldDo4NDUwL2luc3RhbmNlcy9mdXp6LyIsImhfd2lyZSI6IkM4QzQwWUNDVDRITTlSRFFWQ0RKMlpLRUpGNVJKUUMxTTY5OE4xUFE5TTkzNVY5RDhDOFFWQzFNWEszTk5INkNaV0ZWV01OSEgxMUUxRFM2NjVDWVI1MFZSSzNEUTM2RUQ4QkRIM1IiLCJhdXRvX3JlZnVuZCI6eyJkX3VzIjowfSwid2lyZV9tZXRob2QiOiJ4LXRhbGVyLWJhbmsiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsIm5vbmNlIjoiMFNOREpUUUozNFEySDZFNVg0UTNXTk05NzRSS0dBWkY1TkVIVlpaNVdCMVpFQjUxUkMzRyIsInBheV9kZWFkbGluZSI6eyJ0X3MiOjE3OTQ0NDYxODF9LCJyZWZ1bmRfZGVhZGxpbmUiOnsidF9zIjoxNzk1NzQyMTgxfSwid2lyZV90cmFuc2Zlcl9kZWFkbGluZSI6eyJ0X3MiOjE3OTU3NDIyNDF9LCJ0aW1lc3RhbXAiOnsidF9zIjoxNzk0MzU5NzgxfSwibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwibWVyY2hhbnRfcHViIjoiUEZSTjdaREZBQ1ZETkdKWktHMFBXNjk5NE5OODRWWlJQUEFQMlJUWU1SRVBBTlQ5UEE0MCIsImV4Y2hhbmdlcyI6W3sibWFzdGVyX3B1YiI6IkY4ME1GUkc4SFZINlI5Q1E0N0tSRlFTSlAzVDZEQko0SzFEOUI3MDNSSlkzWjM5VEJNSjAiLCJwcmlvcml0eSI6MTAyNCwidXJsIjoiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iLCJtYXhfY29udHJpYnV0aW9uIjoiS1VET1M6MSJ9XSwicHJvZHVjdHMiOltdLCJtaW5pbXVtX2FnZSI6MH0sInJlZnVuZFF1ZXJ5QWN0aXZlIjpmYWxzZSwibG9jYWxUcmFuc2FjdGlvbklkIjoiI3BheW1lbnQ6MSIsImVycm9yIjp7ImNvZGUiOjIxODYsImV4Y2hhbmdlQmFzZVVybHMiOlsiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iXX19
f203-get.uri (2,955 bytes)   
f203-get-small.uri (1,531 bytes)   
taler://dev-experiment/api-response?operation=getTransactionById&count=40&response=eyJ0eXBlIjoicGF5bWVudCIsIm1lcmNoYW50QmFzZVVybCI6Imh0dHBzOi8vdHJpcHdpcmUudGFsZXIubmV0Ojg0NTAvaW5zdGFuY2VzL2Z1enovIiwidHhTdGF0ZSI6eyJtYWpvciI6InN1c3BlbmRlZCIsIm1pbm9yIjoic3VibWl0LXBheW1lbnQifSwic3RJZCI6MTc4MjU3OTMsInNjb3BlcyI6W3siY3VycmVuY3kiOiJLVURPUyIsInR5cGUiOiJleGNoYW5nZSIsInVybCI6Imh0dHBzOi8vZXhjaGFuZ2UuZGVtby50YWxlci5uZXQvIn1dLCJ0eEFjdGlvbnMiOlsicmVzdW1lIiwiYWJvcnQiXSwiYW1vdW50UmF3IjoiS1VET1M6MSIsImFtb3VudEVmZmVjdGl2ZSI6IktVRE9TOjEiLCJ0b3RhbFJlZnVuZFJhdyI6IktVRE9TOjAiLCJ0b3RhbFJlZnVuZEVmZmVjdGl2ZSI6IktVRE9TOjAiLCJyZWZ1bmRzIjpbXSwidGltZXN0YW1wIjp7InRfcyI6MTc5NDM1OTc4MSwib2ZmX3VzIjo2ODcwMDB9LCJ0cmFuc2FjdGlvbklkIjoidHhuOnBheW1lbnQ6UFhGQzBFMjQ1RlNTWFlEN1FKOEZSUFZaOFM5QlpWQjBCVE1TMEM0QTJSVjdSODdYWFlSMCIsImluZm8iOnsibWVyY2hhbnQiOnsibmFtZSI6IkZ1enogaGFybmVzcyBzaG9wIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiQ0gifSwianVyaXNkaWN0aW9uIjp7ImNvdW50cnkiOiJDSCJ9fSwib3JkZXJJZCI6IndjLTIwMjYxMTExVDAxMTYyMS00MWU0NzUiLCJzdW1tYXJ5IjoiRjIwMyBwYXltZW50IHBhdXNlZCBiZWNhdXNlIHRoZSBleGNoYW5nZSByZWZ1c2VkIHRoZSBtZXJjaGFudCIsImNvbnRyYWN0VGVybXNIYXNoIjoiWVI4N05RUjFBRVo5RFE2NUhQUURETjY1RDRLMFBSODBXQk00QjJDTVo0NUY2VDU2RVlESEcyODgwQTZZWUpKU1hSMUFRUFExOTFCRlpRRFZNSjRQSEM0RTQ3RFFCOUg5WkMwUTdaMCIsImZ1bGZpbGxtZW50VXJsIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS93Yy93Yy0yMDI2MTExMVQwMTE2MjEtNDFlNDc1In0sInJlZnVuZFF1ZXJ5QWN0aXZlIjpmYWxzZSwibG9jYWxUcmFuc2FjdGlvbklkIjoiI3BheW1lbnQ6MSIsImVycm9yIjp7ImNvZGUiOjIxODYsImV4Y2hhbmdlQmFzZVVybHMiOlsiaHR0cHM6Ly9leGNoYW5nZS5kZW1vLnRhbGVyLm5ldC8iXX19
f203-get-small.uri (1,531 bytes)   
after-ios-1.png (228,734 bytes)   
after-ios-1.png (228,734 bytes)   
ios-legally-refused-payment-suspended-silently.patch (4,027 bytes)   
diff --git a/TalerWallet1/Resources/Localizable.xcstrings b/TalerWallet1/Resources/Localizable.xcstrings
index e283ca45..b7d2fade 100755
--- a/TalerWallet1/Resources/Localizable.xcstrings
+++ b/TalerWallet1/Resources/Localizable.xcstrings
@@ -2842,6 +2842,9 @@
         }
       }
     },
+    "After the merchant resolves the issue, resume this payment to retry with the same funds." : {
+      "comment" : "below the reason why a payment was paused"
+    },
     "Alias Type:" : {
       "comment" : "Picker title",
       "localizations" : {
@@ -19138,6 +19141,12 @@
         }
       }
     },
+    "The merchant cannot process this payment right now because a payment service transaction limit was reached." : {
+      "comment" : "payment paused by wallet-core: transaction limit at the merchant's exchange"
+    },
+    "The merchant cannot process this payment right now due to legal requirements." : {
+      "comment" : "payment paused by wallet-core: the merchant's exchange refused the deposit (e.g. missing KYC)"
+    },
     "The payment service is waiting for your wire-transfer." : {
       "localizations" : {
         "de" : {
diff --git a/TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift b/TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift
index 6a6b2152..11951d2f 100755
--- a/TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift
+++ b/TalerWallet1/Views/Transactions/PaidTransactionDetailV.swift
@@ -93,6 +93,15 @@ struct PaidTransactionDetailV: View {
                         .talerFont(.body)
                 }
             }
+            if common.isSuspended, let code = common.error?.code,
+               code == 2186 || code == 2184 {   // MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LEGALLY_REFUSED / _TRANSACTION_LIMIT_VIOLATION
+                Text(code == 2186
+                     ? String(localized: "The merchant cannot process this payment right now due to legal requirements.")
+                     : String(localized: "The merchant cannot process this payment right now because a payment service transaction limit was reached."))
+                    .talerFont(.title3)
+                Text("After the merchant resolves the issue, resume this payment to retry with the same funds.")
+                    .talerFont(.body)
+            }
             if let failReason = common.failReason {
                 if let hint = failReason.hint {
                     Text(hint)
diff --git a/TalerWallet1/Views/Transactions/TransactionSummaryList.swift b/TalerWallet1/Views/Transactions/TransactionSummaryList.swift
index 74f04b65..218cb87a 100755
--- a/TalerWallet1/Views/Transactions/TransactionSummaryList.swift
+++ b/TalerWallet1/Views/Transactions/TransactionSummaryList.swift
@@ -228,8 +228,8 @@ struct TransactionSummaryList: View {
     }
 
     @ViewBuilder
-    func suspendResume(_ common: TransactionCommon) -> some View {
-        if talerTX.isSuspendable {
+    func suspendResume(_ common: TransactionCommon, resumeOnly: Bool = false) -> some View {
+        if talerTX.isSuspendable && !resumeOnly {
             TransactionButton(transactionId: common.transactionId,
                                     command: .suspend,
                                     warning: nil,
@@ -317,7 +317,13 @@ struct TransactionSummaryList: View {
         Group {
           if common.type != .dummy && transactionId == common.transactionId {
             let list = List {
-                if developerMode && withActions { suspendResume(common) }
+                if developerMode && withActions {
+                    suspendResume(common)
+                } else if talerTX.isResumable {
+                    // wallet-core suspends some transactions by itself (e.g. a payment the
+                    // merchant's exchange refused for legal reasons); Resume is the only way on
+                    suspendResume(common, resumeOnly: true)
+                }
                 if !isPayDialog {
                     Section {
                         HStack {

Required notifications

Address or linkRemarkAdded byNotifiedActions

Relationships

related to 0012075 new 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) 

Activities

Issue History

Date ModifiedUsernameFieldChange
2026-11-11 01:35bot-cgNew Issue
2026-11-11 01:35bot-cgRelationship addedrelated to 0012075
2026-11-11 01:36bot-cgFile Added: before-android-1.png
2026-11-11 01:36bot-cgFile Added: before-ios-1.png
2026-11-11 01:36bot-cgFile Added: before-web-1.png
2026-11-11 01:36bot-cgFile Added: injected-getTransactionById.json
2026-11-11 01:36bot-cgFile Added: f203-prepare.uri
2026-11-11 01:36bot-cgFile Added: f203-wait.uri
2026-11-11 01:36bot-cgFile Added: f203-get.uri
2026-11-11 01:36bot-cgFile Added: f203-get-small.uri
2026-11-11 01:36bot-cgFile Added: after-ios-1.png
2026-11-11 01:36bot-cgFile Added: ios-legally-refused-payment-suspended-silently.patch