diff options
| author | Adam Soltys <[email protected]> | 2026-03-03 21:26:57 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-03-03 21:26:57 -0800 |
| commit | 9507e040a03834f7d603c5c13a1f69ae6ef48b3a (patch) | |
| tree | c82658ef1110105f1ac3f056a462b7e251db657d /lib/payments.ts | |
| parent | 82f7ae5f84da70e8c5692ff4fffa7263784a7800 (diff) | |
| download | coinos-server-9507e040a03834f7d603c5c13a1f69ae6ef48b3a.tar.gz coinos-server-9507e040a03834f7d603c5c13a1f69ae6ef48b3a.zip | |
bip353
Diffstat (limited to 'lib/payments.ts')
| -rw-r--r-- | lib/payments.ts | 35 |
1 files changed, 34 insertions, 1 deletions
diff --git a/lib/payments.ts b/lib/payments.ts index 47c82d9d..b3c63cfa 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -63,6 +63,22 @@ const { URL } = process.env; const dust = 547; +const resolveBip353 = async (name: string, domain: string) => { + const qname = `${name}.user._bitcoin-payment.${domain}`; + const res = await fetch( + `https://1.1.1.1/dns-query?name=${qname}&type=TXT`, + { headers: { accept: "application/dns-json" } }, + ); + const data = await res.json(); + if (!data.AD) return null; // DNSSEC validation failed + if (!data.Answer?.length) return null; + for (const ans of data.Answer) { + const txt = ans.data?.replace(/^"|"$/g, "").replaceAll('" "', ""); + if (txt?.startsWith("bitcoin:")) return txt; + } + return null; +}; + export const getUserRate = async (user) => { const rates = await g("rates"); const { currency } = user; @@ -478,7 +494,24 @@ const pay = async ({ aid = undefined, amount, to, user }) => { if (to.includes("@") && to.includes(".")) { const [name, domain] = to.split("@"); if (URL.includes(domain)) to = name; - lnurl = `https://${domain}/.well-known/lnurlp/${name}`; + else { + try { + const uri = await resolveBip353(name, domain); + if (uri) { + const params = new URLSearchParams(uri.split("?")[1]); + if (params.has("lno")) to = params.get("lno"); + else if (params.has("sp")) to = params.get("sp"); + else if (params.has("lightning")) to = params.get("lightning"); + else { + const addr = uri.replace("bitcoin:", "").split("?")[0]; + if (addr) to = addr; + } + } + } catch (e) { + warn("BIP 353 resolution failed, falling back to LNURL", e.message); + } + if (to.includes("@")) lnurl = `https://${domain}/.well-known/lnurlp/${name}`; + } } else if (to.startsWith("lnurl")) { lnurl = Buffer.from(bech32.fromWords(bech32.decode(to, 20000).words)).toString(); } |
