diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/payments.js | 106 |
1 files changed, 104 insertions, 2 deletions
diff --git a/lib/payments.js b/lib/payments.js index 369d0aec..098c16f7 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -15,6 +15,7 @@ import fs from "fs"; import EventEmitter from "events"; import crc32 from "buffer-crc32"; import { mqtt1, mqtt2 } from "$lib/mqtt"; +import ln from "$lib/ln"; export const types = { internal: "internal", @@ -72,7 +73,6 @@ export let debit = async ( ourfee || 0 ); - if (r !== "ok") fail("Insufficient funds"); let p = { @@ -160,7 +160,7 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { await s(`payment:${txid}:${vout}`, id); } else { await s(`payment:${hash}`, id); - } + } let m = await db.multi(); @@ -210,3 +210,105 @@ export let confirm = async (address, txid, vout) => { .incrBy(`balance:${p.uid}`, p.amount) .exec(); }; + +export let payLightning = async (user, payreq, amount, maxfee, memo) => { + let p; + let hash = payreq; + + amount = parseInt(amount); + if (amount < 0) fail("Invalid amount"); + + maxfee = maxfee ? parseInt(maxfee) : 0; + if (maxfee < 0) fail("Max fee cannot be negative"); + + let total = amount; + let { amount_msat, payment_hash } = await ln.decode(payreq); + if (amount_msat) total = Math.round(amount_msat / 1000); + + let iid = await g(`invoice:${hash}`); + if (iid && iid.hash) iid = iid.hash; + let invoice = await g(`invoice:${iid}`); + + if (invoice) { + if (invoice.uid === user.id) fail("Cannot send to self"); + hash = payreq; + } else { + let r; + + let { pays } = await ln.listpays(payreq); + if (pays.find(p => p.status === "complete")) + fail("Invoice has already been paid"); + + if (pays.find(p => p.status === "pending")) + fail("Payment is already underway"); + + p = await debit(payreq, total, maxfee, memo, user, types.lightning); + + let check = async () => { + try { + let { pays } = await ln.listpays(payreq); + + let recordExists = !!(await g(`payment:${p.id}`)); + let paymentFailed = + !pays.length || pays.every(p => p.status === "failed"); + + l("checking", payreq, recordExists, paymentFailed); + + if (recordExists && paymentFailed) { + await db.del(`payment:${p.id}`); + + let credit = Math.round(total * config.fee) - p.ourfee; + warn("crediting balance", total + maxfee + p.ourfee); + await db.incrBy(`balance:${p.uid}`, total + maxfee + p.ourfee); + + warn("crediting credits", credit); + await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit); + + warn("reversing payment", p.id); + await db.lRem(`${p.uid}:payments`, 1, p.id); + + clearInterval(interval); + } + } catch (e) { + console.log(e); + err("Failed to reverse payment", r); + } + }; + + let interval = setInterval(check, 5000); + + try { + l("paying lightning invoice", payreq); + + r = await ln.pay({ + bolt11: payreq.replace(/\s/g, "").toLowerCase(), + amount_msat: amount_msat ? undefined : amount * 1000, + maxfee: maxfee * 1000, + retry_for: 5 + }); + + if (!(r.status === "complete" && r.payment_preimage)) + fail("Payment did not complete"); + + l("payment completed", p.id, r.payment_preimage); + + p.amount = -amount; + p.tip = total - amount; + p.fee = Math.round((r.amount_sent_msat - r.amount_msat) / 1000); + p.ref = r.payment_preimage; + + await s(`payment:${p.id}`, p); + + l("refunding fee", maxfee, p.fee, maxfee - p.fee, p.ref); + await db.incrBy(`balance:${p.uid}`, maxfee - p.fee); + } catch (e) { + warn("something went wrong", e.message); + await check(); + throw e; + } + + clearInterval(interval); + } + + return p; +}; |
