padreug commented on issue aiolabs/lightning-pub#13

padreug
2026-04-01T17:01:16Z

Extension: Split Payments
Dual-Layer Split Architecture

The internal split mechanism (like LNbits) and NIP-57 zap tags should be combined as two complementary layers:

Layer 1: NIP-57 Zap Tags (Preferred Path)

The extension publishes the split configuration as zap tags on the user's profile or events:

{
  "tags": [
    ["zap", "<artist-pubkey>", "wss://relay", "7"],   // 70%
    ["zap", "<manager-pubkey>", "wss://relay", "2"],   // 20%
    ["zap", "<venue-pubkey>", "wss://relay", "1"]      // 10%
  ]
}

Nostr-aware wallets (Amethyst, Damus, Primal, etc.) read these tags and split the zap at the sender side. Each recipient gets their own independent zap with a proper zap receipt signed by their own LNURL provider. No double-hop through the source wallet.

Layer 2: Internal Splits (Fallback)

For payments that arrive outside the Nostr ecosystem — raw BOLT11 invoices, LNURL-pay from non-Nostr wallets, CLINK payments, keysend — the extension catches them via onPaymentReceived() and splits internally, just like LNbits does.

How They Interact

Payment arrives at Lightning.Pub
  │
  ├─ Was it already split by sender? (metadata.splitted = true)
  │   └─ YES → Do nothing (Nostr wallet already split via zap tags)
  │
  └─ NO → Internal split
      ├─ Calculate percentages
      ├─ Pay each target via payInvoice() / keysend
      └─ Tag outgoing payments with splitted = true (prevent recursion)

Payment source | What happens
Nostr wallet that reads zap tags | Sender splits before paying — each recipient gets a direct zap. Extension does nothing.
Non-Nostr Lightning wallet | Extension catches via onPaymentReceived() and splits internally
LNURL-pay from any wallet | Internal split (LNURL doesn't carry zap tag context)
CLINK / keysend | Internal split

Why Both Layers

Zap tags are more efficient: no double-hop (sender → source wallet → recipient). Each recipient gets paid directly by the sender.

Zap tags produce better receipts: each recipient gets a proper kind 9735 zap receipt from their own provider, not an internal transfer memo.

Internal splits are the safety net: not every wallet speaks Nostr. The fallback ensures 100% of payments get split, regardless of how they arrive.

The recursion guard is the bridge: metadata.splitted = true prevents the two layers from fighting. If a Nostr wallet already split the payment, the extension recognizes this and stays quiet.

Implementation Note

When the extension publishes zap tags, it should use NIP-57's weight system (not raw percentages). Weights are more flexible — [1, 1, 2] means 25%/25%/50% — and are the standard that Nostr clients already understand.