aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/invoices.js5
-rw-r--r--lib/lightning.js2
-rw-r--r--lib/payments.js36
-rw-r--r--lib/utils.js76
-rw-r--r--lib/whitelist.js1
-rw-r--r--routes/invoices.js2
-rw-r--r--routes/payments.js1
-rw-r--r--routes/users.js14
8 files changed, 122 insertions, 15 deletions
diff --git a/lib/invoices.js b/lib/invoices.js
index 85b03224..6962898a 100644
--- a/lib/invoices.js
+++ b/lib/invoices.js
@@ -10,7 +10,7 @@ import lq from "$lib/liquid";
import bc from "$lib/bitcoin";
import ln from "$lib/ln";
-export let generate = async ({ invoice, user, sender, memo }) => {
+export let generate = async ({ invoice, user, sender, }) => {
let {
currency,
expiry,
@@ -20,6 +20,8 @@ export let generate = async ({ invoice, user, sender, memo }) => {
items = [],
rate,
request_id,
+ memo,
+ memoPrompt,
prompt,
type,
webhook,
@@ -77,6 +79,7 @@ export let generate = async ({ invoice, user, sender, memo }) => {
pending: 0,
received: 0,
request_id,
+ memoPrompt,
prompt,
secret,
text,
diff --git a/lib/lightning.js b/lib/lightning.js
index c32e4151..4c2a830b 100644
--- a/lib/lightning.js
+++ b/lib/lightning.js
@@ -34,7 +34,7 @@ export async function listenForLightning() {
try {
if (JSON.parse(description).kind === 9734) await handleZap(inv);
} catch (e) {
- warn("failed to handle zap", e.message);
+ if (!e.message.startsWith("Unexpected")) warn("failed to handle zap", e.message);
}
}
diff --git a/lib/payments.js b/lib/payments.js
index ff7052ca..54c901e6 100644
--- a/lib/payments.js
+++ b/lib/payments.js
@@ -6,7 +6,17 @@ import { v4 } from "uuid";
import { notify } from "$lib/notifications";
import { db, g, s, t } from "$lib/db";
import { l, err, warn } from "$lib/logging";
-import { btc, fail, getInvoice, sleep, wait, SATS, sats } from "$lib/utils";
+import {
+ f,
+ btc,
+ fail,
+ getInvoice,
+ sleep,
+ wait,
+ SATS,
+ sats,
+ formatReceipt,
+} from "$lib/utils";
import { callWebhook } from "$lib/webhooks";
import got from "got";
import { mqtt1, mqtt2 } from "$lib/mqtt";
@@ -36,9 +46,10 @@ export let debit = async ({
type = types.internal,
rate,
}) => {
- if (type !== types.internal && await g("freeze")) fail("Problem sending payment");
- if (!user.unlimited && amount > 1000000)
- fail(`⚡️${amount} exceeds max withdrawal of ⚡️1,000,000`);
+ if (type !== types.internal && (await g("freeze")))
+ fail("Problem sending payment");
+ // if (!user.unlimited && amount > 1000000)
+ // fail(`⚡️${amount} exceeds max withdrawal of ⚡️1,000,000`);
let ref;
let { id: uid, currency, username } = user;
if (!rate) rate = store.rates[currency];
@@ -144,6 +155,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
let id = v4();
let p = {
id,
+ iid: inv.id,
hash,
amount: parseInt(amount - tip),
uid,
@@ -186,17 +198,29 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
callWebhook(inv, p);
l(username, "received", type, amount);
+ // let items = "";
+ // for (let i of inv.items) {
+ // items += `${i.quantity} ${i.name} ${f(
+ // i.price * i.quantity,
+ // inv.currency,
+ // )}\n`;
+ // }
+ //
+ // console.log(items);
+
+ let items = formatReceipt(inv.items, inv.currency);
+
if (config.mqtt1) {
mqtt1.publish(
username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${items}`,
);
}
if (config.mqtt2) {
mqtt2.publish(
username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}:${p.memo}:${items}`,
);
}
diff --git a/lib/utils.js b/lib/utils.js
index c68d222b..389f8a57 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -75,4 +75,80 @@ export let getInvoice = async (hash) => {
if (iid && iid.id) iid = iid.id;
else if (iid && iid.hash) iid = iid.hash;
return await g(`invoice:${iid}`);
+};
+
+export let f = (s, currency) =>
+ new Intl.NumberFormat("en-US", {
+ style: "currency",
+ currency,
+ })
+ .format(s)
+ .replace("CA", "");
+
+export function formatReceipt(items, currency) {
+function wrapText(text, maxWidth) {
+ const words = text.split(" ");
+ const lines = [];
+ let currentLine = "";
+ words.forEach((word) => {
+ if (currentLine.length + word.length + 1 > maxWidth) {
+ lines.push(currentLine);
+ currentLine = word;
+ } else {
+ currentLine += (currentLine.length > 0 ? " " : "") + word;
+ }
+ });
+ if (currentLine) lines.push(currentLine);
+ return lines;
}
+
+function calculateColumnWidths(items) {
+ let maxQuantityLength = 0;
+ let maxPriceLength = 0;
+
+ items.forEach((item) => {
+ const quantityLength = String(item.quantity).length;
+ if (quantityLength > maxQuantityLength) {
+ maxQuantityLength = quantityLength;
+ }
+
+ const priceLength = f(item.price * item.quantity, currency).length;
+ if (priceLength > maxPriceLength) {
+ maxPriceLength = priceLength;
+ }
+ });
+
+ // Add padding to the widths for aesthetic spacing
+ return {
+ quantityColumnWidth: maxQuantityLength + 1, // Space after quantity
+ priceColumnWidth: maxPriceLength + 1, // Space before price
+ };
+}
+ const maxLineWidth = 32;
+ const { quantityColumnWidth, priceColumnWidth } = calculateColumnWidths(
+ items,
+ );
+ const nameColumnWidth = maxLineWidth - quantityColumnWidth - priceColumnWidth;
+
+ return items
+ .map((item) => {
+ const quantityStr = String(item.quantity).padEnd(quantityColumnWidth);
+ const priceStr = f(item.price * item.quantity, currency).padStart(
+ priceColumnWidth,
+ );
+ const nameLines = wrapText(item.name, nameColumnWidth);
+
+ // Construct the full line(s) with the first line including the price
+ let fullLines = [
+ `${quantityStr}${nameLines[0].padEnd(nameColumnWidth)}${priceStr}`,
+ ];
+ // Add any additional name lines, properly indented
+ for (let i = 1; i < nameLines.length; i++) {
+ fullLines.push(" ".repeat(quantityColumnWidth) + nameLines[i]);
+ }
+
+ return fullLines.join("\n");
+ })
+ .join("\n\n");
+}
+
diff --git a/lib/whitelist.js b/lib/whitelist.js
index 97caf221..e709d53e 100644
--- a/lib/whitelist.js
+++ b/lib/whitelist.js
@@ -22,6 +22,7 @@ export default [
"language",
"linked",
"locktime",
+ "memoPrompt",
"nip5",
"notify",
"nsec",
diff --git a/routes/invoices.js b/routes/invoices.js
index 03911414..7a9a0d1d 100644
--- a/routes/invoices.js
+++ b/routes/invoices.js
@@ -22,7 +22,7 @@ export default {
"pubkey",
]);
- invoice.items ||= []
+ invoice.items ||= [];
}
if (invoice) res.send(invoice);
else res.code(500).send("invoice not found");
diff --git a/routes/payments.js b/routes/payments.js
index 9891c661..cc2576c7 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -32,6 +32,7 @@ export default {
async create({ body, user }, res) {
let { amount, hash, maxfee, fund, memo, payreq, rate, username } = body;
+ memo ||= user.username;
let balance = await g(`balance:${user.id}`);
try {
diff --git a/routes/users.js b/routes/users.js
index 5b76821d..cace4f17 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -107,18 +107,19 @@ export default {
if (!user) return res.code(500).send("User not found");
let whitelist = [
+ "address",
"anon",
- "username",
"banner",
- "profile",
"banner",
- "address",
"currency",
- "npub",
- "pubkey",
"display",
- "prompt",
"id",
+ "memoPrompt",
+ "npub",
+ "profile",
+ "prompt",
+ "pubkey",
+ "username",
];
if (user.pubkey)
@@ -240,6 +241,7 @@ export default {
"language",
"fiat",
"locktime",
+ "memoPrompt",
"nip5",
"notify",
"nsec",