aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2024-10-01 23:22:50 -0700
committerAdam Soltys <[email protected]>2024-10-01 23:22:50 -0700
commita41ba43f2f474b36e3da4c8cedc213f7fb455b3c (patch)
tree681230cac0935ce9a4da7c0085e51f87ab34f492
parentf682348e502702b30439d30f435a335761a0c2f8 (diff)
downloadcoinos-server-a41ba43f2f474b36e3da4c8cedc213f7fb455b3c.tar.gz
coinos-server-a41ba43f2f474b36e3da4c8cedc213f7fb455b3c.zip
reverse failed payments
-rw-r--r--lib/payments.ts78
-rw-r--r--routes/payments.ts19
2 files changed, 58 insertions, 39 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 0116e16b..84f9d961 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -11,6 +11,7 @@ import {
fail,
fiat,
getInvoice,
+ getPayment,
getUser,
sleep,
SATS,
@@ -522,15 +523,25 @@ export let sendLightning = async ({
l("paying lightning invoice", pr.substr(-8), total, amount, maxfee);
- let r = await ln.pay({
- bolt11: pr.replace(/\s/g, "").toLowerCase(),
- amount_msat: amount_msat ? undefined : amount * 1000,
- maxfee: maxfee * 1000,
- retry_for: 5,
- });
+ let r;
+ try {
+ 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") await finalize(r, p);
- else await db.sAdd("inflight", pr);
+ try {
+ if (r.status === "complete") p = await finalize(r, p);
+ else await db.sAdd("pending", pr);
+ } catch (e) {
+ console.log("failed to process payment", e, p);
+ }
+ } catch (e) {
+ await reverse(p);
+ throw e;
+ }
return p;
};
@@ -756,40 +767,28 @@ export let reconcile = async (account, initial = false) => {
}
};
-export let check = async () => {
- let payments = await db.sMembers("inflight");
+let checktime;
+let check = async () => {
+ clearTimeout(checktime);
+ let payments = await db.sMembers("pending");
for (let pr of payments) {
- let p = await g(`payment:${pr}`);
+ let p = await getPayment(pr);
+ if (!p) continue;
let { pays } = await ln.listpays(pr);
- let recordExists = !!(await g(`payment:${pr}`));
- let paymentFailed =
- !pays.length || pays.every((p) => p.status === "failed");
-
- let r = pays.find((p) => p.status === "complete");
+ let failed = !pays.length || pays.every((p) => p.status === "failed");
+ let completed = pays.find((p) => p.status === "complete");
- if (r) {
- await finalize(r, p);
- } else if (recordExists && paymentFailed) {
- await db.del(`payment:${pr}`);
- await db.del(`payment:${p.id}`);
+ if (completed) await finalize(completed, p);
+ else if (failed) await reverse(p);
- 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 + p.fee + ourfee);
-
- await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit);
- await db.lRem(`${p.uid}:payments`, 1, p.id);
- }
-
- setTimeout(check, 2000);
+ checktime = setTimeout(check, 2000);
}
};
let finalize = async (r, p) => {
- await db.sRem("inflight", p.hash);
+ await db.sRem("pending", p.hash);
l("payment completed", p.id, r.payment_preimage);
let maxfee = p.fee;
@@ -800,4 +799,19 @@ let finalize = async (r, p) => {
l("refunding fee", maxfee, p.fee, maxfee - p.fee, p.ref);
await db.incrBy(`balance:${p.uid}`, maxfee - p.fee);
+
+ return p;
+};
+
+let reverse = async (p) => {
+ await db.del(`payment:${p.hash}`);
+ await db.del(`payment:${p.id}`);
+
+ 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 + p.fee + ourfee);
+
+ await db.incrBy(`credit:${types.lightning}:${p.uid}`, credit);
+ await db.lRem(`${p.uid}:payments`, 1, p.id);
};
diff --git a/routes/payments.ts b/routes/payments.ts
index 30dad67e..ec646c59 100644
--- a/routes/payments.ts
+++ b/routes/payments.ts
@@ -72,7 +72,7 @@ export default {
res.send(p);
} catch (e) {
- console.log(e)
+ console.log(e);
warn(user.username, "payment failed", amount, balance, hash, payreq);
err(e.message);
bail(res, e.message);
@@ -144,12 +144,17 @@ export default {
},
async get(req, res) {
- let {
- params: { hash },
- } = req;
- let p = await getPayment(hash);
- if (p.type === types.internal) p.with = await g(`user:${p.ref}`);
- res.send(p);
+ try {
+ let {
+ params: { hash },
+ } = req;
+ let p = await getPayment(hash);
+ if (p.type === types.internal) p.with = await g(`user:${p.ref}`);
+ res.send(p);
+ } catch (e) {
+ err("failed to get payment", e.message);
+ bail(res, e.message);
+ }
},
async parse(req, res) {