@thecryptodonkey/toll-booth 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/adapters/express.d.ts +51 -0
- package/dist/adapters/express.d.ts.map +1 -0
- package/dist/adapters/express.js +237 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/proxy-headers.d.ts +7 -0
- package/dist/adapters/proxy-headers.d.ts.map +1 -0
- package/dist/adapters/proxy-headers.js +58 -0
- package/dist/adapters/proxy-headers.js.map +1 -0
- package/dist/adapters/web-standard.d.ts +60 -0
- package/dist/adapters/web-standard.d.ts.map +1 -0
- package/dist/adapters/web-standard.js +252 -0
- package/dist/adapters/web-standard.js.map +1 -0
- package/dist/backends/alby.d.ts +25 -0
- package/dist/backends/alby.d.ts.map +1 -0
- package/dist/backends/alby.js +137 -0
- package/dist/backends/alby.js.map +1 -0
- package/dist/backends/cln.d.ts +22 -0
- package/dist/backends/cln.d.ts.map +1 -0
- package/dist/backends/cln.js +55 -0
- package/dist/backends/cln.js.map +1 -0
- package/dist/backends/lnbits.d.ts +23 -0
- package/dist/backends/lnbits.d.ts.map +1 -0
- package/dist/backends/lnbits.js +58 -0
- package/dist/backends/lnbits.js.map +1 -0
- package/dist/backends/lnd.d.ts +21 -0
- package/dist/backends/lnd.d.ts.map +1 -0
- package/dist/backends/lnd.js +59 -0
- package/dist/backends/lnd.js.map +1 -0
- package/dist/backends/phoenixd.d.ts +19 -0
- package/dist/backends/phoenixd.d.ts.map +1 -0
- package/dist/backends/phoenixd.js +59 -0
- package/dist/backends/phoenixd.js.map +1 -0
- package/dist/booth.d.ts +54 -0
- package/dist/booth.d.ts.map +1 -0
- package/dist/booth.js +200 -0
- package/dist/booth.js.map +1 -0
- package/dist/core/cashu-redeem.d.ts +9 -0
- package/dist/core/cashu-redeem.d.ts.map +1 -0
- package/dist/core/cashu-redeem.js +85 -0
- package/dist/core/cashu-redeem.js.map +1 -0
- package/dist/core/create-invoice.d.ts +19 -0
- package/dist/core/create-invoice.d.ts.map +1 -0
- package/dist/core/create-invoice.js +66 -0
- package/dist/core/create-invoice.js.map +1 -0
- package/dist/core/invoice-status.d.ts +24 -0
- package/dist/core/invoice-status.d.ts.map +1 -0
- package/dist/core/invoice-status.js +74 -0
- package/dist/core/invoice-status.js.map +1 -0
- package/dist/core/nwc-pay.d.ts +8 -0
- package/dist/core/nwc-pay.d.ts.map +1 -0
- package/dist/core/nwc-pay.js +23 -0
- package/dist/core/nwc-pay.js.map +1 -0
- package/dist/core/toll-booth.d.ts +9 -0
- package/dist/core/toll-booth.d.ts.map +1 -0
- package/dist/core/toll-booth.js +172 -0
- package/dist/core/toll-booth.js.map +1 -0
- package/dist/core/types.d.ts +101 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/free-tier.d.ts +14 -0
- package/dist/free-tier.d.ts.map +1 -0
- package/dist/free-tier.js +41 -0
- package/dist/free-tier.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/macaroon.d.ts +39 -0
- package/dist/macaroon.d.ts.map +1 -0
- package/dist/macaroon.js +111 -0
- package/dist/macaroon.js.map +1 -0
- package/dist/payment-page.d.ts +18 -0
- package/dist/payment-page.d.ts.map +1 -0
- package/dist/payment-page.js +391 -0
- package/dist/payment-page.js.map +1 -0
- package/dist/stats.d.ts +63 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +75 -0
- package/dist/stats.js.map +1 -0
- package/dist/storage/interface.d.ts +58 -0
- package/dist/storage/interface.d.ts.map +1 -0
- package/dist/storage/interface.js +3 -0
- package/dist/storage/interface.js.map +1 -0
- package/dist/storage/memory.d.ts +3 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +139 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/sqlite.d.ts +6 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +264 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/types.d.ts +198 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/llms.txt +91 -0
- package/package.json +100 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { TollBoothEngine } from '../core/toll-booth.js';
|
|
2
|
+
import type { CreateInvoiceDeps } from '../core/create-invoice.js';
|
|
3
|
+
import type { InvoiceStatusDeps } from '../core/invoice-status.js';
|
|
4
|
+
import type { NwcPayDeps } from '../core/nwc-pay.js';
|
|
5
|
+
import type { CashuRedeemDeps } from '../core/cashu-redeem.js';
|
|
6
|
+
export type WebStandardHandler = (req: Request) => Promise<Response>;
|
|
7
|
+
/**
|
|
8
|
+
* Returns a `WebStandardHandler` that enforces L402 payment gating.
|
|
9
|
+
*
|
|
10
|
+
* On `pass` or `proxy` results the request is forwarded to the upstream.
|
|
11
|
+
* On `challenge` a 402 response is returned with invoice details.
|
|
12
|
+
*/
|
|
13
|
+
export interface WebStandardMiddlewareConfig {
|
|
14
|
+
engine: TollBoothEngine;
|
|
15
|
+
upstream: string;
|
|
16
|
+
trustProxy?: boolean;
|
|
17
|
+
responseHeaders?: Record<string, string>;
|
|
18
|
+
/** Timeout in milliseconds for upstream proxy requests (default: 30000). */
|
|
19
|
+
upstreamTimeout?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Custom callback to extract client IP from the request.
|
|
22
|
+
* Use this for platform-specific IP resolution (e.g. Cloudflare's
|
|
23
|
+
* `CF-Connecting-IP`, Deno's `connInfo.remoteAddr`).
|
|
24
|
+
* If `freeTier` is enabled, provide either `trustProxy: true` or
|
|
25
|
+
* a `getClientIp` callback for per-client isolation.
|
|
26
|
+
*/
|
|
27
|
+
getClientIp?: (req: Request) => string;
|
|
28
|
+
}
|
|
29
|
+
export declare function createWebStandardMiddleware(engineOrConfig: TollBoothEngine | WebStandardMiddlewareConfig, upstreamArg?: string): WebStandardHandler;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a `WebStandardHandler` that serves invoice status as JSON or HTML.
|
|
32
|
+
*
|
|
33
|
+
* Extracts the payment hash from the last URL path segment and expects
|
|
34
|
+
* a `?token=...` status lookup secret. When `Accept: text/html` is requested,
|
|
35
|
+
* renders the self-service payment page; otherwise returns JSON with
|
|
36
|
+
* `{ paid, preimage }`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createWebStandardInvoiceStatusHandler(deps: InvoiceStatusDeps): WebStandardHandler;
|
|
39
|
+
/**
|
|
40
|
+
* Returns a `WebStandardHandler` that creates a new Lightning invoice.
|
|
41
|
+
*
|
|
42
|
+
* Parses the JSON body for an optional `amountSats` field, delegates
|
|
43
|
+
* to the core `handleCreateInvoice`, and returns the result.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createWebStandardCreateInvoiceHandler(deps: CreateInvoiceDeps): WebStandardHandler;
|
|
46
|
+
/**
|
|
47
|
+
* Returns a `WebStandardHandler` that pays a Lightning invoice via NWC.
|
|
48
|
+
*
|
|
49
|
+
* Expects JSON body with `{ nwcUri, bolt11, paymentHash, statusToken }`.
|
|
50
|
+
* Returns the payment preimage on success.
|
|
51
|
+
*/
|
|
52
|
+
export declare function createWebStandardNwcHandler(deps: NwcPayDeps): WebStandardHandler;
|
|
53
|
+
/**
|
|
54
|
+
* Returns a `WebStandardHandler` that redeems a Cashu token as payment.
|
|
55
|
+
*
|
|
56
|
+
* Expects JSON body with `{ token, paymentHash, statusToken }`.
|
|
57
|
+
* Uses durable claims and leases to avoid concurrent duplicate redemption.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createWebStandardCashuHandler(deps: CashuRedeemDeps): WebStandardHandler;
|
|
60
|
+
//# sourceMappingURL=web-standard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-standard.d.ts","sourceRoot":"","sources":["../../src/adapters/web-standard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAIlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AAS9D,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAqFpE;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACxC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,CAAA;CACvC;AAED,wBAAgB,2BAA2B,CACzC,cAAc,EAAE,eAAe,GAAG,2BAA2B,EAC7D,WAAW,CAAC,EAAE,MAAM,GACnB,kBAAkB,CAqEpB;AAID;;;;;;;GAOG;AACH,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,iBAAiB,GACtB,kBAAkB,CAwCpB;AAID;;;;;GAKG;AACH,wBAAgB,qCAAqC,CACnD,IAAI,EAAE,iBAAiB,GACtB,kBAAkB,CAiCpB;AAID;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,UAAU,GAAG,kBAAkB,CAmBhF;AAID;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB,CA4BvF"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { handleCreateInvoice } from '../core/create-invoice.js';
|
|
2
|
+
import { handleInvoiceStatus, renderInvoiceStatusHtml } from '../core/invoice-status.js';
|
|
3
|
+
import { handleNwcPay } from '../core/nwc-pay.js';
|
|
4
|
+
import { handleCashuRedeem } from '../core/cashu-redeem.js';
|
|
5
|
+
import { PAYMENT_HASH_RE } from '../core/types.js';
|
|
6
|
+
import { appendVary, applyNoStoreHeaders, stripProxyRequestHeaders, stripProxyResponseHeaders, } from './proxy-headers.js';
|
|
7
|
+
// -- Helpers ------------------------------------------------------------------
|
|
8
|
+
class BodyTooLargeError extends Error {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parses the request body as JSON with a configurable size limit.
|
|
12
|
+
*
|
|
13
|
+
* Checks the `Content-Length` header first for a fast rejection, then reads
|
|
14
|
+
* the body as text and enforces the byte limit before parsing. Returns an
|
|
15
|
+
* empty object on any failure (oversized body, missing body, malformed JSON)
|
|
16
|
+
* so callers behave identically to the previous `.catch(() => ({}))` pattern.
|
|
17
|
+
*
|
|
18
|
+
* @param req - The incoming request.
|
|
19
|
+
* @param maxBytes - Maximum allowed body size in bytes (default: 64 KiB).
|
|
20
|
+
*/
|
|
21
|
+
async function safeParseJson(req, maxBytes = 65_536) {
|
|
22
|
+
// Quick rejection via Content-Length header — avoids reading the body at all
|
|
23
|
+
const contentLength = req.headers.get('content-length');
|
|
24
|
+
const parsedLength = contentLength === null ? NaN : parseInt(contentLength, 10);
|
|
25
|
+
if (Number.isFinite(parsedLength) && parsedLength > maxBytes) {
|
|
26
|
+
return { ok: false };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const text = await readBodyTextWithinLimit(req, maxBytes);
|
|
30
|
+
if (!text.trim())
|
|
31
|
+
return { ok: true, value: {} };
|
|
32
|
+
return { ok: true, value: JSON.parse(text) };
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { ok: false };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function proxyUpstream(upstream, req, timeoutMs = 30_000) {
|
|
39
|
+
const url = new URL(req.url);
|
|
40
|
+
const target = `${upstream}${url.pathname}${url.search}`;
|
|
41
|
+
const headers = stripProxyRequestHeaders(req.headers);
|
|
42
|
+
const init = {
|
|
43
|
+
method: req.method,
|
|
44
|
+
headers,
|
|
45
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
46
|
+
duplex: 'half',
|
|
47
|
+
};
|
|
48
|
+
// Forward body for non-GET/HEAD requests
|
|
49
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
50
|
+
init.body = req.body;
|
|
51
|
+
}
|
|
52
|
+
return fetch(target, init);
|
|
53
|
+
}
|
|
54
|
+
async function readBodyTextWithinLimit(req, maxBytes) {
|
|
55
|
+
if (!req.body)
|
|
56
|
+
return '';
|
|
57
|
+
const reader = req.body.getReader();
|
|
58
|
+
const decoder = new TextDecoder();
|
|
59
|
+
let totalBytes = 0;
|
|
60
|
+
let text = '';
|
|
61
|
+
try {
|
|
62
|
+
while (true) {
|
|
63
|
+
const { done, value } = await reader.read();
|
|
64
|
+
if (done)
|
|
65
|
+
break;
|
|
66
|
+
totalBytes += value.byteLength;
|
|
67
|
+
if (totalBytes > maxBytes) {
|
|
68
|
+
await reader.cancel();
|
|
69
|
+
throw new BodyTooLargeError('Request body exceeded limit');
|
|
70
|
+
}
|
|
71
|
+
text += decoder.decode(value, { stream: true });
|
|
72
|
+
}
|
|
73
|
+
return text + decoder.decode();
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
reader.releaseLock();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function createWebStandardMiddleware(engineOrConfig, upstreamArg) {
|
|
80
|
+
// Support both old (engine, upstream) and new (config) signatures
|
|
81
|
+
const config = typeof upstreamArg === 'string'
|
|
82
|
+
? { engine: engineOrConfig, upstream: upstreamArg }
|
|
83
|
+
: engineOrConfig;
|
|
84
|
+
const engine = config.engine;
|
|
85
|
+
const upstreamBase = config.upstream.replace(/\/$/, '');
|
|
86
|
+
const extraHeaders = config.responseHeaders ?? {};
|
|
87
|
+
const upstreamTimeout = config.upstreamTimeout ?? 30_000;
|
|
88
|
+
// Fail closed when free-tier is enabled but all requests would collapse
|
|
89
|
+
// into one shared bucket.
|
|
90
|
+
if (engine.freeTier && !config.trustProxy && !config.getClientIp) {
|
|
91
|
+
throw new Error('freeTier requires either trustProxy: true or getClientIp for the web-standard adapter');
|
|
92
|
+
}
|
|
93
|
+
return async (req) => {
|
|
94
|
+
const url = new URL(req.url);
|
|
95
|
+
const ip = config.getClientIp
|
|
96
|
+
? config.getClientIp(req)
|
|
97
|
+
: config.trustProxy
|
|
98
|
+
? req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? req.headers.get('x-real-ip') ?? 'unknown'
|
|
99
|
+
: 'unknown';
|
|
100
|
+
const headers = Object.fromEntries(req.headers.entries());
|
|
101
|
+
const result = await engine.handle({
|
|
102
|
+
method: req.method,
|
|
103
|
+
path: url.pathname,
|
|
104
|
+
headers,
|
|
105
|
+
ip,
|
|
106
|
+
body: req.body,
|
|
107
|
+
});
|
|
108
|
+
if (result.action === 'pass' || result.action === 'proxy') {
|
|
109
|
+
try {
|
|
110
|
+
const res = await proxyUpstream(upstreamBase, req, upstreamTimeout);
|
|
111
|
+
const responseHeaders = stripProxyResponseHeaders(res.headers);
|
|
112
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
113
|
+
responseHeaders.set(key, value);
|
|
114
|
+
}
|
|
115
|
+
for (const [key, value] of Object.entries(extraHeaders)) {
|
|
116
|
+
responseHeaders.set(key, value);
|
|
117
|
+
}
|
|
118
|
+
return new Response(res.body, {
|
|
119
|
+
status: res.status,
|
|
120
|
+
statusText: res.statusText,
|
|
121
|
+
headers: responseHeaders,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return Response.json({ error: 'Upstream unavailable' }, { status: 502, headers: new Headers(extraHeaders) });
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// challenge — 402
|
|
129
|
+
const challengeHeaders = new Headers(extraHeaders);
|
|
130
|
+
for (const [key, value] of Object.entries(result.headers)) {
|
|
131
|
+
challengeHeaders.set(key, value);
|
|
132
|
+
}
|
|
133
|
+
applyNoStoreHeaders(challengeHeaders);
|
|
134
|
+
return Response.json(result.body, {
|
|
135
|
+
status: 402,
|
|
136
|
+
headers: challengeHeaders,
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// -- Invoice status handler ---------------------------------------------------
|
|
141
|
+
/**
|
|
142
|
+
* Returns a `WebStandardHandler` that serves invoice status as JSON or HTML.
|
|
143
|
+
*
|
|
144
|
+
* Extracts the payment hash from the last URL path segment and expects
|
|
145
|
+
* a `?token=...` status lookup secret. When `Accept: text/html` is requested,
|
|
146
|
+
* renders the self-service payment page; otherwise returns JSON with
|
|
147
|
+
* `{ paid, preimage }`.
|
|
148
|
+
*/
|
|
149
|
+
export function createWebStandardInvoiceStatusHandler(deps) {
|
|
150
|
+
return async (req) => {
|
|
151
|
+
const url = new URL(req.url);
|
|
152
|
+
const segments = url.pathname.split('/').filter(Boolean);
|
|
153
|
+
const paymentHash = segments[segments.length - 1] ?? '';
|
|
154
|
+
if (!PAYMENT_HASH_RE.test(paymentHash)) {
|
|
155
|
+
return Response.json({ error: 'Invalid payment hash' }, { status: 400 });
|
|
156
|
+
}
|
|
157
|
+
const statusToken = url.searchParams.get('token') ?? undefined;
|
|
158
|
+
const accept = req.headers.get('accept') ?? '';
|
|
159
|
+
try {
|
|
160
|
+
if (accept.includes('text/html')) {
|
|
161
|
+
const { html, status } = await renderInvoiceStatusHtml(deps, paymentHash, statusToken);
|
|
162
|
+
const headers = appendVary(applyNoStoreHeaders(new Headers()), 'Accept');
|
|
163
|
+
headers.set('Content-Type', 'text/html; charset=utf-8');
|
|
164
|
+
return new Response(html, {
|
|
165
|
+
status,
|
|
166
|
+
headers,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const result = await handleInvoiceStatus(deps, paymentHash, statusToken);
|
|
170
|
+
if (!result.found) {
|
|
171
|
+
return Response.json({ error: 'Invoice not found' }, { status: 404, headers: appendVary(applyNoStoreHeaders(new Headers()), 'Accept') });
|
|
172
|
+
}
|
|
173
|
+
return Response.json({ paid: result.paid, preimage: result.preimage, token_suffix: result.tokenSuffix }, { headers: appendVary(applyNoStoreHeaders(new Headers()), 'Accept') });
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return Response.json({ error: 'Failed to check invoice status' }, { status: 502, headers: appendVary(applyNoStoreHeaders(new Headers()), 'Accept') });
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// -- Create invoice handler ---------------------------------------------------
|
|
181
|
+
/**
|
|
182
|
+
* Returns a `WebStandardHandler` that creates a new Lightning invoice.
|
|
183
|
+
*
|
|
184
|
+
* Parses the JSON body for an optional `amountSats` field, delegates
|
|
185
|
+
* to the core `handleCreateInvoice`, and returns the result.
|
|
186
|
+
*/
|
|
187
|
+
export function createWebStandardCreateInvoiceHandler(deps) {
|
|
188
|
+
return async (req) => {
|
|
189
|
+
const parsed = await safeParseJson(req);
|
|
190
|
+
if (!parsed.ok) {
|
|
191
|
+
return Response.json({ error: 'Invalid JSON body' }, { status: 400, headers: applyNoStoreHeaders(new Headers()) });
|
|
192
|
+
}
|
|
193
|
+
const result = await handleCreateInvoice(deps, parsed.value);
|
|
194
|
+
if (!result.success) {
|
|
195
|
+
return Response.json({ error: result.error, tiers: result.tiers }, { status: 400, headers: applyNoStoreHeaders(new Headers()) });
|
|
196
|
+
}
|
|
197
|
+
const d = result.data;
|
|
198
|
+
return Response.json({
|
|
199
|
+
bolt11: d.bolt11,
|
|
200
|
+
payment_hash: d.paymentHash,
|
|
201
|
+
payment_url: d.paymentUrl,
|
|
202
|
+
amount_sats: d.amountSats,
|
|
203
|
+
credit_sats: d.creditSats,
|
|
204
|
+
macaroon: d.macaroon,
|
|
205
|
+
qr_svg: d.qrSvg,
|
|
206
|
+
}, { headers: applyNoStoreHeaders(new Headers()) });
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// -- NWC handler --------------------------------------------------------------
|
|
210
|
+
/**
|
|
211
|
+
* Returns a `WebStandardHandler` that pays a Lightning invoice via NWC.
|
|
212
|
+
*
|
|
213
|
+
* Expects JSON body with `{ nwcUri, bolt11, paymentHash, statusToken }`.
|
|
214
|
+
* Returns the payment preimage on success.
|
|
215
|
+
*/
|
|
216
|
+
export function createWebStandardNwcHandler(deps) {
|
|
217
|
+
return async (req) => {
|
|
218
|
+
const parsed = await safeParseJson(req);
|
|
219
|
+
if (!parsed.ok) {
|
|
220
|
+
return Response.json({ error: 'Invalid JSON body' }, { status: 400, headers: applyNoStoreHeaders(new Headers()) });
|
|
221
|
+
}
|
|
222
|
+
const result = await handleNwcPay(deps, parsed.value);
|
|
223
|
+
if (result.success) {
|
|
224
|
+
return Response.json({ preimage: result.preimage }, { headers: applyNoStoreHeaders(new Headers()) });
|
|
225
|
+
}
|
|
226
|
+
return Response.json({ error: result.error }, { status: result.status, headers: applyNoStoreHeaders(new Headers()) });
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
// -- Cashu handler ------------------------------------------------------------
|
|
230
|
+
/**
|
|
231
|
+
* Returns a `WebStandardHandler` that redeems a Cashu token as payment.
|
|
232
|
+
*
|
|
233
|
+
* Expects JSON body with `{ token, paymentHash, statusToken }`.
|
|
234
|
+
* Uses durable claims and leases to avoid concurrent duplicate redemption.
|
|
235
|
+
*/
|
|
236
|
+
export function createWebStandardCashuHandler(deps) {
|
|
237
|
+
return async (req) => {
|
|
238
|
+
const parsed = await safeParseJson(req);
|
|
239
|
+
if (!parsed.ok) {
|
|
240
|
+
return Response.json({ error: 'Invalid JSON body' }, { status: 400, headers: applyNoStoreHeaders(new Headers()) });
|
|
241
|
+
}
|
|
242
|
+
const result = await handleCashuRedeem(deps, parsed.value);
|
|
243
|
+
if (result.success) {
|
|
244
|
+
return Response.json({ credited: result.credited, token_suffix: result.tokenSuffix }, { headers: applyNoStoreHeaders(new Headers()) });
|
|
245
|
+
}
|
|
246
|
+
if ('state' in result) {
|
|
247
|
+
return Response.json({ state: result.state, retryAfterMs: result.retryAfterMs }, { status: 202, headers: applyNoStoreHeaders(new Headers()) });
|
|
248
|
+
}
|
|
249
|
+
return Response.json({ error: result.error }, { status: result.status, headers: applyNoStoreHeaders(new Headers()) });
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=web-standard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-standard.js","sourceRoot":"","sources":["../../src/adapters/web-standard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LightningBackend } from '../types.js';
|
|
2
|
+
export interface AlbyConfig {
|
|
3
|
+
/** NWC connection URI: nostr+walletconnect://pubkey?relay=wss://...&secret=... */
|
|
4
|
+
nwcUrl: string;
|
|
5
|
+
/** Request timeout in ms (default: 30000) */
|
|
6
|
+
timeout?: number;
|
|
7
|
+
/**
|
|
8
|
+
* Opt into the current insecure JSON-over-relay transport.
|
|
9
|
+
* This exists only for local testing and trusted relay experiments.
|
|
10
|
+
*/
|
|
11
|
+
allowInsecureRelay?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Lightning backend adapter for Alby / Nostr Wallet Connect (NWC).
|
|
15
|
+
*
|
|
16
|
+
* This transport is intentionally disabled by default because it sends
|
|
17
|
+
* unsigned, unencrypted JSON directly to the relay and therefore cannot
|
|
18
|
+
* authenticate responses. Pass `allowInsecureRelay: true` only for local
|
|
19
|
+
* testing or a fully trusted relay shim.
|
|
20
|
+
*
|
|
21
|
+
* The `ws` package is imported dynamically so it only needs to be
|
|
22
|
+
* installed when this backend is actually used.
|
|
23
|
+
*/
|
|
24
|
+
export declare function albyBackend(config: AlbyConfig): LightningBackend;
|
|
25
|
+
//# sourceMappingURL=alby.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alby.d.ts","sourceRoot":"","sources":["../../src/backends/alby.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAA;AAE3E,MAAM,WAAW,UAAU;IACzB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAkCD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAmEhE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
function parseNwcUrl(nwcUrl) {
|
|
2
|
+
// Replace the custom scheme so URL parser can handle it
|
|
3
|
+
const url = new URL(nwcUrl.replace('nostr+walletconnect://', 'https://'));
|
|
4
|
+
const pubkey = url.hostname;
|
|
5
|
+
const relay = url.searchParams.get('relay');
|
|
6
|
+
const secret = url.searchParams.get('secret');
|
|
7
|
+
if (!relay)
|
|
8
|
+
throw new Error('NWC URL missing required "relay" parameter');
|
|
9
|
+
if (!secret)
|
|
10
|
+
throw new Error('NWC URL missing required "secret" parameter');
|
|
11
|
+
return { pubkey, relay, secret };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Lightning backend adapter for Alby / Nostr Wallet Connect (NWC).
|
|
15
|
+
*
|
|
16
|
+
* This transport is intentionally disabled by default because it sends
|
|
17
|
+
* unsigned, unencrypted JSON directly to the relay and therefore cannot
|
|
18
|
+
* authenticate responses. Pass `allowInsecureRelay: true` only for local
|
|
19
|
+
* testing or a fully trusted relay shim.
|
|
20
|
+
*
|
|
21
|
+
* The `ws` package is imported dynamically so it only needs to be
|
|
22
|
+
* installed when this backend is actually used.
|
|
23
|
+
*/
|
|
24
|
+
export function albyBackend(config) {
|
|
25
|
+
if (!config.allowInsecureRelay) {
|
|
26
|
+
throw new Error('albyBackend is disabled by default because its JSON relay transport is unauthenticated; pass allowInsecureRelay: true only for local testing');
|
|
27
|
+
}
|
|
28
|
+
const params = parseNwcUrl(config.nwcUrl);
|
|
29
|
+
const timeoutMs = config.timeout ?? 30_000;
|
|
30
|
+
const MAX_CACHED_INVOICES = 10_000;
|
|
31
|
+
const invoiceMap = new Map();
|
|
32
|
+
return {
|
|
33
|
+
async createInvoice(amountSats, memo) {
|
|
34
|
+
const result = await sendNwcRequest(params, timeoutMs, 'make_invoice', {
|
|
35
|
+
amount: amountSats * 1000, // NWC uses millisats
|
|
36
|
+
description: memo,
|
|
37
|
+
});
|
|
38
|
+
const bolt11 = getStringField(result, 'invoice') ?? getStringField(result, 'bolt11');
|
|
39
|
+
const paymentHash = getStringField(result, 'payment_hash') ?? getStringField(result, 'paymentHash');
|
|
40
|
+
if (!bolt11 || !paymentHash) {
|
|
41
|
+
throw new Error('NWC response missing invoice or payment_hash');
|
|
42
|
+
}
|
|
43
|
+
// Evict oldest entries when cache is full (Map preserves insertion order)
|
|
44
|
+
if (invoiceMap.size >= MAX_CACHED_INVOICES) {
|
|
45
|
+
const oldest = invoiceMap.keys().next().value;
|
|
46
|
+
invoiceMap.delete(oldest);
|
|
47
|
+
}
|
|
48
|
+
invoiceMap.set(paymentHash, { bolt11, paid: false });
|
|
49
|
+
return { bolt11, paymentHash };
|
|
50
|
+
},
|
|
51
|
+
async checkInvoice(paymentHash) {
|
|
52
|
+
const entry = invoiceMap.get(paymentHash);
|
|
53
|
+
if (!entry)
|
|
54
|
+
return { paid: false };
|
|
55
|
+
if (entry.paid) {
|
|
56
|
+
return { paid: true, preimage: entry.preimage };
|
|
57
|
+
}
|
|
58
|
+
const result = await sendNwcRequest(params, timeoutMs, 'lookup_invoice', { payment_hash: paymentHash });
|
|
59
|
+
const lookupRecord = result;
|
|
60
|
+
const paid = isPaid(result);
|
|
61
|
+
const preimage = paid
|
|
62
|
+
? getStringField(lookupRecord, 'payment_preimage') ?? getStringField(lookupRecord, 'preimage')
|
|
63
|
+
: undefined;
|
|
64
|
+
const bolt11 = getStringField(lookupRecord, 'invoice') ?? getStringField(lookupRecord, 'bolt11') ?? entry.bolt11;
|
|
65
|
+
invoiceMap.set(paymentHash, { bolt11, paid, preimage });
|
|
66
|
+
return { paid, preimage };
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async function sendNwcRequest(params, timeoutMs, method, requestParams) {
|
|
71
|
+
const { default: WebSocket } = await import('ws');
|
|
72
|
+
return new Promise((resolve, reject) => {
|
|
73
|
+
const ws = new WebSocket(params.relay);
|
|
74
|
+
let settled = false;
|
|
75
|
+
const cleanup = () => {
|
|
76
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
77
|
+
ws.close();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const finish = (fn) => {
|
|
81
|
+
if (settled)
|
|
82
|
+
return;
|
|
83
|
+
settled = true;
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
cleanup();
|
|
86
|
+
fn();
|
|
87
|
+
};
|
|
88
|
+
const timer = setTimeout(() => {
|
|
89
|
+
finish(() => reject(new Error(`NWC ${method} timed out`)));
|
|
90
|
+
}, timeoutMs);
|
|
91
|
+
ws.on('open', () => {
|
|
92
|
+
ws.send(JSON.stringify({
|
|
93
|
+
method,
|
|
94
|
+
params: requestParams,
|
|
95
|
+
}));
|
|
96
|
+
});
|
|
97
|
+
ws.on('message', (data) => {
|
|
98
|
+
finish(() => {
|
|
99
|
+
try {
|
|
100
|
+
const response = JSON.parse(data.toString());
|
|
101
|
+
const error = response.error;
|
|
102
|
+
if (error) {
|
|
103
|
+
reject(new Error(`NWC ${method} failed: ${formatError(error)}`));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
resolve((response.result ?? response));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
reject(new Error(`Failed to parse NWC response: ${err}`));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
ws.on('error', (err) => {
|
|
114
|
+
finish(() => reject(new Error(`NWC WebSocket error: ${err.message}`)));
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
function getStringField(record, key) {
|
|
119
|
+
const value = record[key];
|
|
120
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
121
|
+
}
|
|
122
|
+
function isPaid(result) {
|
|
123
|
+
return result.paid === true ||
|
|
124
|
+
result.settled === true ||
|
|
125
|
+
result.state === 'paid' ||
|
|
126
|
+
result.state === 'settled' ||
|
|
127
|
+
result.settled_at !== undefined;
|
|
128
|
+
}
|
|
129
|
+
function formatError(error) {
|
|
130
|
+
if (typeof error === 'string')
|
|
131
|
+
return error;
|
|
132
|
+
if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
|
|
133
|
+
return error.message;
|
|
134
|
+
}
|
|
135
|
+
return 'unknown error';
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=alby.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alby.js","sourceRoot":"","sources":["../../src/backends/alby.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LightningBackend } from '../types.js';
|
|
2
|
+
export interface ClnConfig {
|
|
3
|
+
/** CLN REST API URL (e.g. https://localhost:3010). */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Rune token for authentication. */
|
|
6
|
+
rune: string;
|
|
7
|
+
/** Request timeout in ms (default: 30000) */
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Lightning backend adapter for Core Lightning's REST API (clnrest).
|
|
12
|
+
*
|
|
13
|
+
* Uses the `/v1/invoice` and `/v1/listinvoices` endpoints.
|
|
14
|
+
* Authentication via `Rune` header.
|
|
15
|
+
*
|
|
16
|
+
* CLN requires a unique `label` per invoice — we generate one using
|
|
17
|
+
* a `toll-booth-` prefix and a timestamp + random suffix.
|
|
18
|
+
*
|
|
19
|
+
* @see https://docs.corelightning.org/docs/rest
|
|
20
|
+
*/
|
|
21
|
+
export declare function clnBackend(config: ClnConfig): LightningBackend;
|
|
22
|
+
//# sourceMappingURL=cln.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cln.d.ts","sourceRoot":"","sources":["../../src/backends/cln.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAA;AAE3E,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,GAAG,EAAE,MAAM,CAAA;IACX,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,gBAAgB,CA0D9D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightning backend adapter for Core Lightning's REST API (clnrest).
|
|
3
|
+
*
|
|
4
|
+
* Uses the `/v1/invoice` and `/v1/listinvoices` endpoints.
|
|
5
|
+
* Authentication via `Rune` header.
|
|
6
|
+
*
|
|
7
|
+
* CLN requires a unique `label` per invoice — we generate one using
|
|
8
|
+
* a `toll-booth-` prefix and a timestamp + random suffix.
|
|
9
|
+
*
|
|
10
|
+
* @see https://docs.corelightning.org/docs/rest
|
|
11
|
+
*/
|
|
12
|
+
export function clnBackend(config) {
|
|
13
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
14
|
+
const timeoutMs = config.timeout ?? 30_000;
|
|
15
|
+
const headers = {
|
|
16
|
+
'Rune': config.rune,
|
|
17
|
+
};
|
|
18
|
+
return {
|
|
19
|
+
async createInvoice(amountSats, memo) {
|
|
20
|
+
const label = `toll-booth-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
21
|
+
const res = await fetch(`${baseUrl}/v1/invoice`, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
amount_msat: amountSats * 1000,
|
|
26
|
+
label,
|
|
27
|
+
description: memo ?? 'toll-booth payment',
|
|
28
|
+
}),
|
|
29
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const text = await res.text().catch(() => '');
|
|
33
|
+
throw new Error(`CLN createInvoice failed (${res.status}): ${text}`);
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
return { bolt11: data.bolt11, paymentHash: data.payment_hash };
|
|
37
|
+
},
|
|
38
|
+
async checkInvoice(paymentHash) {
|
|
39
|
+
const res = await fetch(`${baseUrl}/v1/listinvoices?payment_hash=${paymentHash}`, { headers, signal: AbortSignal.timeout(timeoutMs) });
|
|
40
|
+
if (!res.ok) {
|
|
41
|
+
const text = await res.text().catch(() => '');
|
|
42
|
+
throw new Error(`CLN checkInvoice failed (${res.status}): ${text}`);
|
|
43
|
+
}
|
|
44
|
+
const data = await res.json();
|
|
45
|
+
const inv = data.invoices[0];
|
|
46
|
+
if (!inv || inv.status !== 'paid')
|
|
47
|
+
return { paid: false };
|
|
48
|
+
return {
|
|
49
|
+
paid: true,
|
|
50
|
+
preimage: inv.payment_preimage,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=cln.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cln.js","sourceRoot":"","sources":["../../src/backends/cln.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAA;IAC1C,MAAM,OAAO,GAA2B;QACtC,MAAM,EAAE,MAAM,CAAC,IAAI;KACpB,CAAA;IAED,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,IAAa;YACnD,MAAM,KAAK,GAAG,cAAc,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;YAElF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,aAAa,EAAE;gBAC/C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC3D,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,WAAW,EAAE,UAAU,GAAG,IAAI;oBAC9B,KAAK;oBACL,WAAW,EAAE,IAAI,IAAI,oBAAoB;iBAC1C,CAAC;gBACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAA;YAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;gBAC7C,MAAM,IAAI,KAAK,CAAC,6BAA6B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YACtE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA8C,CAAA;YACzE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,CAAA;QAChE,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,WAAmB;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CACrB,GAAG,OAAO,iCAAiC,WAAW,EAAE,EACxD,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CACpD,CAAA;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;gBAC7C,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YACrE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAK1B,CAAA;YAED,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YAC5B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YAEzD,OAAO;gBACL,IAAI,EAAE,IAAI;gBACV,QAAQ,EAAE,GAAG,CAAC,gBAAgB;aAC/B,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { LightningBackend } from '../types.js';
|
|
2
|
+
export interface LNbitsConfig {
|
|
3
|
+
/** LNbits instance URL (e.g. https://legend.lnbits.com) */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Invoice/read API key from the LNbits wallet */
|
|
6
|
+
apiKey: string;
|
|
7
|
+
/** Request timeout in ms (default: 30000) */
|
|
8
|
+
timeout?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Lightning backend adapter for LNbits.
|
|
12
|
+
*
|
|
13
|
+
* Uses the LNbits Payments API with `X-Api-Key` header authentication.
|
|
14
|
+
* Works with any LNbits instance — self-hosted or hosted (legend.lnbits.com).
|
|
15
|
+
*
|
|
16
|
+
* LNbits abstracts over multiple funding sources (LND, CLN, Phoenixd,
|
|
17
|
+
* LndHub, etc.), so this backend supports any node type that LNbits
|
|
18
|
+
* connects to.
|
|
19
|
+
*
|
|
20
|
+
* @see https://lnbits.com/
|
|
21
|
+
*/
|
|
22
|
+
export declare function lnbitsBackend(config: LNbitsConfig): LightningBackend;
|
|
23
|
+
//# sourceMappingURL=lnbits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lnbits.d.ts","sourceRoot":"","sources":["../../src/backends/lnbits.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAA;AAE3E,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAA;IACX,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CAkDpE"}
|