aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/payments.js188
-rw-r--r--lib/whitelist.js17
-rw-r--r--routes/invoices.js16
-rw-r--r--routes/payments.js103
-rw-r--r--routes/users.js1
5 files changed, 193 insertions, 132 deletions
diff --git a/lib/payments.js b/lib/payments.js
index c924dd2b..c7cac619 100644
--- a/lib/payments.js
+++ b/lib/payments.js
@@ -8,11 +8,6 @@ import { l, err, warn } from "$lib/logging";
import { btc, fail, getInvoice, sleep, wait, SATS, sats } from "$lib/utils";
import { callWebhook } from "$lib/webhooks";
import got from "got";
-import { spawn } from "child_process";
-import ffmpeg from "fluent-ffmpeg";
-import fs from "fs";
-import EventEmitter from "events";
-import crc32 from "buffer-crc32";
import { mqtt1, mqtt2 } from "$lib/mqtt";
import { mail, templates } from "$lib/mail";
@@ -45,7 +40,7 @@ export let debit = async ({
}) => {
let ref;
let { id: uid, currency, username } = user;
- if (!rate) rate = store.rates[currency];
+ if (!rate) rate = store.rates[currency];
let invoice = await getInvoice(hash);
let iid;
@@ -112,6 +107,7 @@ export let debit = async ({
};
export let credit = async (hash, amount, memo, ref, type = types.internal) => {
+ console.log("crediting");
amount = parseInt(amount) || 0;
let inv = await getInvoice(hash);
@@ -187,6 +183,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
emit(uid, "payment", p);
notify(user, `Received ${amount} sats`);
callWebhook(inv, p);
+ l(username, "received", type, amount);
if (user.verified && user.notify) {
mail(user, "Payment received", templates.paymentReceived, {
@@ -200,20 +197,159 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => {
});
}
- mqtt1.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
- );
+ if (config.mqtt1) {
+ mqtt1.publish(
+ username,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ );
+ }
- mqtt2.publish(
- username,
- `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
- );
+ if (config.mqtt2) {
+ mqtt2.publish(
+ username,
+ `pay:${p.amount}:${p.tip}:${p.rate}:${p.created}:${p.id}`,
+ );
+ }
+
+ autowithdraw(user);
+ console.log("done crediting");
return p;
};
-export let payLightning = async (user, payreq, amount, maxfee, memo) => {
+let autowithdraw = async (user) => {
+ let { autowithdraw, id, destination, threshold, withdrawal } = user;
+ destination = destination.trim();
+ if (autowithdraw) {
+ let balance = await g(`balance:${id}`);
+ if (balance > threshold)
+ await pay({ amount: withdrawal, to: destination, user });
+ }
+};
+
+let pay = async ({ amount, to, user }) => {
+ amount = parseInt(amount) || 0;
+ let lnurl;
+ if (to.includes("@") && to.includes(".")) {
+ let [name, domain] = to.split("@");
+ lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
+ } else if (to.startsWith("lnurl")) {
+ lnurl = Buffer.from(
+ bech32.fromWords(bech32.decode(text, 20000).words),
+ ).toString();
+ }
+
+ if (lnurl) {
+ let { callback } = await got(lnurl).json();
+ let { pr } = await got(`${callback}?amount=${amount}`).json();
+ await sendLightning(user, pr, amount);
+ }
+
+ await sendOnchain({ amount, address: to, user });
+};
+
+let decode = async (hex) => {
+ let type, tx;
+ try {
+ tx = await bc.decodeRawTransaction(hex);
+ type = types.bitcoin;
+ } catch (e) {
+ try {
+ tx = await lq.decodeRawTransaction(hex);
+ type = types.liquid;
+ } catch (e) {
+ fail("unrecognized tx");
+ }
+ }
+
+ console.log("TYPE", type);
+
+ return { tx, type };
+};
+
+export let sendOnchain = async (params) => {
+ let { hex, rate, user } = params;
+ if (!hex) ({ hex } = await build(params));
+
+ let { tx, type } = await decode(hex);
+ let node = getNode(type);
+ let { txid } = tx;
+
+ console.log("YO");
+
+ if (config[type].walletpass)
+ await node.walletPassphrase(config[type].walletpass, 300);
+
+ let { hex: signed } = await node.signRawTransactionWithWallet(
+ type === types.liquid ? await node.blindRawTransaction(hex) : hex,
+ );
+
+ ({ txid } = await node.decodeRawTransaction(signed));
+
+ let r = await node.testMempoolAccept([signed]);
+ if (!r[0].allowed) fail("transaction rejected");
+
+ let total = 0;
+ let fee = 0;
+ let change = 0;
+
+ if (type === types.liquid) {
+ for (let {
+ asset,
+ scriptPubKey: { address, type },
+ value,
+ } of tx.vout) {
+ if (asset !== config.liquid.btc) fail("only L-BTC supported");
+ if (type === "fee") fee = sats(value);
+ else {
+ total += sats(value);
+
+ if (address) {
+ if ((await node.getAddressInfo(address)).ismine) {
+ change += sats(value);
+ }
+ }
+ }
+ }
+ } else {
+ let totalIn = 0;
+ for await (let { txid, vout } of tx.vin) {
+ let hex = await node.getRawTransaction(txid);
+ let tx = await node.decodeRawTransaction(hex);
+ totalIn += sats(tx.vout[vout].value);
+ }
+
+ for (let {
+ scriptPubKey: { address },
+ value,
+ } of tx.vout) {
+ total += sats(value);
+ if (await g(`invoice:${address}`))
+ fail("Cannot send to internal address");
+
+ if ((await node.getAddressInfo(address)).ismine) change += sats(value);
+ }
+
+ fee = totalIn - total;
+ }
+
+ let amount = total - change;
+
+ console.log("HERE", amount, signed.length);
+
+ await debit({
+ hash: txid,
+ amount,
+ fee,
+ rate,
+ user,
+ type,
+ });
+
+ return node.sendRawTransaction(signed);
+};
+
+export let sendLightning = async (user, payreq, amount, maxfee, memo) => {
let p;
let hash = payreq;
@@ -325,25 +461,31 @@ export let getNode = (type) => {
else fail("unrecognized transaction type");
};
-export let build = async ({ amount, address, feeRate }, user) => {
- let type;
-
+let getAddressType = async (a) => {
+ console.log("A", a);
try {
- await bc.getAddressInfo(address);
- type = types.bitcoin;
+ await bc.getAddressInfo(a);
+ return types.bitcoin;
} catch (e) {
try {
- await lq.getAddressInfo(address);
- type = types.liquid;
+ await lq.getAddressInfo(a);
+ return types.liquid;
} catch (e) {
fail("unrecognized address");
}
}
+};
+
+export let build = async ({ amount, address, feeRate, user }) => {
+ let type = await getAddressType(address);
+ console.log("type", type, amount);
let node = getNode(type);
amount = parseInt(amount);
- let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
+ let fees = { halfHourFee: 50 };
+
+ // let fees = await fetch(`${api[type]}/fees/recommended`).then((r) => r.json());
if (!feeRate) {
feeRate = fees.halfHourFee;
diff --git a/lib/whitelist.js b/lib/whitelist.js
index 3c99777c..9ef1ceca 100644
--- a/lib/whitelist.js
+++ b/lib/whitelist.js
@@ -1,10 +1,9 @@
export default [
- "autowithdraw",
- "notify",
- "anon",
- "balance",
"address",
"admin",
+ "anon",
+ "autowithdraw",
+ "balance",
"banner",
"cipher",
"currencies",
@@ -13,28 +12,30 @@ export default [
"display",
"email",
"fiat",
- "follows",
"followers",
+ "follows",
"haspin",
"hasprinter",
"id",
"index",
+ "keys",
"language",
"linked",
- "keys",
"locktime",
"nip5",
+ "notify",
"payments",
"profile",
"prompt",
"pubkey",
"salt",
"seed",
+ "shopifyStore",
+ "shopifyToken",
"theme",
"threshold",
"twofa",
"username",
"verified",
- "shopifyStore",
- "shopifyToken",
+ "withdrawal",
];
diff --git a/routes/invoices.js b/routes/invoices.js
index 898938dd..85c9166f 100644
--- a/routes/invoices.js
+++ b/routes/invoices.js
@@ -22,13 +22,15 @@ export default {
"pubkey",
]);
} else if (config.classic) {
- invoice = await got(`${config.classic}/invoice/${id}`).json();
- if (invoice) {
- invoice.id = invoice.uuid;
- invoice.classic = true;
- invoice.user.id = invoice.user.uuid;
- invoice.user.username += "@classic";
- }
+ try {
+ invoice = await got(`${config.classic}/invoice/${id}`).json();
+ if (invoice) {
+ invoice.id = invoice.uuid;
+ invoice.classic = true;
+ invoice.user.id = invoice.user.uuid;
+ invoice.user.username += "@classic";
+ }
+ } catch (e) {}
}
if (invoice) res.send(invoice);
diff --git a/routes/payments.js b/routes/payments.js
index 3dba0385..13ef20c2 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -12,7 +12,8 @@ import {
types,
getNode,
build,
- payLightning,
+ sendLightning,
+ sendOnchain,
} from "$lib/payments";
import { mqtt1, mqtt2 } from "$lib/mqtt";
import got from "got";
@@ -62,7 +63,7 @@ export default {
})
.json();
} else if (payreq) {
- p = await payLightning(user, payreq, amount, maxfee, memo);
+ p = await sendLightning(user, payreq, amount, maxfee, memo);
}
if (!p) {
@@ -79,7 +80,7 @@ export default {
res.send(p);
} catch (e) {
- warn(user.username, "payment failed", amount, balance, hash, payreq);
+ warn(user.username, "payment failed", amount, balance, hash, payreq);
err(e.message);
bail(res, e.message);
}
@@ -282,8 +283,9 @@ export default {
async fee({ body, user }, res) {
try {
- res.send(await build(body, user));
+ res.send(await build({ ...body, user }));
} catch (e) {
+ console.log(e)
warn(
"problem estimating fee",
e.message,
@@ -300,98 +302,11 @@ export default {
async send({ body, user }, res) {
try {
await requirePin({ body, user });
-
- let { memo, hex, rate } = body;
- if (!hex) ({ hex } = await build(body, user));
-
- let type, tx;
- try {
- tx = await bc.decodeRawTransaction(hex);
- type = types.bitcoin;
- } catch (e) {
- try {
- tx = await lq.decodeRawTransaction(hex);
- type = types.liquid;
- } catch (e) {
- fail("unrecognized tx");
- }
- }
-
- let node = getNode(type);
- let { txid } = tx;
-
- if (config[type].walletpass)
- await node.walletPassphrase(config[type].walletpass, 300);
-
- let { hex: signed } = await node.signRawTransactionWithWallet(
- type === types.liquid ? await node.blindRawTransaction(hex) : hex,
- );
-
- ({ txid } = await node.decodeRawTransaction(signed));
-
- let r = await node.testMempoolAccept([signed]);
- if (!r[0].allowed) fail("transaction rejected");
-
- let total = 0;
- let fee = 0;
- let change = 0;
-
- if (type === types.liquid) {
- for (let {
- asset,
- scriptPubKey: { address, type },
- value,
- } of tx.vout) {
- if (asset !== config.liquid.btc) fail("only L-BTC supported");
- if (type === "fee") fee = sats(value);
- else {
- total += sats(value);
-
- if (address) {
- if ((await node.getAddressInfo(address)).ismine) {
- change += sats(value);
- }
- }
- }
- }
- } else {
- let totalIn = 0;
- for await (let { txid, vout } of tx.vin) {
- let hex = await node.getRawTransaction(txid);
- let tx = await node.decodeRawTransaction(hex);
- totalIn += sats(tx.vout[vout].value);
- }
-
- for (let {
- scriptPubKey: { address },
- value,
- } of tx.vout) {
- total += sats(value);
- if (await g(`invoice:${address}`))
- fail("Cannot send to internal address");
-
- if ((await node.getAddressInfo(address)).ismine)
- change += sats(value);
- }
-
- fee = totalIn - total;
- }
-
- let amount = total - change;
-
- await debit({
- hash: txid,
- amount,
- fee,
- rate,
- user,
- type,
- });
-
- await node.sendRawTransaction(signed);
+ let txid = await sendOnchain({ ...body, user });
res.send({ txid });
} catch (e) {
+ console.log(e)
warn("payment failed", e.message);
res.code(500).send(e.message);
}
@@ -450,7 +365,7 @@ export default {
let r = await got(`${callback}?amount=${amount * 1000}`).json();
if (r.reason) fail(r.rason);
let { pr } = r;
- let p = await payLightning(user, pr, amount, maxfee, memo);
+ let p = await sendLightning(user, pr, amount, maxfee, memo);
if (!p) {
p = await debit({ hash: pr, amount, memo, user });
diff --git a/routes/users.js b/routes/users.js
index 230d46a1..dff3f7bc 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -244,6 +244,7 @@ export default {
"twofa",
"shopifyToken",
"shopifyStore",
+ "withdrawal",
];
for (let a of attributes) {