diff options
| author | Adam Soltys <[email protected]> | 2020-07-08 14:13:01 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-07-08 14:13:01 -0700 |
| commit | c405440c832f54c98bae34b3f10506905199d5ce (patch) | |
| tree | 155ff3e0568a03e943fe47ecd362147bd1aa1bf2 /routes | |
| parent | 3dd0d83f3900818344e534681cf13155c210bd22 (diff) | |
| download | coinos-server-c405440c832f54c98bae34b3f10506905199d5ce.tar.gz coinos-server-c405440c832f54c98bae34b3f10506905199d5ce.zip | |
lnurl pay requests
Diffstat (limited to 'routes')
| -rw-r--r-- | routes/lightning/send.js | 119 | ||||
| -rw-r--r-- | routes/lnurl.js | 103 |
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); |
