From 239f1fd498b4511d8c6a1f4ae4c8cc3b5aeb7b5f Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 2 Aug 2026 08:30:09 -0700 Subject: LNURL: fall back to the v3 names registrar for unknown users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An address at this domain may now belong to a coinos v3 wallet rather than a local account. When the username isn't ours, ask the names registrar and pass its payRequest through unchanged — its callback is absolute, so the payer talks to it directly from there. Co-Authored-By: Claude Fable 5 --- routes/lnurl.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'routes') diff --git a/routes/lnurl.ts b/routes/lnurl.ts index c96a9160..ab9b64f1 100644 --- a/routes/lnurl.ts +++ b/routes/lnurl.ts @@ -13,6 +13,8 @@ import { PaymentType } from "$lib/types"; const { URL } = process.env; const host = URL.split("/").at(-1); const fiveMinutes = 1000 * 60 * 5; +// v3 payment addresses (coinos v3 / halwallet) are registered here. +const NAMES_URL = process.env.NAMES_URL || "https://names.coinos.io"; export default { async encode(req, res) { @@ -67,7 +69,25 @@ export default { .toLowerCase(), ); - if (!user) fail(`User ${username} not found`); + // Not one of ours? It may be a v3 payment address, which lives at this + // same domain but is served by the names registrar. Hand its response + // through unchanged — the callback it returns is absolute, so the payer + // talks to the registrar directly from here on. + if (!user) { + const name = username.toLowerCase().replace(/\s/g, ""); + try { + const r = await fetch( + `${NAMES_URL}/.well-known/lnurlp/${encodeURIComponent(name)}?domain=${host}`, + ); + if (r.ok) { + const body: any = await r.json(); + if (body?.tag === "payRequest") return res.send(body); + } + } catch (e) { + warn("names lookup failed for", name, (e as any).message); + } + fail(`User ${username} not found`); + } const { id: uid } = user; const metadata = JSON.stringify([ -- cgit v1.2.3