NAME

Net::Nostr::Nutzap - NIP-61 nutzaps (Cashu ecash payments)

SYNOPSIS

use Net::Nostr::Nutzap;

my $pubkey      = 'aa' x 32;
my $p2pk_pubkey = 'cc' x 32;

# Publish nutzap info (kind 10019) - tells others how to send you ecash
my $info = Net::Nostr::Nutzap->info_event(
    pubkey      => $pubkey,
    relays      => ['wss://relay1', 'wss://relay2'],
    mints       => [
        { url => 'https://mint1', units => ['usd', 'sat'] },
        { url => 'https://mint2', units => ['sat'] },
    ],
    p2pk_pubkey => $p2pk_pubkey,
);

# Send a nutzap (kind 9321)
my $proof_json = '{"amount":1,"C":"02...","id":"000a","secret":"..."}';
my $zap = Net::Nostr::Nutzap->nutzap(
    pubkey     => $pubkey,
    recipient  => 'bb' x 32,
    proofs     => [$proof_json],
    mint_url   => 'https://mint1',
    unit       => 'sat',
    event_id   => 'dd' x 32,
    event_kind => '1',
    content    => 'Great post!',
);

# Record redemption (kind 7376)
my $nutzap_event_id = 'ee' x 32;
my $redeem = Net::Nostr::Nutzap->redemption(
    pubkey        => $pubkey,
    nutzap_ids    => [$nutzap_event_id],
    sender_pubkey => 'bb' x 32,
    relay_hint    => 'wss://relay1',
    content       => $encrypted_content,  # NIP-44 encrypted
);

# Parse any nutzap-related event
my $parsed = Net::Nostr::Nutzap->from_event($event);

# Validate
Net::Nostr::Nutzap->validate($event);

DESCRIPTION

Implements NIP-61 nutzaps -- peer-to-peer Cashu ecash payments over Nostr. A nutzap is a P2PK-locked Cashu token where the payment itself is the receipt.

Three event kinds are involved:

kind 10019 - Nutzap informational event (replaceable). Lists the user's trusted mints, relay preferences, and P2PK public key for receiving nutzaps. The P2PK pubkey MUST NOT be the user's main Nostr public key.
kind 9321 - Nutzap event. Published by the sender, containing Cashu proofs P2PK-locked to the recipient's specified public key. Includes the mint URL, unit, and optional event reference.
kind 7376 - Nutzap redemption history. Created when claiming tokens, tagging the original nutzap event(s) with a redeemed marker.

CONSTRUCTOR

new

my $info = Net::Nostr::Nutzap->new(%fields);

Creates a new Net::Nostr::Nutzap object. Typically returned by "from_event"; calling new directly is useful for testing or manual construction.

my $info = Net::Nostr::Nutzap->new(
    mint_url  => 'https://mint1',
    unit      => 'sat',
    recipient => $hex_pubkey,
    proofs    => [],
);

Accepted fields: relays (defaults to []), mints (defaults to []), p2pk_pubkey, proofs (defaults to []), mint_url, unit, recipient, event_id, event_kind, nutzap_ids (defaults to []), sender_pubkey. Croaks on unknown arguments.

CLASS METHODS

info_event

my $event = Net::Nostr::Nutzap->info_event(
    pubkey      => $hex_pubkey,
    relays      => ['wss://relay1'],
    mints       => [{ url => 'https://mint1', units => ['sat'] }],
    p2pk_pubkey => $hex_p2pk_key,
);

Creates a kind 10019 nutzap informational Net::Nostr::Event. pubkey, relays, mints, and p2pk_pubkey are required. Each mint entry is a hashref with url and an optional units arrayref of supported base units (e.g. sat, usd).

nutzap

my $event = Net::Nostr::Nutzap->nutzap(
    pubkey     => $hex_pubkey,        # required
    recipient  => $hex_pubkey,        # required (p tag)
    proofs     => [$proof_json, ...], # required (proof tags)
    mint_url   => 'https://mint',     # required (u tag)
    unit       => 'sat',              # optional, default 'sat'
    event_id   => $hex_event_id,      # optional (e tag)
    event_kind => '1',                # optional (k tag)
    relay_hint => 'wss://relay',      # optional
    content    => 'nice!',            # optional comment
);

Creates a kind 9321 nutzap Net::Nostr::Event. pubkey, recipient, proofs, and mint_url are required. Multiple proofs create multiple proof tags.

redemption

my $event = Net::Nostr::Nutzap->redemption(
    pubkey        => $hex_pubkey,
    nutzap_ids    => [$event_id, ...],
    sender_pubkey => $hex_pubkey,
    relay_hint    => 'wss://relay',     # optional
    content       => $encrypted,        # optional (NIP-44 encrypted)
);

Creates a kind 7376 redemption Net::Nostr::Event. Multiple nutzap IDs can be tagged in a single redemption. Each e tag includes the redeemed marker.

The content parameter accepts pre-encrypted NIP-44 data containing the redemption details (direction, amount, unit, created token reference). Defaults to empty string if not provided.

from_event

my $info = Net::Nostr::Nutzap->from_event($event);

Parses a kind 10019, 9321, or 7376 event into a Net::Nostr::Nutzap object. Returns undef for unrecognized kinds.

validate

Net::Nostr::Nutzap->validate($event);

Validates a nutzap-related event. Croaks on invalid structure.

For kind 10019: requires at least one relay tag, one mint tag, and a pubkey tag.

For kind 9321: requires at least one proof tag, a u tag (mint URL), and a p tag (recipient).

For kind 7376: requires at least one e tag.

ACCESSORS

Available on objects returned by "from_event". Which accessors contain data depends on the event kind parsed.

relays

Arrayref of relay URLs (kind 10019).

mints

Arrayref of hashrefs with url and units keys (kind 10019).

p2pk_pubkey

The P2PK public key for receiving nutzaps (kind 10019).

proofs

Arrayref of proof JSON strings (kind 9321).

mint_url

The mint URL from the u tag (kind 9321).

unit

The base unit (kind 9321), e.g. sat, usd.

recipient

The recipient's Nostr pubkey from the p tag (kind 9321).

event_id

The nutzapped event ID from the e tag (kind 9321), or undef.

event_kind

The nutzapped event kind from the k tag (kind 9321), or undef.

nutzap_ids

Arrayref of redeemed nutzap event IDs (kind 7376).

sender_pubkey

The nutzap sender's pubkey from the p tag (kind 7376).

SEE ALSO

NIP-61, Net::Nostr, Net::Nostr::Event