From ccf1a10bad5757f08712b61fbd79093595ed36c3 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Sat, 23 Nov 2024 11:20:34 +0530 Subject: [PATCH 1/2] feat: add LNURL verify --- .env.example | 2 +- src/lnurlp.ts | 48 +++++++++++++++++++++++++++++++++++++++++------- src/main.ts | 11 ++++++----- src/users.ts | 4 ++-- 4 files changed, 50 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index 370bd2e..a6066b9 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -LOG_LEVEL=debug +LOG_LEVEL=DEBUG BASE_URL=http://localhost:8080 DATABASE_URL=postgresql://myuser:mypass@localhost:5432/alby_lite # generate using deno task db:generate:key diff --git a/src/lnurlp.ts b/src/lnurlp.ts index 5d91d9e..3fe1ce3 100644 --- a/src/lnurlp.ts +++ b/src/lnurlp.ts @@ -1,14 +1,14 @@ -import { Hono } from "hono"; +import { Context, Hono } from "hono"; import { nwc } from "npm:@getalby/sdk"; import { logger } from "../src/logger.ts"; import { BASE_URL, DOMAIN } from "./constants.ts"; import { DB } from "./db/db.ts"; import "./nwc/nwcPool.ts"; -export function createLnurlApp(db: DB) { +export function createLnurlWellKnownApp(db: DB) { const hono = new Hono(); - hono.get("/:username", async (c) => { + hono.get("/:username", async (c: Context) => { try { const username = c.req.param("username"); @@ -22,7 +22,7 @@ export function createLnurlApp(db: DB) { return c.json({ tag: "payRequest", commentAllowed: 255, - callback: `${BASE_URL}/.well-known/lnurlp/${username}/callback`, + callback: `${BASE_URL}/lnurlp/${username}/callback`, minSendable: 1000, maxSendable: 10000000000, metadata: `[["text/identifier","${username}@${DOMAIN}"],["text/plain","Sats for ${username}"]]`, @@ -32,7 +32,13 @@ export function createLnurlApp(db: DB) { } }); - hono.get("/:username/callback", async (c) => { + return hono; +} + +export function createLnurlApp(db: DB) { + const hono = new Hono(); + + hono.get("/:username/callback", async (c: Context) => { try { const username = c.req.param("username"); const amount = c.req.query("amount"); @@ -52,17 +58,45 @@ export function createLnurlApp(db: DB) { }); const transaction = await nwcClient.makeInvoice({ - amount: +amount, + amount: Math.floor(+amount / 1000) * 1000, description: comment, }); return c.json({ - pr: transaction.invoice, + verify: `${BASE_URL}/lnurlp/${username}/verify/${transaction.payment_hash}`, routes: [], + pr: transaction.invoice, }); } catch (error) { return c.json({ status: "ERROR", reason: "" + error }); } }); + + hono.get("/:username/verify/:payment_hash", async (c: Context) => { + try { + const username = c.req.param("username"); + const paymentHash = c.req.param("payment_hash"); + logger.debug("LNURLp verify", { username, paymentHash }); + + const connectionSecret = await db.findWalletConnectionSecret(username); + + const nwcClient = new nwc.NWCClient({ + nostrWalletConnectUrl: connectionSecret, + }); + + const transaction = await nwcClient.lookupInvoice({ + payment_hash: paymentHash, + }); + + return c.json({ + settled: !!transaction.settled_at, + preimage: transaction.preimage || null, + pr: transaction.invoice, + }); + } catch (error) { + return c.json({ status: "ERROR", reason: "" + error }); + } + }); + return hono; } diff --git a/src/main.ts b/src/main.ts index 295b4a2..8cb87b3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,10 +1,10 @@ -import { Hono } from "hono"; +import { Context, Hono } from "hono"; import { serveStatic } from "hono/deno"; import { secureHeaders } from "hono/secure-headers"; //import { sentry } from "npm:@hono/sentry"; import { PORT } from "./constants.ts"; import { DB, runMigration } from "./db/db.ts"; -import { createLnurlApp } from "./lnurlp.ts"; +import { createLnurlApp, createLnurlWellKnownApp } from "./lnurlp.ts"; import { LOG_LEVEL, logger, loggerMiddleware } from "./logger.ts"; import { NWCPool } from "./nwc/nwcPool.ts"; import { createUsersApp } from "./users.ts"; @@ -26,16 +26,17 @@ hono.use(secureHeaders()); hono.use("*", sentry({ dsn: SENTRY_DSN })); }*/ -hono.route("/.well-known/lnurlp", createLnurlApp(db)); +hono.route("/.well-known/lnurlp", createLnurlWellKnownApp(db)); +hono.route("/lnurlp", createLnurlApp(db)); hono.route("/users", createUsersApp(db, nwcPool)); -hono.get("/ping", (c) => { +hono.get("/ping", (c: Context) => { return c.body("OK"); }); hono.use("/favicon.ico", serveStatic({ path: "./favicon.ico" })); -hono.get("/robots.txt", (c) => { +hono.get("/robots.txt", (c: Context) => { return c.body("User-agent: *\nDisallow: /", 200); }); diff --git a/src/users.ts b/src/users.ts index 0b9f3e9..80372c2 100644 --- a/src/users.ts +++ b/src/users.ts @@ -1,4 +1,4 @@ -import { Hono } from "hono"; +import { Context, Hono } from "hono"; import { DOMAIN } from "./constants.ts"; import { DB } from "./db/db.ts"; import { logger } from "./logger.ts"; @@ -7,7 +7,7 @@ import { NWCPool } from "./nwc/nwcPool.ts"; export function createUsersApp(db: DB, nwcPool: NWCPool) { const hono = new Hono(); - hono.post("/", async (c) => { + hono.post("/", async (c: Context) => { logger.debug("create user", {}); const createUserRequest: { connectionSecret: string; username?: string } = From eaa1ea95620ad1f1ca1610c1041416476d72d11f Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 4 Dec 2024 18:11:00 +0530 Subject: [PATCH 2/2] chore: remove explicit type --- src/lnurlp.ts | 8 ++++---- src/main.ts | 6 +++--- src/users.ts | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lnurlp.ts b/src/lnurlp.ts index 3fe1ce3..306d215 100644 --- a/src/lnurlp.ts +++ b/src/lnurlp.ts @@ -1,4 +1,4 @@ -import { Context, Hono } from "hono"; +import { Hono } from "hono"; import { nwc } from "npm:@getalby/sdk"; import { logger } from "../src/logger.ts"; import { BASE_URL, DOMAIN } from "./constants.ts"; @@ -8,7 +8,7 @@ import "./nwc/nwcPool.ts"; export function createLnurlWellKnownApp(db: DB) { const hono = new Hono(); - hono.get("/:username", async (c: Context) => { + hono.get("/:username", async (c) => { try { const username = c.req.param("username"); @@ -38,7 +38,7 @@ export function createLnurlWellKnownApp(db: DB) { export function createLnurlApp(db: DB) { const hono = new Hono(); - hono.get("/:username/callback", async (c: Context) => { + hono.get("/:username/callback", async (c) => { try { const username = c.req.param("username"); const amount = c.req.query("amount"); @@ -72,7 +72,7 @@ export function createLnurlApp(db: DB) { } }); - hono.get("/:username/verify/:payment_hash", async (c: Context) => { + hono.get("/:username/verify/:payment_hash", async (c) => { try { const username = c.req.param("username"); const paymentHash = c.req.param("payment_hash"); diff --git a/src/main.ts b/src/main.ts index 8cb87b3..55f14de 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Context, Hono } from "hono"; +import { Hono } from "hono"; import { serveStatic } from "hono/deno"; import { secureHeaders } from "hono/secure-headers"; //import { sentry } from "npm:@hono/sentry"; @@ -30,13 +30,13 @@ hono.route("/.well-known/lnurlp", createLnurlWellKnownApp(db)); hono.route("/lnurlp", createLnurlApp(db)); hono.route("/users", createUsersApp(db, nwcPool)); -hono.get("/ping", (c: Context) => { +hono.get("/ping", (c) => { return c.body("OK"); }); hono.use("/favicon.ico", serveStatic({ path: "./favicon.ico" })); -hono.get("/robots.txt", (c: Context) => { +hono.get("/robots.txt", (c) => { return c.body("User-agent: *\nDisallow: /", 200); }); diff --git a/src/users.ts b/src/users.ts index 80372c2..0b9f3e9 100644 --- a/src/users.ts +++ b/src/users.ts @@ -1,4 +1,4 @@ -import { Context, Hono } from "hono"; +import { Hono } from "hono"; import { DOMAIN } from "./constants.ts"; import { DB } from "./db/db.ts"; import { logger } from "./logger.ts"; @@ -7,7 +7,7 @@ import { NWCPool } from "./nwc/nwcPool.ts"; export function createUsersApp(db: DB, nwcPool: NWCPool) { const hono = new Hono(); - hono.post("/", async (c: Context) => { + hono.post("/", async (c) => { logger.debug("create user", {}); const createUserRequest: { connectionSecret: string; username?: string } =