From f4a1c474297796d5c49d459633ac63f7cf33b709 Mon Sep 17 00:00:00 2001
From: archipelago
Date: Mon, 17 Aug 2026 07:08:15 -0400
Subject: [PATCH] feat(wallet): ecash gets the real payment success screen,
with copyable proof
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Redeeming ecash reported success as one line of small green text, while an
on-chain or Lightning payment got the full moment — amount, verb, and the
identifiers you can copy. That asymmetry matters most for ecash: it leaves
no public ledger entry, so if the payment is ever questioned there is
nothing to look up afterwards. Whatever isn't copyable at that instant is
simply gone.
The success pane is extracted from SendBitcoinModal into a shared
PaymentSuccessPane so Cashu and Fedimint show the *same* screen rather
than a lookalike, and the copyable-row treatment is defined once. Each
caller passes the identifiers its protocol actually has; ecash receive now
shows the issuing mint (newly returned by wallet.ecash-receive) and the
redeemed token itself, clamped so a long token doesn't flood the pane.
Also: the test-ecash switch is a proper toggle (role="switch", keyboard
focusable) rather than a checkbox — it selects which purse the wallet is
looking at, so it should read as a mode you are in.
SendBitcoinModal still carries its own copy of the markup; consolidating it
onto the shared component is a follow-up, deliberately not done in the same
change as the money-path wiring.
Co-Authored-By: Claude Fable 5
---
core/archipelago/src/api/rpc/wallet.rs | 7 +
.../src/components/PaymentSuccessPane.vue | 143 ++++++++++++++++++
.../src/components/WalletSettingsModal.vue | 26 +++-
.../src/views/web5/Web5SendReceiveModals.vue | 42 ++++-
4 files changed, 209 insertions(+), 9 deletions(-)
create mode 100644 neode-ui/src/components/PaymentSuccessPane.vue
diff --git a/core/archipelago/src/api/rpc/wallet.rs b/core/archipelago/src/api/rpc/wallet.rs
index a994081e..16b813fc 100644
--- a/core/archipelago/src/api/rpc/wallet.rs
+++ b/core/archipelago/src/api/rpc/wallet.rs
@@ -206,10 +206,17 @@ impl RpcHandler {
// (redeemed at the mint) or Fedimint notes (reissued via the fmcd
// sidecar). Detect by prefix and route accordingly.
if is_cashu_token(token) {
+ // Which mint issued it, for the success screen. Ecash leaves no
+ // public trace once redeemed, so the mint URL is the only thing a
+ // person can quote later if the payment is ever disputed.
+ let mint_url = crate::wallet::cashu::CashuToken::deserialize(token)
+ .ok()
+ .and_then(|t| t.mint_urls().first().map(|m| m.to_string()));
let amount = ecash::receive_token(&self.config.data_dir, token).await?;
return Ok(serde_json::json!({
"received_sats": amount,
"kind": "cashu",
+ "mint_url": mint_url,
}));
}
diff --git a/neode-ui/src/components/PaymentSuccessPane.vue b/neode-ui/src/components/PaymentSuccessPane.vue
new file mode 100644
index 00000000..8dca76a9
--- /dev/null
+++ b/neode-ui/src/components/PaymentSuccessPane.vue
@@ -0,0 +1,143 @@
+
+