author: rompert
LNURL
is a bech32-encoded HTTPS/Onion URL that can be interacted with automatically by a WALLET
in a standard way such that a SERVICE
can provide extra services or better experience for the user.
An example LNURL
:
https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df
would be bech32-encoded as:
LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS
and presented as the following QR:
Once LNURL
is decoded:
- If
tag
query parameter is present then thisLNURL
has a special meaning, further actions will be based ontag
parameter value. - Otherwise a GET request should be executed which must return a JSON object containing a
tag
field, further actions will be based ontag
field value.
LNURL
is acceptable in two forms: either an https://
clearnet link (no self-signed certificates allowed) or an http://
v2/v3 onion link.
LNURL
can be used as fallback inside of other URI schemes, with the key 'lightning' and the value equal to the bech32-encoding, an example: https://service.com/giftcard/redeem?id=123&lightning=LNURL1...
Neither status codes or any HTTP Header has any meaning. Servers may use whatever they want. Clients should ignore them (and be careful when using libraries that treat responses differently based on headers and status codes) and just parse the response body as JSON, then interpret it accordingly.
In Scala:
import fr.acinq.bitcoin.Bech32
val bech32lnurl: String = "LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS"
val (hrp, dataPart) = Bech32.decode(bech32lnurl)
val requestByteArray = Bech32.five2eight(dataPart)
new String(requestByteArray, "UTF-8") // https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df
In JavaScript:
var { bech32 } = require("bech32")
let bech32lnurl = "LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS"
let { prefix: hrṕ, words: dataPart } = bech32.decode(bech32lnurl, 2000)
let requestByteArray = bech32.fromWords(dataPart)
Buffer.from(requestByteArray).toString()