aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-10-02 07:19:29 -0700
committerAdam Soltys <[email protected]>2026-10-02 07:19:29 -0700
commite8d82b8b23c271aa57134d762e14fd6dcbed9c7a (patch)
tree75ce06480f1e65552e43073647b5110ec7e76a63 /src/features
parentbaef720df9e34be38fc2e5b4e4837674121e5c59 (diff)
downloadcoinosv3-e8d82b8b23c271aa57134d762e14fd6dcbed9c7a.tar.gz
coinosv3-e8d82b8b23c271aa57134d762e14fd6dcbed9c7a.zip
NWC: lookup_invoice, so BTCPay Server can connect
BTCPay's NWC connector requires get_info, make_invoice, lookup_invoice and list_transactions; we never offered lookup_invoice, so it refused the connection string. - ArkManager.lnLookup answers lookup_invoice from wallet state (by hash or bolt11, either direction; pending/settled/expired/failed, preimage once claimed). A retry's several actions on one hash defer to the furthest. - The open app pushes a pending receive along before answering, so a merchant polling sees it settle. - The service worker now answers make_invoice and lookup_invoice too, and claims a paid invoice's HTLCs during the lookup: a BTCPay sale settles with the wallet closed. Its list_transactions reports receives as well. - get_info and the 13194 info event list the new method. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo
Diffstat (limited to 'src/features')
-rw-r--r--src/features/ark.js13
-rw-r--r--src/features/nwc.js13
2 files changed, 22 insertions, 4 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 67759c9..87bb5c9 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -3629,7 +3629,18 @@ export function arkFeature(ctx) {
async arkMakeInvoice(amountSat, description) {
const mgr = await connectArk();
const a = await mgr.createLnInvoice(amountSat, description);
- return { invoice: a.invoice, paymentHash: a.paymentHash, amountSat };
+ return { invoice: a.invoice, paymentHash: a.paymentHash, amountSat, expiresAt: a.expiresAt };
+ },
+ // NIP-47 lookup_invoice: a payment of ours by hash or bolt11. One that
+ // is still pending gets a push first (a receive whose HTLCs have
+ // arrived is claimed right here), so a merchant polling sees it settle.
+ async arkLnLookup(q) {
+ const mgr = await connectArk();
+ const found = mgr.lnLookup(q);
+ if (!found || found.state !== 'pending') return found;
+ const a = (mgr.state.actions || []).find((x) => x.paymentHash === found.payment_hash && x.type.startsWith('ln-'));
+ if (a) await Promise.race([mgr.driveLn(a.id).catch(() => {}), new Promise((r) => setTimeout(r, 8000))]);
+ return mgr.lnLookup(q);
},
arkMovements() { const s = arkStateNow(); return s ? (s.movements || []) : []; },
// The wallet's reusable ark receive address (BIP-353 names publish it).
diff --git a/src/features/nwc.js b/src/features/nwc.js
index 4259207..9cf8d0b 100644
--- a/src/features/nwc.js
+++ b/src/features/nwc.js
@@ -40,7 +40,7 @@ const RES_KIND = 23195;
const INFO_KIND = 13194;
// What we can actually honour. No pay_keysend (Ark can't), no multi_pay.
-const METHODS = ['get_info', 'get_balance', 'pay_invoice', 'make_invoice', 'list_transactions'];
+const METHODS = ['get_info', 'get_balance', 'pay_invoice', 'make_invoice', 'lookup_invoice', 'list_transactions'];
// Requests older than this are ignored — a relay replaying history must not
// re-trigger payments.
@@ -461,12 +461,19 @@ export function nwcFeature(ctx) {
const inv = await hook('arkMakeInvoice', sat, params.description || '');
return {
result: {
- type: 'incoming', invoice: inv.invoice, payment_hash: inv.paymentHash,
+ type: 'incoming', state: 'pending', invoice: inv.invoice, payment_hash: inv.paymentHash,
amount: sat * 1000, created_at: nowSec(), description: params.description || '',
+ expires_at: inv.expiresAt ? Math.floor(inv.expiresAt / 1000) : undefined,
},
};
}
+ if (method === 'lookup_invoice') {
+ if (!params.payment_hash && !params.invoice) return errRes('OTHER', 'payment_hash or invoice required');
+ const tx = await hook('arkLnLookup', { paymentHash: params.payment_hash, invoice: params.invoice });
+ return tx ? { result: tx } : errRes('NOT_FOUND', 'invoice not found');
+ }
+
if (method === 'list_transactions') {
const movements = hook('arkMovements') || [];
const txs = movements
@@ -474,7 +481,7 @@ export function nwcFeature(ctx) {
.slice(-(params.limit || 20))
.map((m) => ({
type: m.type === 'ln-receive' ? 'incoming' : 'outgoing',
- invoice: m.invoice || '', preimage: m.preimage || '',
+ state: 'settled', invoice: m.invoice || '', preimage: m.preimage || '',
amount: (m.amountSat || 0) * 1000, fees_paid: 0,
created_at: Math.floor((m.ts || Date.now()) / 1000),
settled_at: Math.floor((m.ts || Date.now()) / 1000),