feat(content): seller-picked payment methods + music always in the bottom bar + video PiP
- Paid sharing: AccessControl::Paid gains an accepted-methods list (lightning/onchain/ecash/fedimint; empty = all, back-compat). Sellers pick methods in ShareModal, gated on what the node can actually receive (LND running/channel open, ecash wallet, fedimint joined) with an ⓘ that explains exactly how to enable a missing rail. Enforced server-side (the invoice/onchain mints refuse non-accepted methods; the serve gate only honors tokens/hashes for accepted rails) and the buyer's pay modal only offers what the seller accepts. - Purchased music: the two remaining lightbox paths now use the bottom-bar player — the immediate post-ecash-purchase viewer and the Paid Files tab's window.open. - Picture-in-picture buttons on the peer video player and the cloud media lightbox (utils/pip.ts; Chromium/Safari, no-op elsewhere). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d5fc3d01a4
commit
f72d4b92ac
@@ -51,9 +51,25 @@ pub enum AccessControl {
|
||||
PeersOnly,
|
||||
Paid {
|
||||
price_sats: u64,
|
||||
/// Payment methods the sharer accepts: "lightning", "onchain",
|
||||
/// "ecash", "fedimint". Empty = everything — which is also what
|
||||
/// catalogs written before this field deserialize to.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
accepted: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Does the sharer accept this payment method for the item? Empty list =
|
||||
/// all methods (pre-field catalogs and "no preference").
|
||||
pub fn method_accepted(access: &AccessControl, method: &str) -> bool {
|
||||
match access {
|
||||
AccessControl::Paid { accepted, .. } => {
|
||||
accepted.is_empty() || accepted.iter().any(|m| m == method)
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct ContentCatalog {
|
||||
pub items: Vec<ContentItem>,
|
||||
@@ -269,20 +285,26 @@ pub async fn serve_content(
|
||||
|
||||
// Check access control
|
||||
match &item.access {
|
||||
AccessControl::Paid { price_sats } => {
|
||||
AccessControl::Paid { price_sats, .. } => {
|
||||
// Two ways to satisfy payment:
|
||||
// (a) a valid ecash token (the local-wallet fast path), or
|
||||
// (b) a Lightning-invoice payment hash this node issued and has
|
||||
// since confirmed settled (the "pay from any wallet" path, #46).
|
||||
// Each path only counts when the sharer accepts that method.
|
||||
let mut authorized = false;
|
||||
if let Some(token) = payment_token {
|
||||
if verify_payment_token(data_dir, token, *price_sats).await {
|
||||
if (method_accepted(&item.access, "ecash")
|
||||
|| method_accepted(&item.access, "fedimint"))
|
||||
&& verify_payment_token(data_dir, token, *price_sats).await
|
||||
{
|
||||
authorized = true;
|
||||
}
|
||||
}
|
||||
if !authorized {
|
||||
if let Some(hash) = invoice_hash {
|
||||
if crate::content_invoice::is_paid_for(hash, id).await {
|
||||
if method_accepted(&item.access, "lightning")
|
||||
&& crate::content_invoice::is_paid_for(hash, id).await
|
||||
{
|
||||
authorized = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user