feat: streaming ecash payments + media playback overhaul
Cashu ecash protocol (BDHKE blind signatures, cashuA token format, mint HTTP client) replacing the stub wallet. TollGate-inspired streaming data payment system with step-based pricing (bytes/time/requests), session management with incremental top-ups, usage metering, and Nostr kind 10021 service advertisements. 13 new streaming.* RPC endpoints. Content server now verifies real Cashu tokens. Profits tracking includes streaming revenue. Frontend: GlobalAudioPlayer (persistent bottom bar across all pages), video lightbox with full controls, audio in MediaLightbox, free file previews (no blur), paid 10% audio/video previews, separated play vs download buttons in PeerFiles. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
90506ee52c
commit
ffd57ad29d
@@ -9,7 +9,8 @@ impl RpcHandler {
|
||||
let wallet = ecash::load_wallet(&self.config.data_dir).await?;
|
||||
Ok(serde_json::json!({
|
||||
"balance_sats": wallet.balance(),
|
||||
"token_count": wallet.tokens.iter().filter(|t| !t.spent).count(),
|
||||
"proof_count": wallet.proofs.iter().filter(|p| !p.spent && !p.reserved).count(),
|
||||
"mint_url": wallet.mint_url,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -27,10 +28,36 @@ impl RpcHandler {
|
||||
return Err(anyhow::anyhow!("Amount must be between 1 and 1,000,000 sats"));
|
||||
}
|
||||
|
||||
let token = ecash::mint_tokens(&self.config.data_dir, amount_sats).await?;
|
||||
// Step 1: Get a mint quote (returns Lightning invoice)
|
||||
let quote = ecash::mint_quote(&self.config.data_dir, amount_sats).await?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"token_id": token.id,
|
||||
"amount_sats": token.amount_sats,
|
||||
"quote_id": quote.quote,
|
||||
"bolt11": quote.request,
|
||||
"state": quote.state,
|
||||
"amount_sats": amount_sats,
|
||||
"message": "Pay the Lightning invoice, then call wallet.ecash-mint-claim with the quote_id",
|
||||
}))
|
||||
}
|
||||
|
||||
/// Claim minted tokens after paying the Lightning invoice.
|
||||
pub(super) async fn handle_wallet_ecash_mint_claim(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let quote_id = params
|
||||
.get("quote_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing quote_id"))?;
|
||||
let amount_sats = params
|
||||
.get("amount_sats")
|
||||
.and_then(|v| v.as_u64())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing amount_sats"))?;
|
||||
|
||||
let minted = ecash::mint_tokens(&self.config.data_dir, quote_id, amount_sats).await?;
|
||||
Ok(serde_json::json!({
|
||||
"minted_sats": minted,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -39,14 +66,41 @@ impl RpcHandler {
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let token_id = params
|
||||
.get("token_id")
|
||||
let bolt11 = params
|
||||
.get("bolt11")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing token_id"))?;
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing bolt11 (Lightning invoice)"))?;
|
||||
|
||||
// Step 1: Get melt quote
|
||||
let quote = ecash::melt_quote(&self.config.data_dir, bolt11).await?;
|
||||
|
||||
let amount = ecash::melt_tokens(&self.config.data_dir, token_id).await?;
|
||||
Ok(serde_json::json!({
|
||||
"melted_sats": amount,
|
||||
"quote_id": quote.quote,
|
||||
"amount_sats": quote.amount,
|
||||
"fee_reserve_sats": quote.fee_reserve,
|
||||
"total_needed_sats": quote.amount + quote.fee_reserve,
|
||||
"message": "Call wallet.ecash-melt-confirm with quote_id and bolt11 to execute",
|
||||
}))
|
||||
}
|
||||
|
||||
/// Confirm and execute a melt (pay Lightning invoice with ecash).
|
||||
pub(super) async fn handle_wallet_ecash_melt_confirm(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let quote_id = params
|
||||
.get("quote_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing quote_id"))?;
|
||||
let bolt11 = params
|
||||
.get("bolt11")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing bolt11"))?;
|
||||
|
||||
let melted = ecash::melt_tokens(&self.config.data_dir, quote_id, bolt11).await?;
|
||||
Ok(serde_json::json!({
|
||||
"melted_sats": melted,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -100,6 +154,7 @@ impl RpcHandler {
|
||||
"total_sats": summary.total_sats,
|
||||
"content_sales_sats": summary.content_sales_sats,
|
||||
"routing_fees_sats": summary.routing_fees_sats,
|
||||
"streaming_revenue_sats": summary.streaming_revenue_sats,
|
||||
"recent": summary.recent,
|
||||
}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user