LN adresa na vlastní doméně
Máte vlastní doménu? A na ní třeba i vlastní emailovou adresu? Tak se nabízí otázka, proč nepoužít tu stejnou adresu i pro příjem platby v bitcoinu pomocí Lighting network (dále LN), namísto vystavovaní faktury a posílání změti čísel a znaků, kolikrát v délce srovnatelné s emailovou zprávou? Tak hurá na svoji novou LN adresu, budeme k tomu ale potřebovat několik pomocníků.
Co potřebuji?
1) LNURLp
Ten první krok – automatické vygenerování faktury – zá nás už vyřešilo LNURL a jeho konkrétní varianta LNURpayRequest, (někdy také LNURLp).
Co to je?
Jedná se v podstě o zakódovanou URL adresu, kde se klient (pěněženka) dozví, základní informace o tom, kolik minimálně a maximálně může zaplatit, nějaké ty metainfromace, kde najde potvrzení, nějakou zprávu, a hlavně na jakou URL adresu má požádat o vytvoření nové LN faktury na konkrétní částku, která pak může být zaplacena.
Takhle nějak to vypadá ten proces komunikace:
Tak to je skvělé! Není nad automatizaci. Namísto celé dlouhé faktury, kterou musí člověk vytvořit, už nám stačí jen jedna URL do nekonečna a na částce, faktuře k zaplacení, a případně dalších věcech ať se domluví stroje spolu sami, mě stačí bitcony.
Kde to seženu?
Je potřeba mít peněženku, která ji podporuje a umí nějakou funkční vygenerovat. Je to poměrně rozšířený standard, a pokud vám na vygenerovanou LNURL půjde zaplatit, pak vám bude fungovat i platba přes email na vaší doméně díky překladu LN adresy popsanému dále. Pro vlastní hostované řešení, je pravděpodobně nejpoužívanější LNBits.
2) Webový server obsluhující Vaši doménu
Pro překlad LN adresy ve formě emailu budeme potřebovat webový server, na který pak půjde požadavek po zadání LN adresy v peněžence. Za tímto ůčelem jsem si vypůjčil přehlednou grafiku z dokumentace lighting-address, jak samotný překlad na URL vypadá a která podle mě víc vysvětlení nepotřebuje.
Skvěle, to všechno už mám... jak to ale slepit?
Tak hurá na to. Jako první musíme dekódovat LNURL na reálnou URL, na kterou budeme přeposílat klienty jdoucí naši LN adresu (respektive na .well-knonw/.... URL).
Dekódování LNURL
Budeme potřebovat rozklíčovat, na jakou URL vlastně vede naše LNURL adresa. Je to adresa, která po dotázání vrátí základní podrobnosti o platbě v podobě LNURL Pay JSONu. V dokumentaci se můžeme dočíst, že jde o zakódování URL pomocí bech32 a je tam i ukázka kódu v Scala a JavaScriptu. Nejjednodužší je prostě copy/pasta buď do nějakého online nástroje pro testovaní, například JSFiddle (javascript) nebo Scastie (nicméně bývají problém s přímým importem), nebo využít vlastní dev/IDE prostředí. Já osobně jsem si to nechal pomocí AI přepsat do Pythonu, který už mám nainstalovaný a bylo to pro mě nejjednodužší řešení a hned po pár iteracích jsem se dopracoval až tomuto kódu:
# python3
import bech32
import sys
def decode_lnurl(url):
"""
Decodes a bech32 encoded LNURL string.
Args:
url (str): The bech32 encoded LNURL.
Returns:
str: The decoded URL.
"""
# The 'bech32_decode' function correctly handles the full bech32 string.
# It returns the human-readable part (hrp) and the data part as 5-bit integers.
hrp, data = bech32.bech32_decode(url)
if data is None:
raise ValueError("Invalid bech32 string")
# The 'convertbits' function converts from 5-bit data to 8-bit data.
decoded_bytes = bech32.convertbits(data, 5, 8, False)
# The resulting bytes are then decoded into a UTF-8 string.
return bytes(decoded_bytes).decode('utf-8')
# --- Main execution block ---
if __name__ == "__main__":
# sys.argv is a list of command-line arguments.
# sys.argv[0] is the script name itself.
# sys.argv[1] is the first argument.
if len(sys.argv) < 2:
print("Usage: python your_script_name.py <bech32_encoded_url>", file=sys.stderr)
# Exit with a non-zero status code to indicate an error
sys.exit(1)
# Get the LNURL from the first command-line argument
bech32lnurl = sys.argv[1]
try:
# It's good practice to uppercase the input as bech32 is case-insensitive
# but decoders often expect uppercase for validation.
decoded_url = decode_lnurl(bech32lnurl.upper())
print(f"Decoded URL: {decoded_url}")
except (ValueError, TypeError) as e:
print(f"Error decoding LNURL: {e}", file=sys.stderr)
sys.exit(1)
pak už jen stačilo nainstalovat bech32 modul, dát jako parametr zakódovanou LNURL a vyplivlo mi to, co jsem potřeboval.
python3 -m venv bech32
source bech32/bin/activate
python3 -m pip install bech32
python3 bech32encode.py "LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS"
Decoded URL: https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df
NOTE: Pokud by jste chtěli nahlédnout na LN fakturu, LNURL JSON, ale bez těch “tanečků” okolo, můžete použít Lighting decoder.
Nastavení HTTP serveru
Tady je situace asi nejsložitější, protože zavisí na způsobu řešení. Pokud sami hostujete službu poskytujicí LNURL endpoint (např. LNBits), pravděpodobně vám bude stačit přidat už jen rewrite pravidlo pro danou konkretni LNURL:
<VirtualHost *:443>
ServerName domain.com
# SSL konfigurace
SSLEngine on
#....
# Proxy pro /lnurlp requests na backend server
ProxyPreserveHost On
ProxyPass /lnurlp/ http://10.1.1.2:5000/lnurlp/
ProxyPassReverse /lnurlp/ http://10.1.1.2:5000/lnurlp/
# Rewrite pravidlo pro Lightning address
RewriteEngine On
RewriteRule ^/\.well-known/lnurlp/username$ /lnurlp/3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df [L]
#....
</VirtualHost>
Pokud jde o LNURL třetí strany, použije se přímý redirect na danou decodovanou URL:
<VirtualHost *:443>
ServerName domain.com
# ...
# Redirect pravidlo pro Lightning address - přesměruje na API endpoint
RewriteEngine On
RewriteRule ^/\.well-known/lnurlp/username$ https://service.com/api?q=3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df [R=302,L]
# Alternativne, presmeruj "bitlifi@domain.com na wallet u bitlifi na tel. cislo do 90-tek.
RewriteRule ^/\.well-known/lnurlp/bitlifi$ https://bitlifi.com/.well-known/lnurlp/420609112567 [R=302,L]
# ...
</VirtualHost>
V extrémním případě je možné vrátit i ručně editovany JSON soubor. Určitě to není ideální, ale funguje to, pokud bude endpointURL dostupna a souhlasit hodnoty.
S přepisem do jiných webových serverů případně může pomoci AI.
Testování
Nejjednodužší je prostě zkusit curl na náš nový rewrite:
curl -s https://domain.com/.well-known/lnurlp/username
# někdy je potřeba si explicitně vyžádat JSON,
# napřiklad v případě Bitlifi nebo Wallet of Satoshi
curl -s -X GET \
-H "Accept: application/json" \
-H "Content-type: application/json" \
https://bitlifi.com/.well-known/lnurlp/<TELEFONICISLO>
# pro hezky formátovaný JSON se dá využít pipe do jq:
curl -s -X GET \
-H "Accept: application/json" \
-H "Content-type: application/json" \
https://bitlifi.com/.well-known/lnurlp/<TELEFONICISLO> | jq -r
a v idelaním případě bychom měli dostat odpověd podobnou tomuto JSON formátu (mohou tam být i políčka navíc, zaleží na implementovaných specifikacích)
{
"tag": "payRequest",
"callback": "https://service.com/lnurlp/api/v1/lnurl/cb/3fc3645b439ce8e7f2553a69e5267081d96dcd340693afabe04be7b0ccd178df",
"minSendable": 1000,
"maxSendable": 100000,
"metadata": "[[\"text/plain\", \"Platba\"], [\"text/identifier\", \"username@domain.com\"]]",
"commentAllowed": 300
}