lnurl-0.1.0.0: src/LnUrl/Channel.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{- |
Module: LnUrl.Channel
See <https://github.com/fiatjaf/lnurl-rfc/blob/master/lnurl-channel.md>.
== Workflow
1. @LN SERVICE@ provides a URL for @LNURL-channel@.
2. @LN WALLET@ makes a @GET@ request to this URL.
3. @LN SERVICE@ responds with 'Response' 'SuccessResponse'.
4. @LN WALLET@ connects to the node at 'remoteNode'.
5. @LN WALLET@ prepare and make a @GET@ request using 'proceed' (followed
possibly by 'cancel').
6. @LN SERVICE@ responds with 'AckResponse'.
7. @LN WALLET@ awaits an @OpenChannel@ message.
-}
module LnUrl.Channel (
-- * Client
proceed,
cancel,
-- * Types
NodeId,
SuccessResponse (..),
Response (..),
AckResponse (..),
) where
import Data.Aeson (
FromJSON,
ToJSON,
object,
parseJSON,
toJSON,
withObject,
(.:),
(.=),
)
import Data.Bool (bool)
import Data.ByteString (ByteString)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import LnUrl (AckResponse (..), NodeId, Response (..))
import LnUrl.Utils (Base16 (..), JsonURI (..), getBase16)
import Network.URI (URI)
import Network.URI.Utils (addQueryParams)
-- | @LN SERVICE@ responds with 'Response' 'SuccessResponse'
data SuccessResponse = SuccessResponse
{ -- | Remote node URI
remoteNode :: ByteString
, -- | Service URL
callback :: URI
, -- | Wallet identifier
k1 :: ByteString
}
deriving (Eq, Show)
instance FromJSON SuccessResponse where
parseJSON = withObject "SuccessResponse" $ \obj ->
obj .: "tag" >>= \case
"channelRequest" ->
SuccessResponse
<$> (encodeUtf8 <$> obj .: "uri")
<*> (getJsonURI <$> obj .: "callback")
<*> (getBase16 <$> obj .: "k1")
tag -> fail $ "Unknown tag: " <> tag
instance ToJSON SuccessResponse where
toJSON success =
object
[ "uri" .= (decodeUtf8 . remoteNode) success
, "callback" .= (show . callback) success
, "k1" .= Base16 (k1 success)
]
{- | Create the URL for the follow up LNURL-channel request. @LN SERVICE@
responds with 'AckResponse'.
-}
proceed :: SuccessResponse -> NodeId -> Bool -> URI
proceed payload theRemoteNode isPrivate =
addQueryParams
(callback payload)
[ ("k1", Just $ k1 payload)
, ("remoteid", Just theRemoteNode)
, ("private", Just $ bool "0" "1" isPrivate)
]
{- | Create the URL to cancel a LNURL-channel request. @LN SERVICE@ responds
with 'AckResponse'.
-}
cancel :: SuccessResponse -> NodeId -> URI
cancel payload theRemoteNode =
addQueryParams
(callback payload)
[ ("k1", Just $ k1 payload)
, ("remoteid", Just theRemoteNode)
, ("cancel", Just "1")
]