nostr-1.3.0.0: lib/Nostr/Nip57.hs
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : Nostr.Nip57
Description : NIP-57 Lightning Zaps
Copyright : (c) Emre YILMAZ, 2026
License : MIT
Maintainer : z@emre.xyz
This module provides support for NIP-57 Zap Requests and Receipts.
-}
module Nostr.Nip57
( kindZapRequest
, kindZapReceipt
, ZapRequest(..)
, ZapReceipt(..)
, parseZapRequest
, parseZapReceipt
) where
import Data.Text (Text)
import qualified Data.Text as T
import Data.Maybe (listToMaybe, mapMaybe)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as BSL
import qualified Data.Text.Encoding as TE
import Nostr.Event (Event(..), Kind, Tag, PubKey)
kindZapRequest :: Kind
kindZapRequest = 9734
kindZapReceipt :: Kind
kindZapReceipt = 9735
-- | Represents a Zap Request (kind 9734)
data ZapRequest = ZapRequest
{ zrRelays :: [Text]
, zrAmount :: Maybe Text
, zrLnurl :: Maybe Text
, zrP :: Text -- Recipient pubkey
, zrE :: Maybe Text -- Event id being zapped
} deriving (Show, Eq)
-- | Represents a Zap Receipt (kind 9735)
data ZapReceipt = ZapReceipt
{ zrpBolt11 :: Text
, zrpPreimage :: Maybe Text
, zrpDescription :: Text -- The JSON representation of the Zap Request event
, zrpP :: Text
, zrpE :: Maybe Text
} deriving (Show, Eq)
findTagValue :: Text -> [Tag] -> Maybe Text
findTagValue name tags = listToMaybe $ mapMaybe (extract name) tags
where
extract n (t:v:_) | t == n = Just v
extract _ _ = Nothing
findTagValues :: Text -> [Tag] -> [Text]
findTagValues name tags = mapMaybe (extract name) tags
where
extract n (t:v:_) | t == n = Just v
extract _ _ = Nothing
-- | Parse an Event into a ZapRequest if it matches kind 9734
parseZapRequest :: Event -> Maybe ZapRequest
parseZapRequest ev
| eventKind ev /= kindZapRequest = Nothing
| otherwise = do
let tags = eventTags ev
p <- findTagValue "p" tags
-- relays tag can have multiple values in a single tag ["relays", "url1", "url2"]
-- or multiple ["relays", "url1"] tags. We handle both by concat.
let relays = concatMap (drop 1) (filter (\t -> case t of ("relays":_) -> True; _ -> False) tags)
return $ ZapRequest
{ zrRelays = relays
, zrAmount = findTagValue "amount" tags
, zrLnurl = findTagValue "lnurl" tags
, zrP = p
, zrE = findTagValue "e" tags
}
-- | Parse an Event into a ZapReceipt if it matches kind 9735
parseZapReceipt :: Event -> Maybe ZapReceipt
parseZapReceipt ev
| eventKind ev /= kindZapReceipt = Nothing
| otherwise = do
let tags = eventTags ev
bolt11 <- findTagValue "bolt11" tags
description <- findTagValue "description" tags
p <- findTagValue "p" tags
return $ ZapReceipt
{ zrpBolt11 = bolt11
, zrpPreimage = findTagValue "preimage" tags
, zrpDescription = description
, zrpP = p
, zrpE = findTagValue "e" tags
}