aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/payments.ts13
1 files changed, 12 insertions, 1 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 16eb7213..c715af47 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -697,6 +697,14 @@ export const sendLightning = async ({
}) => {
let p;
+ // BOLT11 is case-insensitive, but downstream code is not: the stored payment
+ // hash, the `pending` reconciler entry, and check()'s startsWith("ln") test
+ // all assume lowercase. An uppercase-pasted invoice gets stored uppercase,
+ // then check() treats its pending entry as non-lightning and evicts it
+ // without reversing — a failed send that never gets auto-refunded. Normalize
+ // once, up front, so every downstream use sees the same lowercase form.
+ pr = pr.replace(/\s/g, "").toLowerCase();
+
if (typeof amount !== "undefined") {
amount = Number.parseInt(amount);
if (amount < 0 || amount > SATS || Number.isNaN(amount)) {
@@ -1072,7 +1080,10 @@ export const check = async () => {
const payments = await db.sMembers("pending");
for (const pr of payments) {
- if (!pr.startsWith("ln")) {
+ // Case-insensitive: an uppercase-pasted invoice ("LNBC…") is still a
+ // lightning payment. Matching case-sensitively here used to evict such
+ // entries without reversing, stranding failed sends with no auto-refund.
+ if (!pr.toLowerCase().startsWith("ln")) {
await db.sRem("pending", pr);
continue;
}