aboutsummaryrefslogtreecommitdiffstats
path: root/lib/invoices.ts
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-08-26 21:17:43 -0700
committerAdam Soltys <[email protected]>2024-08-26 21:17:43 -0700
commitfe62cadd6fb4a7e49f7d59401fc74ae0411e32a7 (patch)
treeaa8f34e1692bd2d638a2c996eeadb58671d07aea /lib/invoices.ts
parent00e43ddc0bf3d6dd719fc9295d156567fa2fecfb (diff)
downloadcoinos-server-fe62cadd6fb4a7e49f7d59401fc74ae0411e32a7.tar.gz
coinos-server-fe62cadd6fb4a7e49f7d59401fc74ae0411e32a7.zip
typescript
Diffstat (limited to 'lib/invoices.ts')
-rw-r--r--lib/invoices.ts128
1 files changed, 128 insertions, 0 deletions
diff --git a/lib/invoices.ts b/lib/invoices.ts
new file mode 100644
index 00000000..2f1225e7
--- /dev/null
+++ b/lib/invoices.ts
@@ -0,0 +1,128 @@
+import crypto from "crypto";
+import store from "$lib/store";
+import { l } from "$lib/logging";
+import { emit } from "$lib/sockets";
+import { db, g, s } from "$lib/db";
+import { getUser, bip21, fail, SATS } from "$lib/utils";
+import { types } from "$lib/payments";
+import { v4 } from "uuid";
+
+import lq from "$lib/liquid";
+import bc from "$lib/bitcoin";
+import ln from "$lib/ln";
+
+export let generate = async ({ invoice, user, sender }) => {
+ let {
+ bolt11,
+ currency,
+ expiry,
+ fiat,
+ tip,
+ amount,
+ items = [],
+ rate,
+ request_id,
+ memo,
+ memoPrompt,
+ prompt,
+ type,
+ webhook,
+ secret,
+ } = invoice;
+
+ amount = parseInt(amount || 0);
+ tip = parseInt(tip) || null;
+
+ if (user) user = await getUser(user.username);
+ else if (sender) user = await getUser(sender.username);
+ if (!user) fail("user not provided");
+
+ let rates = await g('rates');
+ if (!currency) currency = user.currency;
+ if (!rate) rate = rates[currency];
+ if (fiat) amount = Math.round((SATS * fiat) / rate);
+ if (amount < 0) fail("invalid amount");
+ if (tip < 0) fail("invalid tip");
+ if (rate < 0) fail("invalid rate");
+ if (memo && memo.length > 5000) fail("memo too long");
+
+ let id = v4();
+
+ let hash, text;
+ if (type === types.lightning) {
+ let r;
+ if (bolt11) {
+ let { id: nodeid } = await ln.getinfo();
+ r = await ln.decode(bolt11);
+ if (r.payee !== nodeid) fail("invalid invoice");
+ amount = Math.round(r.amount_msat / 1000);
+ r.bolt11 = bolt11;
+ } else {
+ r = await ln.invoice({
+ amount_msat: amount ? `${amount + tip}sat` : "any",
+ label: id,
+ description: memo || "",
+ expiry: expiry || 60 * 60 * 24 * 30,
+ deschashonly: true,
+ cltv: 19,
+ });
+ }
+
+ hash = r.bolt11;
+ text = r.bolt11;
+ } else if (type === types.bitcoin) {
+ hash = await bc.getNewAddress();
+ text = bip21(hash, invoice);
+ } else if (type === types.liquid) {
+ hash = await lq.getNewAddress();
+ text = bip21(hash, invoice);
+ } else if (type === types.internal) {
+ hash = id;
+ } else {
+ fail("unrecognized type");
+ }
+
+ invoice = {
+ amount,
+ created: Date.now(),
+ currency,
+ hash,
+ id,
+ items,
+ memo,
+ rate,
+ pending: 0,
+ received: 0,
+ request_id,
+ memoPrompt,
+ prompt,
+ secret,
+ text,
+ tip,
+ type,
+ uid: user.id,
+ webhook,
+ };
+
+ if (type === "liquid") {
+ let { unconfidential } = await lq.getAddressInfo(hash);
+ await s(`invoice:${unconfidential}`, id);
+ }
+
+ await s(`invoice:${hash}`, id);
+ await s(`invoice:${id}`, invoice);
+ await db.lPush(`${user.id}:invoices`, id);
+
+ if (request_id) {
+ let request = await g(`request:${request_id}`);
+ if (request) {
+ let { invoice_id: prev } = request;
+ request.invoice_id = id;
+ await s(`request:${request_id}`, request);
+
+ if (!prev) emit(request.requester_id, "invoice", invoice);
+ }
+ }
+
+ return invoice;
+};