diff options
| author | Adam Soltys <[email protected]> | 2020-07-06 08:59:27 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-07-06 08:59:27 -0700 |
| commit | 9306393fbe1de9840e75f6a6738b99418fafb50f (patch) | |
| tree | e4c07c39e13990c8d953975921c68241b92b6912 /routes | |
| parent | c748c1012b8bc174fb65c72100b6a35dc100b0ac (diff) | |
| download | coinos-server-9306393fbe1de9840e75f6a6738b99418fafb50f.tar.gz coinos-server-9306393fbe1de9840e75f6a6738b99418fafb50f.zip | |
lnurl withdrawal
Diffstat (limited to 'routes')
| -rw-r--r-- | routes/lnurl.js | 175 | ||||
| -rw-r--r-- | routes/users.js | 2 |
2 files changed, 163 insertions, 14 deletions
diff --git a/routes/lnurl.js b/routes/lnurl.js index 7cd1a19e..8fb79874 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -3,17 +3,65 @@ const lnurl = require("lnurl"); const jwt = require("jsonwebtoken"); logins = {}; +withdrawals = {}; lnurlServer = lnurl.createServer(config.lnurl); -var optionalAuth = function(req, res, next) { - passport.authenticate('jwt', { session: false }, function(err, user, info) { +var optionalAuth = function (req, res, next) { + passport.authenticate("jwt", { session: false }, function (err, user, info) { req.user = user; next(); })(req, res, next); }; -app.get("/loginUrl", optionalAuth, async (req, res) => { +app.get("/withdraw", auth, async (req, res) => { + const { min, max } = req.query; + try { + const result = await lnurlServer.generateNewUrl("withdrawRequest", { + minWithdrawable: min, + maxWithdrawable: max, + defaultDescription: "coinos withdrawal", + }); + + withdrawals[result.secret] = req.user; + + res.send(result); + } catch (e) { + l.error(e.message); + res.status(500).send(e.message); + } +}); + +app.post("/withdraw", auth, async (req, res) => { + const { user } = req; + const { + amount: value, + params: { callback, k1 }, + } = req.body; + const invoice = await lnb.addInvoice({ value }); + const { payment_request: pr } = invoice; + const url = `${callback}?k1=${k1}&pr=${pr}`; + + 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", + }); + + 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); + } +}); + +app.get("/login", optionalAuth, async (req, res) => { try { const result = await lnurlServer.generateNewUrl("login"); @@ -41,20 +89,120 @@ app.get("/decode", async (req, res) => { } }); -lnurlServer.bindToHook("login", async params => { - try { - const { k1, key } = params; +lnurlServer.bindToHook( + "middleware:signedLnurl:afterCheckSignature", + async (req, res, next) => { + let user; + const { key, tag, pr, k1 } = req.query; + + if (tag === "login") { + const username = logins[k1]; + if (!username) { + user = await db.Key.findOne({ + where: { hex: key }, + }); + + if (!user) { + next(new Error("User not found")); + } + } + + logins[key] = { k1, username }; + + return next(); + } + + user = withdrawals[k1]; + if (user && pr) { + try { + let decoded = await lna.decodePayReq({ pay_req: pr }); + let amount = decoded.num_satoshis; + + 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 payment = await db.Payment.create( + { + amount: -amount, + account_id: account.id, + user_id: user.id, + hash: pr, + rate: app.get("rates")[user.currency], + currency: user.currency, + confirmed: true, + network: "LNBTC", + }, + { transaction } + ); + setTimeout(async () => { + try { + let { payments } = await lna.listPayments({ + include_incomplete: false, + max_payments: 5, + reversed: true, + }); + + p = payments.find((p) => p.payment_request === pr); + if (p) { + payment.fee = p.fee; + account.balance -= p.fee; + await account.save(); + await payment.save(); + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); + emit(user.username, "account", account); + emit(user.username, "payment", payment); + } else { + l.warn("payment not found, fee not set", pr); + } + } catch (e) { + l.error("problem trying to get ln withdrawal payment", e); + } + }, 1000); + + account.balance -= amount; + await account.save({ transaction }); + + let p = payment.get({ plain: true }); + p.account = account.get({ plain: true }); + + emit(user.username, "account", p.account); + emit(user.username, "payment", p); + }); + } catch (e) { + l.error("failed to process withdrawal", e.message); + return next(e); + } + } + + if (next) next(); + } +); + +lnurlServer.bindToHook("login", async (key) => { + try { const exists = await db.Key.findOne({ where: { hex: key }, - include: [{model: db.User, as: "user" }] + include: [{ model: db.User, as: "user" }], }); let user; - if (logins[k1]) { - const username = logins[k1]; + if (logins[key]) { + const { username } = logins[key]; user = await db.User.findOne({ - where: { username } + where: { username }, }); const k = await db.Key.create({ @@ -63,15 +211,14 @@ lnurlServer.bindToHook("login", async params => { }); l.info("added key", username, k); - emit(username, 'key', k); - } - else if (exists) ({ user } = exists); + emit(username, "key", k); + } else if (exists) ({ user } = exists); else return; if (user && user.username) { const payload = { username: user.username }; const token = jwt.sign(payload, config.jwt); - const ws = sessions[k1]; + const ws = sessions[logins[key].k1]; if (ws && ws.send) ws.send(JSON.stringify({ type: "token", data: token })); } diff --git a/routes/users.js b/routes/users.js index 5a708d00..02e76b0b 100644 --- a/routes/users.js +++ b/routes/users.js @@ -200,6 +200,7 @@ app.post("/user", auth, async (req, res) => { let { user } = req; let { username, + fiat, unit, currency, currencies, @@ -240,6 +241,7 @@ app.post("/user", auth, async (req, res) => { user.twofa = twofa; user.pin = pin; user.seed = seed; + user.fiat = fiat; if (password && password === passconfirm) { user.password = await bcrypt.hash(password, 1); |
