Scanning a taler://pay/ URI opens the payment sheet, and the single button at the bottom of that sheet changes what it does twice while the user is looking at it, without moving: 1. "Done" — while talerTX is still the dummy transaction 2. "Cancel" — once it is a pay dialog but the amount is not known 3. "Pay now" — once wallet-core has returned the amount All three are drawn in the same rectangle by the same .safeAreaInset, so the user's thumb is already on its way to one of them when it becomes another. Measured by UI automation (iPhone 17, iOS 27.2, GNU_Taler debug build): a control at [32, 792, 338, 47] changed from "Cancel" to "Pay KUDOS 0.10 now" in 118 ms, in the same place and at the same size, while the sheet's title changed from "Payment" to "Confirm Payment". 118 ms is well under the time between deciding to press something and pressing it. Both directions cost the user something real: * aiming for "Done" — meaning "do not pay" — and pressing "Pay": money leaves the wallet on a decision the user had made the other way; * aiming for "Pay", because that is where a Pay button normally is, and pressing "Done": the payment is not made, the order is filed away among the scanned QR codes, and finding it again is not obvious. The duration of each state is one network round trip, so this is worst on a connection fast enough that the swap happens after the user has aimed, and on a slow one the misleading state is simply shown for longer. Cause ----- TalerWallet1/Views/Transactions/TransactionSummaryList.swift, the .safeAreaInset(edge: .bottom). One slot, whose content is chosen by `isPayDialog` and then by whether `effective` and `contractTerms` have arrived; `PaymentScan` passes showDone: .prominent, so before wallet-core answers the slot holds "Done". Suggested fix (patch attached, compiles against current master) --------------------------------------------------------------- Give the payment sheet two slots that never trade places: the commit action on top, the dismiss action below it. The commit slot exists from the first frame and is merely disabled until the amount is known, so nothing changes meaning under the user's thumb. `expectPayDialog` is how a caller says a sheet is going to become a payment dialog before wallet-core has confirmed it, which is exactly the window in which the slot used to say "Done". A sheet waiting on that answer stays swipe-dismissable so that a disabled commit button is never a trap. The patch adds one string, "Pay…", which needs translating. A smaller mitigation, if the two-slot layout is unwanted: ignore taps on the bottom button for a few hundred milliseconds after its action changes. That treats the symptom rather than the cause, but it does prevent the mis-tap.