diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkDebitPayer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkDebitPayer.kt index 4282332b53..85f48dc31e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkDebitPayer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkDebitPayer.kt @@ -30,6 +30,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.withTimeoutOrNull @@ -115,7 +116,15 @@ object ClinkDebitPayer { return try { account.client.publish(request, relays) val response = withTimeoutOrNull(timeoutMs) { reply.await() } ?: return null - client.parseResponse(response) + // Treat an undecryptable/malformed reply as no usable response rather than + // throwing — callers only handle null, and an uncaught decode error would + // leave the calling UI hung (spinner stuck, no toast, sibling zaps cancelled). + try { + client.parseResponse(response) + } catch (e: Exception) { + if (e is CancellationException) throw e + null + } } finally { account.client.unsubscribe(subId) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkOfferPayer.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkOfferPayer.kt index 39e6603650..9d6eba4125 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkOfferPayer.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/ClinkOfferPayer.kt @@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.SubscriptionListener import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.withTimeoutOrNull @@ -82,7 +83,16 @@ object ClinkOfferPayer { return try { account.client.publish(request, relays) val response = withTimeoutOrNull(timeoutMs) { reply.await() } ?: return null - client.parseResponse(response) + // A reply that can't be decrypted/parsed (corrupt ciphertext, malformed JSON + // from a buggy or hostile relay) is treated as no usable response rather than + // thrown — callers only handle null, and an uncaught decode error would hang + // the UI (the Pay button stuck on "Requesting…"). + try { + client.parseResponse(response) + } catch (e: Exception) { + if (e is CancellationException) throw e + null + } } finally { account.client.unsubscribe(subId) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/invoice/ClinkOfferPreview.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/invoice/ClinkOfferPreview.kt index 5fc7aa994a..0af1691a46 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/invoice/ClinkOfferPreview.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/creators/invoice/ClinkOfferPreview.kt @@ -145,10 +145,12 @@ fun ClinkOfferPreview( // FIXED offers display their preset price; SPONTANEOUS offers (and the default // when the pointer omits a price type) require the payer to enter an amount. - val effectiveType = offer.priceType ?: OfferPriceType.SPONTANEOUS + // Reflect the pointer actually being charged (which may have changed if the + // service redirected us to a replacement noffer via "Expired or Moved"). + val effectiveType = activeOffer.priceType ?: OfferPriceType.SPONTANEOUS if (effectiveType == OfferPriceType.FIXED) { - offer.price?.let { + activeOffer.price?.let { Text( text = "$it ${stringRes(id = R.string.sats)}", fontSize = 25.sp, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt index b6d3ae1600..8d05d74d01 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt @@ -287,8 +287,11 @@ class WalletViewModel : ViewModel() { fun setDefaultWallet(walletId: String) { val acc = account ?: return - acc.settings.setDefaultPaymentSource(walletId) - _defaultWalletId.value = walletId + // Only reflect the change locally if it actually persisted (the id must exist + // in one of the lists); otherwise the star and the stored default would diverge. + if (acc.settings.setDefaultPaymentSource(walletId)) { + _defaultWalletId.value = walletId + } } fun removeWallet(walletId: String) {