aboutsummaryrefslogtreecommitdiffstats
path: root/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-07-08 14:13:01 -0700
committerAdam Soltys <[email protected]>2020-07-08 14:13:01 -0700
commitc405440c832f54c98bae34b3f10506905199d5ce (patch)
tree155ff3e0568a03e943fe47ecd362147bd1aa1bf2 /routes
parent3dd0d83f3900818344e534681cf13155c210bd22 (diff)
downloadcoinos-server-c405440c832f54c98bae34b3f10506905199d5ce.tar.gz
coinos-server-c405440c832f54c98bae34b3f10506905199d5ce.zip
lnurl pay requests
Diffstat (limited to 'routes')
-rw-r--r--routes/lightning/send.js119
-rw-r--r--routes/lnurl.js103
2 files changed, 97 insertions, 125 deletions
diff --git a/routes/lightning/send.js b/routes/lightning/send.js
index 9c4f2259..ed561c52 100644
--- a/routes/lightning/send.js
+++ b/routes/lightning/send.js
@@ -1,124 +1,13 @@
-const bolt11 = require("bolt11");
-
module.exports = async (req, res) => {
- let hash = req.body.payreq;
- let payreq = bolt11.decode(hash);
+ let { payreq } = req.body;
let { amount, route } = req.body;
let { user } = req;
- if (!amount) amount = payreq.satoshis;
-
- l.info("attempting lightning payment", user.username, amount);
-
- if (seen.includes(hash)) {
- l.warn("attempted to pay a paid invoice", user.username);
- return res.status(500).send("Invoice has been paid, can't pay again");
- }
-
try {
- await db.transaction(async transaction => {
- let account = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset: config.liquid.btcasset
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- if (account.balance < amount) {
- throw new Error("Insufficient funds");
- }
-
- let error, fee, total, preimage;
-
- if (config.lna.clightning) {
- let m = await lna.pay(hash);
- let sent;
- ({
- msatoshi: total,
- msatoshi_sent: sent,
- payment_preimage: preimage
- } = m);
-
- fee = parseInt((sent - total)/1000);
- total = parseInt(total / 1000);
- } else {
- let m = await lna.sendPaymentSync({
- amt: amount,
- payment_request: hash,
- max_parts: 10
- });
-
- if (m.payment_error) throw new Error(m.payment_error);
-
- ({
- payment_route: { total_fees: fee, total_amt: total },
- payment_preimage: preimage
- } = m);
- }
-
- preimage = preimage.toString("hex");
-
-
- if (payreq.payeeNodeKey === config.lnb.id) {
- if (config.lna.clightning) {
- let invoice = await lna.invoice(`${amount}sat` || "any", new Date(), "", 360);
- let { bolt11: payment_request } = invoice;
- await lnb.pay(payment_request);
- } else {
- let invoice = await lna.addInvoice({ value: amount })
- let { payment_request } = invoice;
- await lnb.sendPaymentSync({ payment_request, max_parts: 10 });
- }
- l.info("lnb sent back", amount);
- }
-
- if (seen.includes(preimage)) {
- l.warn("duplicate payment detected", preimage);
- throw new Error("Duplicate payment detected");
- }
-
- total = parseInt(total);
- fee = parseInt(fee);
-
- account.balance -= total;
- await account.save({ transaction });
-
- let payment = await db.Payment.create(
- {
- amount: -total,
- account_id: account.id,
- user_id: user.id,
- hash,
- preimage,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- confirmed: true,
- network: "LNBTC",
- fee
- },
- { transaction }
- );
-
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
-
- emit(user.username, "account", payment.account);
- emit(user.username, "payment", payment);
-
- seen.push(preimage);
- l.info("sent lightning", user.username, total);
-
- seen.push(hash);
- res.send(payment);
- });
+ res.send(await send(amount, pr, user));
} catch (e) {
- l.error(
- "problem sending lightning payment",
- user.username,
- e.message
- );
+ l.error("problem sending lightning payment", user.username, e.message);
+ throw new Error(e.message);
return res.status(500).send(e.message);
}
};
diff --git a/routes/lnurl.js b/routes/lnurl.js
index 88c2f67a..743936bc 100644
--- a/routes/lnurl.js
+++ b/routes/lnurl.js
@@ -4,6 +4,8 @@ const jwt = require("jsonwebtoken");
const qs = require("query-string");
logins = {};
+recipients = {};
+payments = {};
withdrawals = {};
lnurlServer = lnurl.createServer(config.lnurl);
@@ -39,7 +41,7 @@ app.post("/withdraw", auth, async (req, res) => {
amount: value,
params: { callback, k1 },
} = req.body;
- const invoice = await lnb.addInvoice({ value });
+ const invoice = await lna.addInvoice({ value });
const { payment_request: pr } = invoice;
const url = `${callback}?k1=${k1}&pr=${pr}`;
@@ -62,6 +64,61 @@ app.post("/withdraw", auth, async (req, res) => {
}
});
+app.get("/pay", auth, async (req, res) => {
+ const { user } = req;
+ const { amount } = req.query;
+
+ try {
+ const result = await lnurlServer.generateNewUrl("payRequest", {
+ minSendable: amount * 1000,
+ maxSendable: amount * 1000,
+ metadata: JSON.stringify([["text/plain", "coinos"]]),
+ });
+
+ recipients[result.secret] = req.user;
+ res.send(result);
+ } catch (e) {
+ l.error("problem generating payment url", e.message);
+ res.status(500).send(e.message);
+ }
+});
+
+app.post("/pay", auth, async (req, res) => {
+ const { user } = req;
+ const {
+ amount,
+ params: { callback, k1 },
+ } = req.body;
+
+ const url = `${callback}?amount=${amount * 1000}`;
+
+ try {
+ const parts = callback.split("/");
+ const secret = parts[parts.length - 1];
+ payments[secret] = user;
+
+ const result = (await axios.get(url)).data;
+ const recipient = recipients[secret];
+
+ if (recipient) {
+ await db.Invoice.create({
+ user_id: recipient.id,
+ text: result.pr,
+ rate: app.get("rates")[user.currency],
+ currency: recipient.currency,
+ amount: Math.round(amount / 1000),
+ tip: 0,
+ network: "BTC",
+ });
+ }
+
+ res.send(await send(amount, result.pr, user));
+ } catch (e) {
+ l.error("failed to send payment", e.message);
+ res.status(500).send(e.message);
+ }
+});
+
app.get("/login", optionalAuth, async (req, res) => {
try {
const result = await lnurlServer.generateNewUrl("login");
@@ -88,7 +145,12 @@ app.get("/decode", async (req, res) => {
tag: "login",
k1: qs.parse(spl[1]).k1,
callback: url,
- domain: url.split("://")[1].split("/")[0].split("@").slice(-1)[0].split(":")[0],
+ domain: url
+ .split("://")[1]
+ .split("/")[0]
+ .split("@")
+ .slice(-1)[0]
+ .split(":")[0],
});
}
@@ -104,7 +166,26 @@ lnurlServer.bindToHook(
"middleware:signedLnurl:afterCheckSignature",
async (req, res, next) => {
let user;
- const { key, tag, pr, k1 } = req.query;
+ const { amount: msats, key, tag, pr, k1 } = req.query;
+
+ if (msats) {
+ amount = Math.round(msats / 1000);
+ const parts = req.originalUrl.split("/");
+ const secret = parts[parts.length - 1].split("?")[0];
+ user = payments[secret];
+ let account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset: config.liquid.btcasset,
+ }
+ });
+
+ if (account.balance < amount) {
+ throw new Error("Insufficient funds");
+ }
+
+ l.info("lnurl payment", user.username, amount);
+ }
if (tag === "login") {
let username = logins[k1];
@@ -112,10 +193,12 @@ lnurlServer.bindToHook(
if (!username) {
const keyObj = await db.Key.findOne({
where: { hex: key },
- include: [{
- model: db.User,
- as: 'user'
- }],
+ include: [
+ {
+ model: db.User,
+ as: "user",
+ },
+ ],
});
if (keyObj) ({ user } = keyObj);
@@ -185,7 +268,7 @@ lnurlServer.bindToHook(
reversed: true,
});
- p = payments.find((p) => p.payment_request === pr);
+ let p = payments.find((p) => p.payment_request === pr);
if (p) {
payment.fee = p.fee;
account.balance -= p.fee;
@@ -218,7 +301,7 @@ lnurlServer.bindToHook(
}
}
- if (next) next();
+ if (next) next(req, res);
}
);
@@ -242,7 +325,7 @@ lnurlServer.bindToHook("login", async (key) => {
where: {
user_id: user.id,
hex: key,
- }
+ },
});
l.info("added key", username, k);