diff --git a/src/components/profile/subscription/LightningAddressForm.js b/src/components/profile/subscription/LightningAddressForm.js
index 3259159..382dd97 100644
--- a/src/components/profile/subscription/LightningAddressForm.js
+++ b/src/components/profile/subscription/LightningAddressForm.js
@@ -5,6 +5,7 @@ import { useSession } from 'next-auth/react';
import { useToast } from '@/hooks/useToast';
import { InputText } from 'primereact/inputtext';
import { ProgressSpinner } from 'primereact/progressspinner';
+import { InputNumber } from 'primereact/inputnumber';
import GenericButton from '@/components/buttons/GenericButton';
const LightningAddressForm = ({ visible, onHide }) => {
@@ -91,23 +92,25 @@ const LightningAddressForm = ({ visible, onHide }) => {
) : (
+
Only LND is currently supported at this time
+
Name
- setName(e.target.value)} />
+ setName(e.target.value)} tooltip='This is your Lightning Address name, it must be unique and will be displayed as name@plebdevs.com' />
Description
- setDescription(e.target.value)} />
+ setDescription(e.target.value)} tooltip='This is your Lightning Address description, it will be displayed as the description LUD16 lnurlp endpoint' />
Max Sendable
- setMaxSendable(e.target.value)} />
+ {/* Todo: max is 2,147,483 sats until i imlement bigInt for sat amounts */}
+ setMaxSendable(e.target.value)} max={2147483647} min={1000} tooltip='This is the maximum amount of sats that can be sent to your Lightning Address (currently denominated in sats NOT msat)' />
Min Sendable
- setMinSendable(e.target.value)} />
+ setMinSendable(e.target.value)} min={1} max={2147483647} tooltip='This is the minimum amount of sats that can be sent to your Lightning Address (currently denominated in sats NOT msat)' />
Invoice Macaroon
- setInvoiceMacaroon(e.target.value)} />
+ setInvoiceMacaroon(e.target.value)} tooltip='This is your LND Invoice Macaroon, it is used to create invoices for your Lightning Address but DOES NOT grant access to move funds from your LND node' />
LND Cert
- setLndCert(e.target.value)} />
+ setLndCert(e.target.value)} tooltip='This is your LND TLS Certificate, it is used to connect to your LND node (this may be optional)' />
LND Host
- setLndHost(e.target.value)} />
+ setLndHost(e.target.value)} tooltip='This is your LND Host, it is the hostname to your LND node' />
LND Port
- setLndPort(e.target.value)} />
+ setLndPort(e.target.value)} tooltip='This is your LND Port, it is the port to your LND node (defaults to 8080)' />
{!existingLightningAddress && (
diff --git a/src/pages/api/lightning-address/callback/[slug].js b/src/pages/api/lightning-address/callback/[slug].js
index f4631f1..b282b84 100644
--- a/src/pages/api/lightning-address/callback/[slug].js
+++ b/src/pages/api/lightning-address/callback/[slug].js
@@ -3,6 +3,7 @@ import crypto from "crypto";
import { verifyEvent } from 'nostr-tools/pure';
import appConfig from "@/config/appConfig";
import { runMiddleware, corsMiddleware } from "@/utils/corsMiddleware";
+import { getLightningAddressByName } from "@/db/models/lightningAddressModels";
const BACKEND_URL = process.env.BACKEND_URL;
@@ -10,9 +11,21 @@ export default async function handler(req, res) {
await runMiddleware(req, res, corsMiddleware);
const { slug, ...queryParams } = req.query;
+ let foundAddress = null;
const customAddress = appConfig.customLightningAddresses.find(addr => addr.name === slug);
if (customAddress) {
+ foundAddress = customAddress;
+ } else {
+ foundAddress = await getLightningAddressByName(slug);
+ }
+
+ if (!foundAddress) {
+ res.status(404).json({ error: 'Lightning address not found' });
+ return;
+ }
+
+ if (foundAddress) {
if (queryParams.amount) {
const amount = parseInt(queryParams.amount);
let metadata, metadataString, hash, descriptionHash;
@@ -41,7 +54,7 @@ export default async function handler(req, res) {
} else {
// This is a regular lnurl-pay request
metadata = [
- ["text/plain", `${customAddress.name}'s LNURL endpoint, CHEERS!`]
+ ["text/plain", `${foundAddress.name}'s LNURL endpoint, CHEERS!`]
];
metadataString = JSON.stringify(metadata);
hash = crypto.createHash('sha256').update(metadataString).digest('hex');
@@ -49,10 +62,10 @@ export default async function handler(req, res) {
}
// Convert amount from millisatoshis to satoshis
- if (amount < (customAddress.minSendable)) {
+ if (amount < (foundAddress.minSendable)) {
res.status(400).json({ error: 'Amount too low' });
return;
- } else if (amount > (customAddress.maxSendable || Number.MAX_SAFE_INTEGER)) {
+ } else if (amount > (foundAddress.maxSendable || Number.MAX_SAFE_INTEGER)) {
res.status(400).json({ error: 'Amount too high' });
return;
} else {
diff --git a/src/pages/api/lightning-address/lnd.js b/src/pages/api/lightning-address/lnd.js
index 4d3127e..6bb117a 100644
--- a/src/pages/api/lightning-address/lnd.js
+++ b/src/pages/api/lightning-address/lnd.js
@@ -2,9 +2,8 @@ import axios from "axios";
import { finalizeEvent } from 'nostr-tools/pure';
import { SimplePool } from 'nostr-tools/pool';
import appConfig from "@/config/appConfig";
+import { getLightningAddressByName } from "@/db/models/lightningAddressModels";
-const LND_HOST = process.env.LND_HOST;
-const LND_MACAROON = process.env.LND_MACAROON;
const ZAP_PRIVKEY = process.env.ZAP_PRIVKEY;
export default async function handler(req, res) {
@@ -12,16 +11,23 @@ export default async function handler(req, res) {
const { amount, description_hash, zap_request=null, name } = req.body;
// Find the custom lightning address
+ let foundAddress = null;
const customAddress = appConfig.customLightningAddresses.find(addr => addr.name === name);
- if (!customAddress) {
+ if (customAddress) {
+ foundAddress = customAddress;
+ } else {
+ foundAddress = await getLightningAddressByName(name);
+ }
+
+ if (!foundAddress) {
res.status(404).json({ error: 'Lightning address not found' });
return;
}
// Check if amount is within allowed range
- const minSendable = customAddress.minSendable || appConfig.defaultMinSendable || 1;
- const maxSendable = customAddress.maxSendable || appConfig.defaultMaxSendable || Number.MAX_SAFE_INTEGER;
+ const minSendable = foundAddress.minSendable || appConfig.defaultMinSendable || 1;
+ const maxSendable = foundAddress.maxSendable || appConfig.defaultMaxSendable || Number.MAX_SAFE_INTEGER;
if (amount < minSendable || amount > maxSendable) {
res.status(400).json({ error: 'Amount out of allowed range' });
@@ -29,30 +35,30 @@ export default async function handler(req, res) {
}
// Check if the custom address allows zaps
- if (zap_request && !customAddress.allowsNostr) {
+ if (zap_request && !foundAddress.allowsNostr) {
res.status(400).json({ error: 'Nostr zaps not allowed for this address' });
return;
}
- const response = await axios.post(`https://${LND_HOST}/v1/invoices`, {
+ const response = await axios.post(`https://${foundAddress.lndHost}/v1/invoices`, {
value_msat: amount,
description_hash: description_hash
}, {
headers: {
- 'Grpc-Metadata-macaroon': LND_MACAROON,
+ 'Grpc-Metadata-macaroon': foundAddress.invoiceMacaroon,
}
});
const invoice = response.data.payment_request;
// If this is a zap, publish a zap receipt
- if (zap_request && customAddress.allowsNostr) {
+ if (zap_request && foundAddress.allowsNostr) {
console.log("ZAP REQUEST", zap_request)
const zapRequest = JSON.parse(zap_request);
const zapReceipt = {
kind: 9735,
created_at: Math.floor(Date.now() / 1000),
- content: customAddress.zapMessage || appConfig.defaultZapMessage || '',
+ content: foundAddress.zapMessage || appConfig.defaultZapMessage || '',
tags: [
['p', zapRequest.pubkey],
['e', zapRequest.id],
@@ -61,11 +67,11 @@ export default async function handler(req, res) {
]
};
- const signedZapReceipt = finalizeEvent(zapReceipt, customAddress.relayPrivkey || ZAP_PRIVKEY);
+ const signedZapReceipt = finalizeEvent(zapReceipt, foundAddress.relayPrivkey || ZAP_PRIVKEY);
// Publish zap receipt to relays
const pool = new SimplePool();
- const relays = customAddress.defaultRelays || appConfig.defaultRelayUrls || [];
+ const relays = foundAddress.defaultRelays || appConfig.defaultRelayUrls || [];
await Promise.any(pool.publish(relays, signedZapReceipt));
}
diff --git a/src/pages/api/lightning-address/lnurlp/[slug].js b/src/pages/api/lightning-address/lnurlp/[slug].js
index 9a8ba25..f084a18 100644
--- a/src/pages/api/lightning-address/lnurlp/[slug].js
+++ b/src/pages/api/lightning-address/lnurlp/[slug].js
@@ -1,5 +1,6 @@
import appConfig from "@/config/appConfig"
import { runMiddleware, corsMiddleware } from "@/utils/corsMiddleware";
+import { getLightningAddressByName } from "@/db/models/lightningAddressModels";
const BACKEND_URL = process.env.BACKEND_URL
const ZAP_PUBKEY = process.env.ZAP_PUBKEY
@@ -13,17 +14,29 @@ export default async function handler(req, res) {
return
}
- const customAddress = appConfig.customLightningAddresses.find(addr => addr.name === slug)
+ let foundAddress = null;
+ const customAddress = appConfig.customLightningAddresses.find(addr => addr.name === slug);
if (customAddress) {
+ foundAddress = customAddress;
+ } else {
+ foundAddress = await getLightningAddressByName(slug);
+ }
+
+ if (!foundAddress) {
+ res.status(404).json({ error: 'Lightning address not found' })
+ return
+ }
+
+ if (foundAddress) {
const metadata = [
- ["text/plain", `${customAddress.description}`]
+ ["text/plain", `${foundAddress.description}`]
];
res.status(200).json({
- callback: `${BACKEND_URL}/api/lightning-address/callback/${customAddress.name}`,
- maxSendable: customAddress.maxSendable || 10000000000,
- minSendable: customAddress.minSendable || 1000,
+ callback: `${BACKEND_URL}/api/lightning-address/callback/${foundAddress.name}`,
+ maxSendable: foundAddress.maxSendable || 10000000000,
+ minSendable: foundAddress.minSendable || 1000,
metadata: JSON.stringify(metadata),
tag: 'payRequest',
allowsNostr: true,