aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-10-01 22:28:48 -0700
committerAdam Soltys <[email protected]>2024-10-01 22:28:48 -0700
commitf682348e502702b30439d30f435a335761a0c2f8 (patch)
tree31c51469eaf3230851ea5316fc1ffc01402b3a46
parent80470a2c66c17e4992f176438025b2b021581d11 (diff)
downloadcoinos-server-f682348e502702b30439d30f435a335761a0c2f8.tar.gz
coinos-server-f682348e502702b30439d30f435a335761a0c2f8.zip
check inflight payments
-rw-r--r--lib/payments.ts154
-rw-r--r--routes/payments.ts1
2 files changed, 68 insertions, 87 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 852d6c57..0116e16b 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -366,6 +366,7 @@ export let decode = async (hex) => {
return { tx, type };
};
+let inflight = {};
export let sendOnchain = async (params) => {
let { aid, hex, rate, user, signed } = params;
if (!aid) aid = user.id;
@@ -480,7 +481,6 @@ let getMaxFee = (n) =>
: n * 0.01,
);
-let inflight = {};
export let sendLightning = async ({
user,
pr,
@@ -488,87 +488,51 @@ export let sendLightning = async ({
maxfee,
memo = undefined,
}) => {
- try {
- if (inflight[pr]) fail("payment in flight");
- inflight[pr] = true;
-
- let p;
- let hash = pr;
-
- if (typeof amount !== "undefined") {
- amount = parseInt(amount);
- if (amount < 0 || amount > SATS || isNaN(amount)) fail("Invalid amount");
- }
-
- let total = amount;
- let decoded = await ln.decode(pr);
- let { amount_msat } = decoded;
- if (amount_msat) total = Math.round(amount_msat / 1000);
-
- maxfee = parseInt(maxfee) || getMaxFee(total);
-
- if (maxfee < 0) fail("Max fee cannot be negative");
-
- let invoice = await getInvoice(hash);
+ let p;
- if (invoice) {
- if (invoice.aid === user.id) fail("Cannot send to self");
- hash = pr;
- } else {
- let r;
+ if (typeof amount !== "undefined") {
+ amount = parseInt(amount);
+ if (amount < 0 || amount > SATS || isNaN(amount)) fail("Invalid amount");
+ }
- let { pays } = await ln.listpays(pr);
- if (pays.find((p) => p.status === "complete"))
- fail("Invoice has already been paid");
+ let total = amount;
+ let decoded = await ln.decode(pr);
+ let { amount_msat } = decoded;
+ if (amount_msat) total = Math.round(amount_msat / 1000);
- if (pays.find((p) => p.status === "pending"))
- fail("Payment is already underway");
+ maxfee = parseInt(maxfee) || getMaxFee(total);
- p = await debit({
- hash: pr,
- amount: total,
- fee: maxfee,
- memo,
- user,
- type: types.lightning,
- });
-
- 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,
- });
+ if (maxfee < 0) fail("Max fee cannot be negative");
- if (!(r.status === "complete" && r.payment_preimage))
- fail("Payment did not complete");
+ let { pays } = await ln.listpays(pr);
+ if (pays.find((p) => p.status === "complete"))
+ fail("Invoice has already been paid");
- l("payment completed", p.id, r.payment_preimage);
+ if (pays.find((p) => p.status === "pending"))
+ fail("Payment is already underway");
- p.amount = -total;
- p.fee = Math.round((r.amount_sent_msat - r.amount_msat) / 1000);
- p.ref = r.payment_preimage;
+ p = await debit({
+ hash: pr,
+ amount: total,
+ fee: maxfee,
+ memo,
+ user,
+ type: types.lightning,
+ });
- await s(`payment:${p.id}`, p);
+ l("paying lightning invoice", pr.substr(-8), total, amount, maxfee);
- l("refunding fee", maxfee, p.fee, maxfee - p.fee, p.ref);
- await db.incrBy(`balance:${p.uid}`, maxfee - p.fee);
- } catch (e) {
- throw e;
- }
+ let r = await ln.pay({
+ bolt11: pr.replace(/\s/g, "").toLowerCase(),
+ amount_msat: amount_msat ? undefined : amount * 1000,
+ maxfee: maxfee * 1000,
+ retry_for: 5,
+ });
- clearInterval(interval);
- }
+ if (r.status === "complete") await finalize(r, p);
+ else await db.sAdd("inflight", pr);
- delete inflight[user.id];
- return p;
- } catch (e) {
- delete inflight[user.id];
- throw e;
- }
+ return p;
};
export let sendInternal = async ({
@@ -796,28 +760,44 @@ export let check = async () => {
let payments = await db.sMembers("inflight");
for (let pr of payments) {
- try {
- let { pays } = await ln.listpays(pr);
+ let p = await g(`payment:${pr}`);
+ let { pays } = await ln.listpays(pr);
+
+ let recordExists = !!(await g(`payment:${pr}`));
+ let paymentFailed =
+ !pays.length || pays.every((p) => p.status === "failed");
- let recordExists = !!(await g(`payment:${pr}`));
- let paymentFailed =
- !pays.length || pays.every((p) => p.status === "failed");
+ let r = pays.find((p) => p.status === "complete");
- if (recordExists && paymentFailed) {
- await db.del(`payment:${pr}`);
- await db.del(`payment:${p.id}`);
+ if (r) {
+ await finalize(r, p);
+ } else if (recordExists && paymentFailed) {
+ await db.del(`payment:${pr}`);
+ await db.del(`payment:${p.id}`);
- let ourfee = p.ourfee || 0;
+ let total = Math.abs(p.amount);
+ let ourfee = p.ourfee || 0;
let credit = Math.round(total * config.fee) - ourfee;
- await db.incrBy(`balance:${p.uid}`, total + maxfee + ourfee);
+ await db.incrBy(`balance:${p.uid}`, total + p.fee + ourfee);
- await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit);
- await db.lRem(`${p.uid}:payments`, 1, p.id);
- }
- } catch (e) {
- err("Failed to reverse payment", r);
+ await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit);
+ await db.lRem(`${p.uid}:payments`, 1, p.id);
}
- setTimeout(check, 2000);
+ setTimeout(check, 2000);
}
};
+
+let finalize = async (r, p) => {
+ await db.sRem("inflight", p.hash);
+ l("payment completed", p.id, r.payment_preimage);
+
+ let maxfee = p.fee;
+ 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);
+};
diff --git a/routes/payments.ts b/routes/payments.ts
index 6ee5a23a..30dad67e 100644
--- a/routes/payments.ts
+++ b/routes/payments.ts
@@ -72,6 +72,7 @@ export default {
res.send(p);
} catch (e) {
+ console.log(e)
warn(user.username, "payment failed", amount, balance, hash, payreq);
err(e.message);
bail(res, e.message);