aboutsummaryrefslogtreecommitdiffstats
path: root/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-08-03 21:38:29 -0700
committerAdam Soltys <[email protected]>2020-08-03 21:38:29 -0700
commit2bcd5e9cd2233934b6d78724a07172b187ceb0b0 (patch)
treedba0c6451c1fa962dda1c3aa848abaa36d7012ab /routes
parent1f44dadfb24913d9a6835f3b18340ede580e886a (diff)
downloadcoinos-server-2bcd5e9cd2233934b6d78724a07172b187ceb0b0.tar.gz
coinos-server-2bcd5e9cd2233934b6d78724a07172b187ceb0b0.zip
user urls
Diffstat (limited to 'routes')
-rw-r--r--routes/invoices.js23
-rw-r--r--routes/lightning/invoice.js2
-rw-r--r--routes/lightning/receive.js139
-rw-r--r--routes/lnurl.js381
-rw-r--r--routes/payments.js2
-rw-r--r--routes/users.js12
6 files changed, 295 insertions, 264 deletions
diff --git a/routes/invoices.js b/routes/invoices.js
index fdfb39d8..f344e9f9 100644
--- a/routes/invoices.js
+++ b/routes/invoices.js
@@ -14,14 +14,23 @@ app.get("/invoice", auth, ah(async (req, res, next) => {
}
}));
-app.post("/invoice", auth, ah(async (req, res, next) => {
- const { invoice } = req.body;
- invoice.user_id = req.user.id;
+app.post("/invoice", ah(async (req, res, next) => {
+ let { invoice, user } = req.body;
+ if (!user) ({ user } = req);
+ else {
+ user = await db.User.findOne({
+ where: {
+ username: user.username,
+ }
+ });
+ }
+ if (!user) throw new Error("user not provided");
+ invoice.user_id = user.id;
l.info(
"creating invoice",
invoice.text,
- req.user.username,
+ user.username,
invoice.network,
invoice.amount,
invoice.tip,
@@ -44,7 +53,7 @@ app.post("/invoice", auth, ah(async (req, res, next) => {
},
});
- res.send(
- exists ? await exists.update(invoice) : await db.Invoice.create(invoice)
- );
+ invoice = exists ? await exists.update(invoice) : await db.Invoice.create(invoice);
+ addresses[invoice.address] = user.username;
+ res.send(invoice);
}));
diff --git a/routes/lightning/invoice.js b/routes/lightning/invoice.js
index 689c696a..51eff332 100644
--- a/routes/lightning/invoice.js
+++ b/routes/lightning/invoice.js
@@ -1,8 +1,6 @@
module.exports = ah(async (req, res) => {
const { amount, memo, tip } = req.body;
- l.info("adding lightning invoice", req.user.username, amount, tip);
-
try {
if (config.lna.clightning) {
const invoice = await lnb.invoice(`${amount + tip}sat` || "any", new Date(), memo, 360);
diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js
index 34ac1fc3..e8246ab9 100644
--- a/routes/lightning/receive.js
+++ b/routes/lightning/receive.js
@@ -1,90 +1,97 @@
const asset = "LNBTC";
-const handlePayment = async msg => {
+const handlePayment = async (msg) => {
try {
- await db.transaction(async transaction => {
- if (!msg.settled) return;
+ await db.transaction(async (transaction) => {
+ if (!msg.settled) return;
- const invoice = await db.Invoice.findOne({
- where: {
- text: msg.payment_request
- }
- });
+ const invoice = await db.Invoice.findOne({
+ where: {
+ text: msg.payment_request,
+ },
+ });
+
+ if (!invoice)
+ return l.warn(
+ "received lightning with no invoice",
+ msg.payment_request
+ );
- if (!invoice) return l.warn("received lightning with no invoice", msg.payment_request);
+ const { text: hash, currency, memo, rate, tip, user_id } = invoice;
+ const amount = parseInt(msg.amt_paid_sat) - tip;
- const { text: hash, currency, memo, rate, tip, user_id } = invoice;
- const amount = parseInt(msg.amt_paid_sat) - tip;
+ const account = await db.Account.findOne({
+ where: {
+ user_id,
+ asset: config.liquid.btcasset,
+ },
+ include: {
+ model: db.User,
+ as: "user",
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction,
+ });
- const account = await db.Account.findOne({
- where: {
- user_id,
- asset: config.liquid.btcasset
- },
- include: {
- model: db.User,
- as: 'user',
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
+ const { user } = account;
- const { user } = account;
+ let preimage = msg.r_preimage.toString("hex");
- let preimage = msg.r_preimage.toString("hex");
+ let payment = await db.Payment.create(
+ {
+ account_id: account.id,
+ user_id,
+ hash,
+ memo,
+ amount,
+ currency,
+ preimage,
+ rate,
+ received: true,
+ confirmed: true,
+ network: "LNBTC",
+ tip,
+ },
+ { transaction }
+ );
- let payment = await db.Payment.create({
- account_id: account.id,
- user_id,
- hash,
- memo,
- amount,
- currency,
- preimage,
- rate,
- received: true,
- confirmed: true,
- network: "LNBTC",
- tip
- },
- { transaction });
+ let total = amount + tip;
+ invoice.received += total;
+ account.balance += total;
- let total = amount + tip;
- invoice.received += total;
- account.balance += total;
+ await account.save({ transaction });
+ await invoice.save({ transaction });
+ await payment.save({ transaction });
+ payments.push(msg.payment_request);
- await account.save({ transaction });
- await invoice.save({ transaction });
- await payment.save({ transaction });
- payments.push(msg.payment_request);
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- emit(user.username, "payment", payment);
- emit(user.username, "account", payment.account);
- notify(user, `Received ${total} SAT`);
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", payment.account);
+ notify(user, `Received ${total} SAT`);
- l.info(
- "lightning payment received",
- user.username,
- payment.amount,
- payment.tip
- );
- });
- } catch(e) {
+ l.info(
+ "lightning payment received",
+ user.username,
+ payment.amount,
+ payment.tip
+ );
+ });
+ } catch (e) {
l.error("problem receiving lightning payment", e.message);
- }
+ }
};
if (config.lna.clightning) {
- const poll = async ln => {
- const wait = async i => {
+ const poll = async (ln) => {
+ const wait = async (i) => {
const {
bolt11: payment_request,
pay_index,
status,
msatoshi_received,
- payment_preimage: r_preimage
+ payment_preimage: r_preimage,
} = await ln.waitanyinvoice(i);
let settled = status === "paid";
@@ -94,13 +101,13 @@ if (config.lna.clightning) {
payment_request,
settled,
amt_paid_sat,
- r_preimage
+ r_preimage,
});
wait(pay_index);
};
const { invoices } = await ln.listinvoices();
- wait(Math.max(...invoices.map(i => i.pay_index).filter(n => n)));
+ wait(Math.max(...invoices.map((i) => i.pay_index).filter((n) => n)));
};
poll(lna);
diff --git a/routes/lnurl.js b/routes/lnurl.js
index d8336709..44eb0fb9 100644
--- a/routes/lnurl.js
+++ b/routes/lnurl.js
@@ -17,186 +17,217 @@ var optionalAuth = function (req, res, next) {
})(req, res, next);
};
-app.get("/url", ah(async (req, res, next) => {
- try {
- const code = await db.Code.findOne({
- where: {
- code: req.query.code,
- },
- });
+app.get(
+ "/url",
+ ah(async (req, res, next) => {
+ try {
+ const code = await db.Code.findOne({
+ where: {
+ code: req.query.code,
+ },
+ });
- if (code) {
- res.send(code.text);
- } else {
- throw new Error("code not found");
+ if (code) {
+ res.send(code.text);
+ } else {
+ throw new Error("code not found");
+ }
+ } catch (e) {
+ l.error("couldn't find url", e.message);
}
- } catch (e) {
- l.error("couldn't find url", e.message);
- }
-}));
-
-app.get("/withdraw", auth, ah(async (req, res, next) => {
- const { min, max } = req.query;
- try {
- const result = await lnurlServer.generateNewUrl("withdrawRequest", {
- minWithdrawable: min * 1000,
- maxWithdrawable: max * 1000,
- defaultDescription: "coinos voucher",
- });
+ })
+);
- withdrawals[result.secret] = req.user;
+app.get(
+ "/withdraw",
+ auth,
+ ah(async (req, res, next) => {
+ const { min, max } = req.query;
+ try {
+ const result = await lnurlServer.generateNewUrl("withdrawRequest", {
+ minWithdrawable: min * 1000,
+ maxWithdrawable: max * 1000,
+ defaultDescription: "coinos voucher",
+ });
- res.send(result);
- } catch (e) {
- l.error("problem generating withdrawl url", e.message);
- res.status(500).send(e.message);
- }
-}));
+ withdrawals[result.secret] = req.user;
-app.post("/code", ah(async (req, res, next) => {
- try {
- const { encoded } = req.body.lnurl;
- const code = await db.Code.create({
- code: "lnurl:" + encoded.substr(-32),
- text: encoded,
- });
+ res.send(result);
+ } catch (e) {
+ l.error("problem generating withdrawl url", e.message);
+ res.status(500).send(e.message);
+ }
+ })
+);
- res.send(code);
- } catch (e) {
- res.status(500).send(e.message);
- }
-}));
+app.post(
+ "/code",
+ ah(async (req, res, next) => {
+ try {
+ const { encoded } = req.body.lnurl;
+ const code = await db.Code.create({
+ code: "lnurl:" + encoded.substr(-32),
+ text: encoded,
+ });
-app.post("/withdraw", auth, ah(async (req, res, next) => {
- const { user } = req;
- const {
- amount: value,
- params: { callback, k1 },
- } = req.body;
- const invoice = await lna.addInvoice({ value });
- const { payment_request: pr } = invoice;
- const url = `${callback}?k1=${k1}&pr=${pr}`;
+ res.send(code);
+ } catch (e) {
+ res.status(500).send(e.message);
+ }
+ })
+);
- await db.Invoice.create({
- user_id: user.id,
- text: pr,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- amount: value,
- tip: 0,
- network: "BTC",
- });
+app.post(
+ "/withdraw",
+ auth,
+ ah(async (req, res, next) => {
+ const { user } = req;
+ const {
+ amount: value,
+ params: { callback, k1 },
+ } = req.body;
+ const invoice = await lna.addInvoice({ value });
+ const { payment_request: pr } = invoice;
+ const url = `${callback}?k1=${k1}&pr=${pr}`;
- try {
- result = (await axios.get(url)).data;
- res.send(result);
- } catch (e) {
- l.error("failed to withdraw", e.message);
- res.status(500).send(e.message);
- }
-}));
+ await db.Invoice.create({
+ user_id: user.id,
+ text: pr,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ amount: value,
+ tip: 0,
+ network: "BTC",
+ });
-const pay = async ({ amount, minSendable, maxSendable }, res, user) => {
- minSendable = minSendable || 1000;
- maxSendable = maxSendable || 1000000000;
- if (parseInt(amount)) minSendable = maxSendable = amount * 1000;
+ try {
+ result = (await axios.get(url)).data;
+ res.send(result);
+ } catch (e) {
+ l.error("failed to withdraw", e.message);
+ res.status(500).send(e.message);
+ }
+ })
+);
- try {
- const result = await lnurlServer.generateNewUrl("payRequest", {
- minSendable,
- maxSendable,
- metadata: JSON.stringify([["text/plain", `paying ${user.username}`]]),
+app.get(
+ "/pay/:username",
+ ah(async (req, res, next) => {
+ const { username } = req.params;
+ let user = await db.User.findOne({
+ where: {
+ username,
+ },
});
- recipients[result.secret] = user;
- l.info("recipient", user.username, result.secret);
- res.send(result);
- } catch (e) {
- l.error("problem generating payment url", e.message);
- res.status(500).send(e.message);
- }
-};
+ let { amount, minSendable, maxSendable } = req.query;
+ minSendable = minSendable || 1000;
+ maxSendable = maxSendable || 1000000000;
+ if (parseInt(amount)) minSendable = maxSendable = amount * 1000;
-app.get("/pay/:username", ah(async (req, res, next) => {
- const { username } = req.params;
- let user = await db.User.findOne({
- where: {
- username,
- },
- });
- pay(req.query, res, user);
-}));
+ let invoices = await db.Invoice.findAll({
+ where: {
+ amount,
+ },
+ order: [["id", "DESC"]],
+ limit: 1,
+ });
-app.get("/pay", auth, ah(async (req, res, next) => {
- const { user } = req;
- pay(req.query, res, user);
-}));
+ if (invoices.length) await invoices[0].destroy();
-app.post("/pay", auth, ah(async (req, res, next) => {
- const { user } = req;
- const {
- amount,
- params: { callback, k1 },
- } = req.body;
+ try {
+ const result = await lnurlServer.generateNewUrl("payRequest", {
+ minSendable,
+ maxSendable,
+ metadata: JSON.stringify([["text/plain", `paying ${user.username}`]]),
+ });
- const url = `${callback}?amount=${amount * 1000}`;
- l.info("url", url);
+ recipients[result.secret] = user;
+ l.info("recipient", user.username, result.secret);
+ res.send(result);
+ } catch (e) {
+ l.error("problem generating payment url", e.message);
+ res.status(500).send(e.message);
+ }
+ })
+);
- try {
- const parts = callback.split("/");
- const secret = parts[parts.length - 1];
- payments[secret] = user;
+app.post(
+ "/pay",
+ auth,
+ ah(async (req, res, next) => {
+ const { user } = req;
+ const {
+ amount,
+ params: { callback, k1 },
+ } = req.body;
- const result = (await axios.get(url)).data;
- res.send(await send(amount, '', result.pr, user));
- } catch (e) {
- l.error("failed to send payment", e.message);
- res.status(500).send(e.message);
- }
-}));
+ const url = `${callback}?amount=${amount * 1000}`;
-app.get("/login", optionalAuth, ah(async (req, res, next) => {
- try {
- const result = await lnurlServer.generateNewUrl("login");
+ try {
+ const parts = callback.split("/");
+ const secret = parts[parts.length - 1];
+ payments[secret] = user;
- if (req.user) {
- logins[result.secret] = req.user.username;
+ const result = (await axios.get(url)).data;
+ res.send(await send(amount, "", result.pr, user));
+ } catch (e) {
+ l.error("failed to send payment", e.message);
+ throw e;
+ res.status(500).send(e.message);
}
+ })
+);
- res.send(result);
- } catch (e) {
- l.error("problem generating login url", e.message);
- res.status(500).send(e.message);
- }
-}));
+app.get(
+ "/login",
+ optionalAuth,
+ ah(async (req, res, next) => {
+ try {
+ const result = await lnurlServer.generateNewUrl("login");
-app.get("/decode", ah(async (req, res, next) => {
- const { text } = req.query;
+ if (req.user) {
+ logins[result.secret] = req.user.username;
+ }
- try {
- const url = lnurl.decode(text);
- let spl = url.split("?");
- if (spl.length > 1 && qs.parse(spl[1]).tag === "login") {
- return res.send({
- tag: "login",
- k1: qs.parse(spl[1]).k1,
- callback: url,
- domain: url
- .split("://")[1]
- .split("/")[0]
- .split("@")
- .slice(-1)[0]
- .split(":")[0],
- });
+ res.send(result);
+ } catch (e) {
+ l.error("problem generating login url", e.message);
+ res.status(500).send(e.message);
}
+ })
+);
- const { data: params } = await axios.get(url);
- res.send(params);
- } catch (e) {
- l.error("problem decoding lnurl", e.message);
- res.status(500).send(e.message);
- }
-}));
+app.get(
+ "/decode",
+ ah(async (req, res, next) => {
+ const { text } = req.query;
+
+ try {
+ const url = lnurl.decode(text);
+ let spl = url.split("?");
+ if (spl.length > 1 && qs.parse(spl[1]).tag === "login") {
+ return res.send({
+ tag: "login",
+ k1: qs.parse(spl[1]).k1,
+ callback: url,
+ domain: url
+ .split("://")[1]
+ .split("/")[0]
+ .split("@")
+ .slice(-1)[0]
+ .split(":")[0],
+ });
+ }
+
+ const { data: params } = await axios.get(url);
+ res.send(params);
+ } catch (e) {
+ l.error("problem decoding lnurl", e.message);
+ res.status(500).send(e.message);
+ }
+ })
+);
lnurlServer.bindToHook(
"middleware:signedLnurl:afterCheckSignature",
@@ -221,36 +252,29 @@ lnurlServer.bindToHook(
if (account.balance < amount) {
throw new Error("Insufficient funds");
}
-
- l.info("lnurl payment", user.username, amount);
}
const recipient = recipients[secret];
if (recipient) {
- setTimeout(async () => {
- try {
- let { invoices } = await lnb.listInvoices({
- pending_only: true,
- num_max_invoices: 1,
- reversed: true,
- });
- let i = invoices.find((i) => parseInt(i.value) === amount);
- if (i) {
- await db.Invoice.create({
- user_id: recipient.id,
- text: i.payment_request,
- rate: app.get("rates")[recipient.currency],
- currency: recipient.currency,
- amount: Math.round(amount / 1000),
- tip: 0,
- network: "BTC",
- });
- }
- } catch (e) {
- l.error("problem trying to get create payment invoice", e);
- }
- }, 1000);
+ setTimeout(async () => {
+ let { invoices } = await lnb.listInvoices({
+ pending_only: true,
+ });
+
+ if (invoices.length) {
+ let invoice = await db.Invoice.create({
+ user_id: recipient.id,
+ text: invoices[invoices.length - 1].payment_request,
+ rate: app.get("rates")[recipient.currency],
+ currency: recipient.currency,
+ amount,
+ tip: 0,
+ network: "BTC",
+ });
+ l.info("invoice created", invoice.text, invoice.amount);
+ }
+ }, 100);
}
}
@@ -337,6 +361,7 @@ lnurlServer.bindToHook(
let p = payments.find((p) => p.payment_request === pr);
if (p) {
+ l.info("found payment", pr);
payment.fee = p.fee;
account.balance -= p.fee;
await account.save();
diff --git a/routes/payments.js b/routes/payments.js
index dcd76f2e..d88c95a7 100644
--- a/routes/payments.js
+++ b/routes/payments.js
@@ -95,7 +95,7 @@ const read = require("../lib/read");
"/lightning/channelRequest",
require("./lightning/channelRequest")
);
- app.post("/lightning/invoice", auth, require("./lightning/invoice"));
+ app.post("/lightning/invoice", require("./lightning/invoice"));
app.post("/lightning/query", auth, require("./lightning/query"));
app.post("/lightning/send", auth, require("./lightning/send"));
require("./lightning/receive");
diff --git a/routes/users.js b/routes/users.js
index 1a0f82c1..471a4e5e 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -294,23 +294,15 @@ app.post(
})
);
-app.post(
+app.get(
"/address",
- auth,
ah(async (req, res) => {
if (!config.bitcoin) return res.status(500).send("Bitcoin not configured");
- let { user } = req;
- delete addresses[user.address];
-
if (config.bitcoin.walletpass)
await bc.walletPassphrase(config.bitcoin.walletpass, 300);
- user.address = await bc.getNewAddress("", req.body.type || "bech32");
- await user.save();
- addresses[user.address] = user.username;
- emit(user.username, "user", user);
- res.send(user.address);
+ res.send(await bc.getNewAddress("", req.query.type || "bech32"));
})
);