aboutsummaryrefslogtreecommitdiffstats
path: root/lib/payments.ts
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-04 13:05:53 -0700
committerAdam Soltys <[email protected]>2026-10-04 13:05:53 -0700
commit1ff2623dbb43148267eac5cbe68c76d2c0639805 (patch)
tree469260cac95741bbd319ee1e699ed56834e1abcc /lib/payments.ts
parent41a5819f5d7c662a5a2884952e49a14464e83c41 (diff)
downloadcoinos-server-1ff2623dbb43148267eac5cbe68c76d2c0639805.tar.gz
coinos-server-1ff2623dbb43148267eac5cbe68c76d2c0639805.zip
v3 bridge: a migrated name's legacy receives are paid on to its v3 wallet
A user who moved their name to coinos v3 keeps their legacy account, and old BOLT12 offers, bolt11 invoices and bitcoin/liquid deposit addresses keep paying into it. Each confirmed receive of those kinds is now forwarded as it lands to the name's v3 lightning address (registrar invoice on the ASP node; the v3 forwarder delivers to the Ark mailbox even with the wallet closed). Only new receives move; existing balances stay. Routing cost only: the platform fee is waived (debit exempt), and the direct channels to the ASP node quote 0. Proof required: the registrar holds the name for the legacy account's own key, or for the wallet its session named in a migration grant (/name granted flag). Autowithdraw stands down for forwarded receives; failures retry with backoff (v3fwd:retry, one process at a time) and otherwise stay put. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_011bvzKaZosXGHkigcGcSzAR
Diffstat (limited to 'lib/payments.ts')
-rw-r--r--lib/payments.ts16
1 files changed, 14 insertions, 2 deletions
diff --git a/lib/payments.ts b/lib/payments.ts
index 84d2f25a..58dabc0d 100644
--- a/lib/payments.ts
+++ b/lib/payments.ts
@@ -40,6 +40,7 @@ import {
t,
} from "$lib/utils";
import { callWebhook } from "$lib/webhooks";
+import { maybeForwardToV3, retryV3Forwards } from "$lib/v3forward";
import changeid from "$lib/changeid";
import rpc from "@coinos/rpc";
import { bech32 } from "bech32";
@@ -111,6 +112,8 @@ export const debit = async ({
user,
type = PaymentType.internal,
rate = undefined,
+ // a v3 bridge forward (lib/v3forward.ts) costs routing only
+ exempt = false,
maxTotal = undefined,
}) => {
amount = Number.parseInt(amount);
@@ -245,7 +248,7 @@ export const debit = async ({
? Math.round((amount + fee + tip) * feeRate)
: 0;
- if (aid !== uid) ourfee = 0;
+ if (aid !== uid || exempt) ourfee = 0;
// NWC and other delegated spenders can provide the exact allowance remaining
// for this call. Enforce it only after debit has reloaded the invoice and
@@ -533,10 +536,17 @@ export const credit = async ({
return p;
};
+const forwardSend = (o) => sendLightning({ ...o, exempt: true });
+// failed forwards retry with backoff; one process works the queue at a time
+setInterval(() => retryV3Forwards(forwardSend).catch(() => {}), 60_000).unref();
+
export const completePayment = async (inv, p, user) => {
const { id, autowithdraw, threshold, reserve, destination, username } = user;
let withdrawal;
- if (p.confirmed) {
+ // A name that moved to coinos v3: the receive is paid on to the v3 wallet
+ // (lib/v3forward.ts), and legacy autowithdraw stands down for it.
+ const forwarded = await maybeForwardToV3(p, user, forwardSend);
+ if (p.confirmed && !forwarded) {
if (autowithdraw) {
try {
const to = destination.trim();
@@ -945,6 +955,7 @@ export const sendLightning = async ({
memo = undefined,
payerNote = undefined,
maxTotal = undefined,
+ exempt = false,
}) => {
let p;
@@ -1049,6 +1060,7 @@ export const sendLightning = async ({
user,
type: PaymentType.lightning,
maxTotal,
+ exempt,
});
await db.sAdd("pending", pr);