diff options
| -rw-r--r-- | lib/payments.js | 160 |
1 files changed, 85 insertions, 75 deletions
diff --git a/lib/payments.js b/lib/payments.js index 5958bc14..cb5d4111 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -440,112 +440,122 @@ let getMaxFee = (n) => ); let checking = {}; +let inflight = {}; export let sendLightning = async ({ user, pr, amount, maxfee, memo }) => { - let p; - let hash = pr; + try { + if (inflight[user.id]) fail("payment in flight"); + inflight[user.id] = true; - if (typeof amount !== "undefined") { - amount = parseInt(amount); - if (amount < 0 || amount > SATS || isNaN(amount)) fail("Invalid amount"); - } + let p; + let hash = pr; - let total = amount; - let { amount_msat, payment_hash } = await ln.decode(pr); - if (amount_msat) total = Math.round(amount_msat / 1000); + if (typeof amount !== "undefined") { + amount = parseInt(amount); + if (amount < 0 || amount > SATS || isNaN(amount)) fail("Invalid amount"); + } - maxfee = parseInt(maxfee) || getMaxFee(total); + let total = amount; + let { amount_msat, payment_hash } = await ln.decode(pr); + if (amount_msat) total = Math.round(amount_msat / 1000); - if (maxfee < 0) fail("Max fee cannot be negative"); + maxfee = parseInt(maxfee) || getMaxFee(total); - let invoice = await getInvoice(hash); + if (maxfee < 0) fail("Max fee cannot be negative"); - if (invoice) { - if (invoice.uid === user.id) fail("Cannot send to self"); - hash = pr; - } else { - let r; + let invoice = await getInvoice(hash); - let { pays } = await ln.listpays(pr); - if (pays.find((p) => p.status === "complete")) - fail("Invoice has already been paid"); + if (invoice) { + if (invoice.uid === user.id) fail("Cannot send to self"); + hash = pr; + } else { + let r; - if (pays.find((p) => p.status === "pending")) - fail("Payment is already underway"); + let { pays } = await ln.listpays(pr); + if (pays.find((p) => p.status === "complete")) + fail("Invoice has already been paid"); - p = await debit({ - hash: pr, - amount: total, - fee: maxfee, - memo, - user, - type: types.lightning, - }); + if (pays.find((p) => p.status === "pending")) + fail("Payment is already underway"); - let check = async () => { - if (checking(pr)) return; - checking[pr] = true; + p = await debit({ + hash: pr, + amount: total, + fee: maxfee, + memo, + user, + type: types.lightning, + }); - try { - let { pays } = await ln.listpays(pr); + let check = async () => { + if (checking[pr]) return; + checking[pr] = true; - let recordExists = !!(await g(`payment:${pr}`)); - let paymentFailed = - !pays.length || pays.every((p) => p.status === "failed"); + try { + let { pays } = await ln.listpays(pr); - if (recordExists && paymentFailed) { - await db.del(`payment:${pr}`); - await db.del(`payment:${p.id}`); + let recordExists = !!(await g(`payment:${pr}`)); + let paymentFailed = + !pays.length || pays.every((p) => p.status === "failed"); - let ourfee = p.ourfee || 0; - let credit = Math.round(total * config.fee) - ourfee; - await db.incrBy(`balance:${p.uid}`, total + maxfee + ourfee); + if (recordExists && paymentFailed) { + await db.del(`payment:${pr}`); + await db.del(`payment:${p.id}`); - await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit); - await db.lRem(`${p.uid}:payments`, 1, p.id); + let ourfee = p.ourfee || 0; + let credit = Math.round(total * config.fee) - ourfee; + await db.incrBy(`balance:${p.uid}`, total + maxfee + ourfee); - clearInterval(interval); + await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit); + await db.lRem(`${p.uid}:payments`, 1, p.id); + + clearInterval(interval); + } + } catch (e) { + err("Failed to reverse payment", r); } - } catch (e) { - err("Failed to reverse payment", r); - } checking[pr] = false; - }; + }; - let interval = setInterval(check, 5000); + let interval = setInterval(check, 5000); - try { - l("paying lightning invoice", pr.substr(-8), total, amount, maxfee); + try { + l("paying lightning invoice", pr.substr(-8), total, amount, maxfee); - r = await ln.pay({ - bolt11: pr.replace(/\s/g, "").toLowerCase(), - amount_msat: amount_msat ? undefined : amount * 1000, - maxfee: maxfee * 1000, - retry_for: 5, - }); + r = await ln.pay({ + bolt11: pr.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"); + if (!(r.status === "complete" && r.payment_preimage)) + fail("Payment did not complete"); - l("payment completed", p.id, r.payment_preimage); + l("payment completed", p.id, r.payment_preimage); - p.amount = -total; - p.fee = Math.round((r.amount_sent_msat - r.amount_msat) / 1000); - p.ref = r.payment_preimage; + p.amount = -total; + p.fee = Math.round((r.amount_sent_msat - r.amount_msat) / 1000); + p.ref = r.payment_preimage; - await s(`payment:${p.id}`, p); + 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) { - await check(); - throw e; + l("refunding fee", maxfee, p.fee, maxfee - p.fee, p.ref); + await db.incrBy(`balance:${p.uid}`, maxfee - p.fee); + } catch (e) { + await check(); + throw e; + } + + clearInterval(interval); } - clearInterval(interval); + delete inflight[user.id]; + return p; + } catch (e) { + delete inflight[user.id]; + throw e; } - - return p; }; export let getNode = (type) => { |
