@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,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightning backend adapter for LNbits.
|
|
3
|
+
*
|
|
4
|
+
* Uses the LNbits Payments API with `X-Api-Key` header authentication.
|
|
5
|
+
* Works with any LNbits instance — self-hosted or hosted (legend.lnbits.com).
|
|
6
|
+
*
|
|
7
|
+
* LNbits abstracts over multiple funding sources (LND, CLN, Phoenixd,
|
|
8
|
+
* LndHub, etc.), so this backend supports any node type that LNbits
|
|
9
|
+
* connects to.
|
|
10
|
+
*
|
|
11
|
+
* @see https://lnbits.com/
|
|
12
|
+
*/
|
|
13
|
+
export function lnbitsBackend(config) {
|
|
14
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
15
|
+
const timeoutMs = config.timeout ?? 30_000;
|
|
16
|
+
const headers = {
|
|
17
|
+
'X-Api-Key': config.apiKey,
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
async createInvoice(amountSats, memo) {
|
|
22
|
+
const res = await fetch(`${baseUrl}/api/v1/payments`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers,
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
out: false,
|
|
27
|
+
amount: amountSats,
|
|
28
|
+
memo: memo ?? 'toll-booth payment',
|
|
29
|
+
}),
|
|
30
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok) {
|
|
33
|
+
const text = await res.text().catch(() => '');
|
|
34
|
+
throw new Error(`LNbits createInvoice failed (${res.status}): ${text}`);
|
|
35
|
+
}
|
|
36
|
+
const data = await res.json();
|
|
37
|
+
return { bolt11: data.payment_request, paymentHash: data.payment_hash };
|
|
38
|
+
},
|
|
39
|
+
async checkInvoice(paymentHash) {
|
|
40
|
+
const res = await fetch(`${baseUrl}/api/v1/payments/${paymentHash}`, {
|
|
41
|
+
headers: { 'X-Api-Key': config.apiKey },
|
|
42
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
43
|
+
});
|
|
44
|
+
if (res.status === 404)
|
|
45
|
+
return { paid: false };
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const text = await res.text().catch(() => '');
|
|
48
|
+
throw new Error(`LNbits checkInvoice failed (${res.status}): ${text}`);
|
|
49
|
+
}
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
return {
|
|
52
|
+
paid: data.paid,
|
|
53
|
+
preimage: data.paid ? data.preimage : undefined,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=lnbits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lnbits.js","sourceRoot":"","sources":["../../src/backends/lnbits.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAAC,MAAoB;IAChD,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,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,cAAc,EAAE,kBAAkB;KACnC,CAAA;IAED,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,IAAa;YACnD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,kBAAkB,EAAE;gBACpD,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,GAAG,EAAE,KAAK;oBACV,MAAM,EAAE,UAAU;oBAClB,IAAI,EAAE,IAAI,IAAI,oBAAoB;iBACnC,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,gCAAgC,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YACzE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuD,CAAA;YAClF,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,EAAE,CAAA;QACzE,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,WAAmB;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,oBAAoB,WAAW,EAAE,EAAE;gBACnE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;gBACvC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAA;YAEF,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YAE9C,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,+BAA+B,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YACxE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA0C,CAAA;YACrE,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;aAChD,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { LightningBackend } from '../types.js';
|
|
2
|
+
export interface LndConfig {
|
|
3
|
+
/** LND REST API URL (e.g. https://localhost:8080) */
|
|
4
|
+
url: string;
|
|
5
|
+
/** Admin macaroon as hex string */
|
|
6
|
+
macaroon?: string;
|
|
7
|
+
/** Path to admin.macaroon file (alternative to hex) */
|
|
8
|
+
macaroonPath?: string;
|
|
9
|
+
/** Request timeout in ms (default: 30000) */
|
|
10
|
+
timeout?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Lightning backend adapter for LND's REST API.
|
|
14
|
+
*
|
|
15
|
+
* Authenticates via the `Grpc-Metadata-macaroon` header using a hex-encoded
|
|
16
|
+
* admin macaroon. The macaroon can be provided directly or read from a file.
|
|
17
|
+
*
|
|
18
|
+
* @see https://lightning.engineering/api-docs/api/lnd/
|
|
19
|
+
*/
|
|
20
|
+
export declare function lndBackend(config: LndConfig): LightningBackend;
|
|
21
|
+
//# sourceMappingURL=lnd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lnd.d.ts","sourceRoot":"","sources":["../../src/backends/lnd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAA;AAG3E,MAAM,WAAW,SAAS;IACxB,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAA;IACX,mCAAmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,gBAAgB,CAsD9D"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Lightning backend adapter for LND's REST API.
|
|
4
|
+
*
|
|
5
|
+
* Authenticates via the `Grpc-Metadata-macaroon` header using a hex-encoded
|
|
6
|
+
* admin macaroon. The macaroon can be provided directly or read from a file.
|
|
7
|
+
*
|
|
8
|
+
* @see https://lightning.engineering/api-docs/api/lnd/
|
|
9
|
+
*/
|
|
10
|
+
export function lndBackend(config) {
|
|
11
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
12
|
+
const timeoutMs = config.timeout ?? 30_000;
|
|
13
|
+
let macaroonHex;
|
|
14
|
+
if (config.macaroon) {
|
|
15
|
+
macaroonHex = config.macaroon;
|
|
16
|
+
}
|
|
17
|
+
else if (config.macaroonPath) {
|
|
18
|
+
macaroonHex = readFileSync(config.macaroonPath).toString('hex');
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
throw new Error('LND backend requires either macaroon (hex) or macaroonPath');
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
async createInvoice(amountSats, memo) {
|
|
25
|
+
const res = await fetch(`${baseUrl}/v1/invoices`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'Grpc-Metadata-macaroon': macaroonHex,
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify({ value: String(amountSats), memo: memo ?? '' }),
|
|
32
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const text = await res.text().catch(() => '');
|
|
36
|
+
throw new Error(`LND createinvoice failed (${res.status}): ${text}`);
|
|
37
|
+
}
|
|
38
|
+
const data = await res.json();
|
|
39
|
+
const paymentHash = Buffer.from(data.r_hash, 'base64').toString('hex');
|
|
40
|
+
return { bolt11: data.payment_request, paymentHash };
|
|
41
|
+
},
|
|
42
|
+
async checkInvoice(paymentHash) {
|
|
43
|
+
const res = await fetch(`${baseUrl}/v1/invoice/${paymentHash}`, {
|
|
44
|
+
headers: { 'Grpc-Metadata-macaroon': macaroonHex },
|
|
45
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
return { paid: false };
|
|
49
|
+
const data = await res.json();
|
|
50
|
+
return {
|
|
51
|
+
paid: data.settled,
|
|
52
|
+
preimage: data.settled && data.r_preimage
|
|
53
|
+
? Buffer.from(data.r_preimage, 'base64').toString('hex')
|
|
54
|
+
: undefined,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=lnd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lnd.js","sourceRoot":"","sources":["../../src/backends/lnd.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAatC;;;;;;;GAOG;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;IAE1C,IAAI,WAAmB,CAAA;IACvB,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAA;IAC/B,CAAC;SAAM,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QAC/B,WAAW,GAAG,YAAY,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IACjE,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAA;IAC/E,CAAC;IAED,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,IAAa;YACnD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;gBAChD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,wBAAwB,EAAE,WAAW;oBACrC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;gBACrE,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,EAAiD,CAAA;YAC5E,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YAEtE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE,CAAA;QACtD,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,WAAmB;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,eAAe,WAAW,EAAE,EAAE;gBAC9D,OAAO,EAAE,EAAE,wBAAwB,EAAE,WAAW,EAAE;gBAClD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAA;YAEF,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YAEnC,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA+C,CAAA;YAE1E,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,OAAO;gBAClB,QAAQ,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,UAAU;oBACvC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;oBACxD,CAAC,CAAC,SAAS;aACd,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LightningBackend } from '../types.js';
|
|
2
|
+
export interface PhoenixdConfig {
|
|
3
|
+
url: string;
|
|
4
|
+
password: string;
|
|
5
|
+
/** Request timeout in ms (default: 30000) */
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Lightning backend adapter for Phoenixd's HTTP API.
|
|
10
|
+
*
|
|
11
|
+
* Phoenixd uses HTTP Basic auth with an empty username — the Authorization
|
|
12
|
+
* header encodes `:password` (note the leading colon) in base64.
|
|
13
|
+
* The createinvoice endpoint expects an `application/x-www-form-urlencoded`
|
|
14
|
+
* POST body, not JSON.
|
|
15
|
+
*
|
|
16
|
+
* @see https://phoenix.acinq.co/server/api
|
|
17
|
+
*/
|
|
18
|
+
export declare function phoenixdBackend(config: PhoenixdConfig): LightningBackend;
|
|
19
|
+
//# sourceMappingURL=phoenixd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phoenixd.d.ts","sourceRoot":"","sources":["../../src/backends/phoenixd.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAA0B,MAAM,aAAa,CAAA;AAE3E,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,gBAAgB,CAqDxE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightning backend adapter for Phoenixd's HTTP API.
|
|
3
|
+
*
|
|
4
|
+
* Phoenixd uses HTTP Basic auth with an empty username — the Authorization
|
|
5
|
+
* header encodes `:password` (note the leading colon) in base64.
|
|
6
|
+
* The createinvoice endpoint expects an `application/x-www-form-urlencoded`
|
|
7
|
+
* POST body, not JSON.
|
|
8
|
+
*
|
|
9
|
+
* @see https://phoenix.acinq.co/server/api
|
|
10
|
+
*/
|
|
11
|
+
export function phoenixdBackend(config) {
|
|
12
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
13
|
+
const authHeader = 'Basic ' + Buffer.from(`:${config.password}`).toString('base64');
|
|
14
|
+
const timeoutMs = config.timeout ?? 30_000;
|
|
15
|
+
return {
|
|
16
|
+
async createInvoice(amountSats, memo) {
|
|
17
|
+
const body = new URLSearchParams();
|
|
18
|
+
body.set('amountSat', String(amountSats));
|
|
19
|
+
if (memo)
|
|
20
|
+
body.set('description', memo);
|
|
21
|
+
body.set('externalId', '');
|
|
22
|
+
const res = await fetch(`${baseUrl}/createinvoice`, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: {
|
|
25
|
+
'Authorization': authHeader,
|
|
26
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
27
|
+
},
|
|
28
|
+
body,
|
|
29
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
30
|
+
});
|
|
31
|
+
if (!res.ok) {
|
|
32
|
+
const text = await res.text().catch(() => '');
|
|
33
|
+
throw new Error(`Phoenixd createinvoice failed (${res.status}): ${text}`);
|
|
34
|
+
}
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
return { bolt11: data.serialized, paymentHash: data.paymentHash };
|
|
37
|
+
},
|
|
38
|
+
async checkInvoice(paymentHash) {
|
|
39
|
+
const res = await fetch(`${baseUrl}/payments/incoming/${paymentHash}`, {
|
|
40
|
+
headers: { 'Authorization': authHeader },
|
|
41
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
42
|
+
});
|
|
43
|
+
// 404 = invoice not found (normal for unknown hashes)
|
|
44
|
+
if (res.status === 404)
|
|
45
|
+
return { paid: false };
|
|
46
|
+
// Auth failures and server errors must propagate so health checks detect them
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
const text = await res.text().catch(() => '');
|
|
49
|
+
throw new Error(`Phoenixd checkInvoice failed (${res.status}): ${text}`);
|
|
50
|
+
}
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
return {
|
|
53
|
+
paid: data.isPaid,
|
|
54
|
+
preimage: data.isPaid ? data.preimage : undefined,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=phoenixd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phoenixd.js","sourceRoot":"","sources":["../../src/backends/phoenixd.ts"],"names":[],"mappings":"AASA;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC7C,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACnF,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAA;IAE1C,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,IAAa;YACnD,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAA;YAClC,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,CAAA;YACzC,IAAI,IAAI;gBAAE,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;YACvC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;YAE1B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,gBAAgB,EAAE;gBAClD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,eAAe,EAAE,UAAU;oBAC3B,cAAc,EAAE,mCAAmC;iBACpD;gBACD,IAAI;gBACJ,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,kCAAkC,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YAC3E,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAiD,CAAA;YAC5E,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAA;QACnE,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,WAAmB;YACpC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,sBAAsB,WAAW,EAAE,EAAE;gBACrE,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE;gBACxC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAA;YAEF,sDAAsD;YACtD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;YAE9C,8EAA8E;YAC9E,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,iCAAiC,GAAG,CAAC,MAAM,MAAM,IAAI,EAAE,CAAC,CAAA;YAC1E,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA4C,CAAA;YACvE,OAAO;gBACL,IAAI,EAAE,IAAI,CAAC,MAAM;gBACjB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;aAClD,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|
package/dist/booth.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { BoothConfig, EventHandler } from './types.js';
|
|
2
|
+
import type { StorageBackend } from './storage/interface.js';
|
|
3
|
+
import { StatsCollector } from './stats.js';
|
|
4
|
+
export type AdapterType = 'express' | 'web-standard';
|
|
5
|
+
export interface BoothOptions extends BoothConfig {
|
|
6
|
+
adapter: AdapterType;
|
|
7
|
+
storage?: StorageBackend;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Encapsulates the middleware, invoice-status handler, create-invoice handler,
|
|
11
|
+
* and wallet adapter endpoints with shared internal state.
|
|
12
|
+
*
|
|
13
|
+
* The `adapter` option selects the framework integration:
|
|
14
|
+
* - `'express'` — Express middleware and handlers
|
|
15
|
+
* - `'web-standard'` — Web Standards (Request/Response) handlers
|
|
16
|
+
*
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const booth = new Booth({ adapter: 'express', ...config })
|
|
19
|
+
* app.get('/invoice-status/:paymentHash', booth.invoiceStatusHandler)
|
|
20
|
+
* app.post('/create-invoice', booth.createInvoiceHandler)
|
|
21
|
+
* app.use('/*', booth.middleware)
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare class Booth {
|
|
25
|
+
readonly middleware: unknown;
|
|
26
|
+
readonly invoiceStatusHandler: unknown;
|
|
27
|
+
readonly createInvoiceHandler: unknown;
|
|
28
|
+
readonly nwcPayHandler?: unknown;
|
|
29
|
+
readonly cashuRedeemHandler?: unknown;
|
|
30
|
+
/** Aggregate usage statistics. Resets on restart. */
|
|
31
|
+
readonly stats: StatsCollector;
|
|
32
|
+
private readonly storage;
|
|
33
|
+
private readonly engine;
|
|
34
|
+
private readonly rootKey;
|
|
35
|
+
private readonly redeemCashu?;
|
|
36
|
+
private readonly pruneTimer?;
|
|
37
|
+
constructor(config: BoothOptions & EventHandler);
|
|
38
|
+
/** Reset free-tier counters for all IPs. */
|
|
39
|
+
resetFreeTier(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Recover Cashu redemptions that were claimed but never settled (crash recovery).
|
|
42
|
+
* Automatically called on startup when Cashu is enabled. Can also be called
|
|
43
|
+
* manually. This requires an idempotent `redeemCashu` implementation keyed by
|
|
44
|
+
* `paymentHash`. For each pending claim, retries the redeem call:
|
|
45
|
+
* - If recovery lease is acquired: attempts redeem
|
|
46
|
+
* - On success: settles with the credited amount
|
|
47
|
+
* - On failure: leaves the claim pending for the next recovery attempt
|
|
48
|
+
*
|
|
49
|
+
* Returns the number of successfully recovered claims.
|
|
50
|
+
*/
|
|
51
|
+
recoverPendingClaims(redeemFn: (token: string, paymentHash: string) => Promise<number>): Promise<number>;
|
|
52
|
+
close(): void;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=booth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booth.d.ts","sourceRoot":"","sources":["../src/booth.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAM5D,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAmB3C,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,cAAc,CAAA;AAEpD,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,OAAO,EAAE,WAAW,CAAA;IACpB,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,KAAK;IAChB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAA;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAA;IACtC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAA;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAA;IAChC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAErC,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAE9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAyD;IACtF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAgC;gBAEhD,MAAM,EAAE,YAAY,GAAG,YAAY;IAgI/C,4CAA4C;IAC5C,aAAa,IAAI,IAAI;IAIrB;;;;;;;;;;OAUG;IACG,oBAAoB,CACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,GAChE,OAAO,CAAC,MAAM,CAAC;IA6BlB,KAAK,IAAI,IAAI;CAId"}
|
package/dist/booth.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createTollBooth } from './core/toll-booth.js';
|
|
2
|
+
import { sqliteStorage } from './storage/sqlite.js';
|
|
3
|
+
import { StatsCollector } from './stats.js';
|
|
4
|
+
import { randomBytes } from 'node:crypto';
|
|
5
|
+
import { REDEEM_LEASE_MS } from './core/cashu-redeem.js';
|
|
6
|
+
import { createExpressMiddleware, createExpressInvoiceStatusHandler, createExpressCreateInvoiceHandler, createExpressNwcHandler, createExpressCashuHandler, } from './adapters/express.js';
|
|
7
|
+
import { createWebStandardMiddleware, createWebStandardInvoiceStatusHandler, createWebStandardCreateInvoiceHandler, createWebStandardNwcHandler, createWebStandardCashuHandler, } from './adapters/web-standard.js';
|
|
8
|
+
/**
|
|
9
|
+
* Encapsulates the middleware, invoice-status handler, create-invoice handler,
|
|
10
|
+
* and wallet adapter endpoints with shared internal state.
|
|
11
|
+
*
|
|
12
|
+
* The `adapter` option selects the framework integration:
|
|
13
|
+
* - `'express'` — Express middleware and handlers
|
|
14
|
+
* - `'web-standard'` — Web Standards (Request/Response) handlers
|
|
15
|
+
*
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const booth = new Booth({ adapter: 'express', ...config })
|
|
18
|
+
* app.get('/invoice-status/:paymentHash', booth.invoiceStatusHandler)
|
|
19
|
+
* app.post('/create-invoice', booth.createInvoiceHandler)
|
|
20
|
+
* app.use('/*', booth.middleware)
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class Booth {
|
|
24
|
+
middleware;
|
|
25
|
+
invoiceStatusHandler;
|
|
26
|
+
createInvoiceHandler;
|
|
27
|
+
nwcPayHandler;
|
|
28
|
+
cashuRedeemHandler;
|
|
29
|
+
/** Aggregate usage statistics. Resets on restart. */
|
|
30
|
+
stats;
|
|
31
|
+
storage;
|
|
32
|
+
engine;
|
|
33
|
+
rootKey;
|
|
34
|
+
redeemCashu;
|
|
35
|
+
pruneTimer;
|
|
36
|
+
constructor(config) {
|
|
37
|
+
if (!config.backend && !config.redeemCashu) {
|
|
38
|
+
throw new Error('At least one payment method required: provide a Lightning backend, redeemCashu callback, or both');
|
|
39
|
+
}
|
|
40
|
+
const rootKeyInput = config.rootKey ?? randomBytes(32).toString('hex');
|
|
41
|
+
if (!/^[0-9a-fA-F]{64}$/.test(rootKeyInput)) {
|
|
42
|
+
throw new Error('rootKey must be exactly 64 hex characters (32 bytes)');
|
|
43
|
+
}
|
|
44
|
+
this.rootKey = rootKeyInput;
|
|
45
|
+
if (config.storage && config.dbPath) {
|
|
46
|
+
throw new Error('Provide either storage or dbPath, not both');
|
|
47
|
+
}
|
|
48
|
+
this.storage = config.storage ?? sqliteStorage({ path: config.dbPath ?? './toll-booth.db' });
|
|
49
|
+
this.stats = new StatsCollector();
|
|
50
|
+
const defaultAmount = config.defaultInvoiceAmount ?? 1000;
|
|
51
|
+
// Wire stats collection while preserving user-provided callbacks
|
|
52
|
+
const userOnPayment = config.onPayment;
|
|
53
|
+
const userOnRequest = config.onRequest;
|
|
54
|
+
const userOnChallenge = config.onChallenge;
|
|
55
|
+
const stats = this.stats;
|
|
56
|
+
this.engine = createTollBooth({
|
|
57
|
+
backend: config.backend,
|
|
58
|
+
storage: this.storage,
|
|
59
|
+
pricing: config.pricing,
|
|
60
|
+
upstream: config.upstream,
|
|
61
|
+
defaultInvoiceAmount: defaultAmount,
|
|
62
|
+
rootKey: this.rootKey,
|
|
63
|
+
freeTier: config.freeTier,
|
|
64
|
+
strictPricing: config.strictPricing,
|
|
65
|
+
creditTiers: config.creditTiers,
|
|
66
|
+
onPayment: (event) => {
|
|
67
|
+
stats.recordPayment(event);
|
|
68
|
+
userOnPayment?.(event);
|
|
69
|
+
},
|
|
70
|
+
onRequest: (event) => {
|
|
71
|
+
stats.recordRequest(event);
|
|
72
|
+
userOnRequest?.(event);
|
|
73
|
+
},
|
|
74
|
+
onChallenge: (event) => {
|
|
75
|
+
stats.recordChallenge(event);
|
|
76
|
+
userOnChallenge?.(event);
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
const createInvoiceDeps = {
|
|
80
|
+
backend: config.backend,
|
|
81
|
+
storage: this.storage,
|
|
82
|
+
rootKey: this.rootKey,
|
|
83
|
+
tiers: config.creditTiers ?? [],
|
|
84
|
+
defaultAmount,
|
|
85
|
+
};
|
|
86
|
+
const invoiceStatusDeps = {
|
|
87
|
+
backend: config.backend,
|
|
88
|
+
storage: this.storage,
|
|
89
|
+
tiers: config.creditTiers,
|
|
90
|
+
nwcEnabled: !!config.nwcPayInvoice,
|
|
91
|
+
cashuEnabled: !!config.redeemCashu,
|
|
92
|
+
};
|
|
93
|
+
const upstream = config.upstream.replace(/\/$/, '');
|
|
94
|
+
const adapterConfig = {
|
|
95
|
+
engine: this.engine,
|
|
96
|
+
upstream,
|
|
97
|
+
trustProxy: config.trustProxy,
|
|
98
|
+
getClientIp: config.getClientIp,
|
|
99
|
+
responseHeaders: config.responseHeaders,
|
|
100
|
+
upstreamTimeout: config.upstreamTimeout,
|
|
101
|
+
};
|
|
102
|
+
const nwcPayDeps = config.nwcPayInvoice
|
|
103
|
+
? { nwcPay: config.nwcPayInvoice, storage: this.storage }
|
|
104
|
+
: undefined;
|
|
105
|
+
const cashuRedeemDeps = config.redeemCashu
|
|
106
|
+
? { redeem: config.redeemCashu, storage: this.storage }
|
|
107
|
+
: undefined;
|
|
108
|
+
switch (config.adapter) {
|
|
109
|
+
case 'express':
|
|
110
|
+
this.middleware = createExpressMiddleware(adapterConfig);
|
|
111
|
+
this.invoiceStatusHandler = createExpressInvoiceStatusHandler(invoiceStatusDeps);
|
|
112
|
+
this.createInvoiceHandler = createExpressCreateInvoiceHandler(createInvoiceDeps);
|
|
113
|
+
if (nwcPayDeps)
|
|
114
|
+
this.nwcPayHandler = createExpressNwcHandler(nwcPayDeps);
|
|
115
|
+
if (cashuRedeemDeps) {
|
|
116
|
+
this.redeemCashu = config.redeemCashu;
|
|
117
|
+
this.cashuRedeemHandler = createExpressCashuHandler(cashuRedeemDeps);
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
case 'web-standard':
|
|
121
|
+
this.middleware = createWebStandardMiddleware(adapterConfig);
|
|
122
|
+
this.invoiceStatusHandler = createWebStandardInvoiceStatusHandler(invoiceStatusDeps);
|
|
123
|
+
this.createInvoiceHandler = createWebStandardCreateInvoiceHandler(createInvoiceDeps);
|
|
124
|
+
if (nwcPayDeps)
|
|
125
|
+
this.nwcPayHandler = createWebStandardNwcHandler(nwcPayDeps);
|
|
126
|
+
if (cashuRedeemDeps) {
|
|
127
|
+
this.redeemCashu = config.redeemCashu;
|
|
128
|
+
this.cashuRedeemHandler = createWebStandardCashuHandler(cashuRedeemDeps);
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
// Invoice expiry pruning
|
|
133
|
+
const maxAge = config.invoiceMaxAgeMs ?? 86_400_000; // 24 hours
|
|
134
|
+
if (maxAge > 0) {
|
|
135
|
+
const timer = setInterval(() => {
|
|
136
|
+
this.storage.pruneExpiredInvoices(maxAge);
|
|
137
|
+
this.storage.pruneStaleRecords(maxAge);
|
|
138
|
+
}, 3_600_000); // every hour
|
|
139
|
+
timer.unref();
|
|
140
|
+
this.pruneTimer = timer;
|
|
141
|
+
}
|
|
142
|
+
// Auto-recover any pending Cashu claims from a previous crash
|
|
143
|
+
if (this.redeemCashu) {
|
|
144
|
+
const fn = this.redeemCashu;
|
|
145
|
+
this.recoverPendingClaims(fn).catch(() => {
|
|
146
|
+
// Recovery failures are non-fatal; claims stay pending for next restart
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Reset free-tier counters for all IPs. */
|
|
151
|
+
resetFreeTier() {
|
|
152
|
+
this.engine.freeTier?.reset();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Recover Cashu redemptions that were claimed but never settled (crash recovery).
|
|
156
|
+
* Automatically called on startup when Cashu is enabled. Can also be called
|
|
157
|
+
* manually. This requires an idempotent `redeemCashu` implementation keyed by
|
|
158
|
+
* `paymentHash`. For each pending claim, retries the redeem call:
|
|
159
|
+
* - If recovery lease is acquired: attempts redeem
|
|
160
|
+
* - On success: settles with the credited amount
|
|
161
|
+
* - On failure: leaves the claim pending for the next recovery attempt
|
|
162
|
+
*
|
|
163
|
+
* Returns the number of successfully recovered claims.
|
|
164
|
+
*/
|
|
165
|
+
async recoverPendingClaims(redeemFn) {
|
|
166
|
+
const claims = this.storage.pendingClaims();
|
|
167
|
+
let recovered = 0;
|
|
168
|
+
const renewIntervalMs = Math.max(1_000, Math.floor(REDEEM_LEASE_MS / 2));
|
|
169
|
+
for (const claim of claims) {
|
|
170
|
+
// Respect active leases so startup recovery does not race in-flight requests
|
|
171
|
+
// (or another process already handling this claim).
|
|
172
|
+
const leasedClaim = this.storage.tryAcquireRecoveryLease(claim.paymentHash, REDEEM_LEASE_MS);
|
|
173
|
+
if (!leasedClaim)
|
|
174
|
+
continue;
|
|
175
|
+
const timer = setInterval(() => {
|
|
176
|
+
this.storage.extendRecoveryLease(leasedClaim.paymentHash, REDEEM_LEASE_MS);
|
|
177
|
+
}, renewIntervalMs);
|
|
178
|
+
try {
|
|
179
|
+
const credited = await redeemFn(leasedClaim.token, leasedClaim.paymentHash);
|
|
180
|
+
if (this.storage.settleWithCredit(leasedClaim.paymentHash, credited)) {
|
|
181
|
+
recovered++;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// Transient failure (network, mint outage) — leave claim pending
|
|
186
|
+
// for the next recovery attempt. Do NOT settle with 0.
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
clearInterval(timer);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return recovered;
|
|
193
|
+
}
|
|
194
|
+
close() {
|
|
195
|
+
if (this.pruneTimer)
|
|
196
|
+
clearInterval(this.pruneTimer);
|
|
197
|
+
this.storage.close();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=booth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booth.js","sourceRoot":"","sources":["../src/booth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { StorageBackend } from '../storage/interface.js';
|
|
2
|
+
import type { CashuRedeemRequest, CashuRedeemResult } from './types.js';
|
|
3
|
+
export declare const REDEEM_LEASE_MS = 30000;
|
|
4
|
+
export interface CashuRedeemDeps {
|
|
5
|
+
redeem: (token: string, paymentHash: string) => Promise<number>;
|
|
6
|
+
storage: StorageBackend;
|
|
7
|
+
}
|
|
8
|
+
export declare function handleCashuRedeem(deps: CashuRedeemDeps, request: CashuRedeemRequest): Promise<CashuRedeemResult>;
|
|
9
|
+
//# sourceMappingURL=cashu-redeem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cashu-redeem.d.ts","sourceRoot":"","sources":["../../src/core/cashu-redeem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGvE,eAAO,MAAM,eAAe,QAAS,CAAA;AAGrC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC/D,OAAO,EAAE,cAAc,CAAA;CACxB;AAED,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CA8E5B"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { PAYMENT_HASH_RE } from './types.js';
|
|
2
|
+
export const REDEEM_LEASE_MS = 30_000;
|
|
3
|
+
const REDEEM_LEASE_RENEW_MS = Math.max(1_000, Math.floor(REDEEM_LEASE_MS / 2));
|
|
4
|
+
export async function handleCashuRedeem(deps, request) {
|
|
5
|
+
try {
|
|
6
|
+
const { token, paymentHash, statusToken } = request;
|
|
7
|
+
if (typeof token !== 'string' || !token ||
|
|
8
|
+
!PAYMENT_HASH_RE.test(paymentHash) ||
|
|
9
|
+
typeof statusToken !== 'string' || !statusToken) {
|
|
10
|
+
return { success: false, error: 'Invalid request: token, paymentHash, and statusToken required', status: 400 };
|
|
11
|
+
}
|
|
12
|
+
const invoice = deps.storage.getInvoiceForStatus(paymentHash, statusToken);
|
|
13
|
+
if (!invoice) {
|
|
14
|
+
return { success: false, error: 'Unknown payment hash or invalid status token', status: 400 };
|
|
15
|
+
}
|
|
16
|
+
// Fast path: already settled
|
|
17
|
+
if (deps.storage.isSettled(paymentHash)) {
|
|
18
|
+
return {
|
|
19
|
+
success: true,
|
|
20
|
+
credited: 0,
|
|
21
|
+
tokenSuffix: deps.storage.getSettlementSecret(paymentHash) ?? '',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// Try to claim exclusively before the irreversible external redeem call.
|
|
25
|
+
if (!deps.storage.claimForRedeem(paymentHash, token, REDEEM_LEASE_MS)) {
|
|
26
|
+
// Already settled — idempotent success
|
|
27
|
+
if (deps.storage.isSettled(paymentHash)) {
|
|
28
|
+
return {
|
|
29
|
+
success: true,
|
|
30
|
+
credited: 0,
|
|
31
|
+
tokenSuffix: deps.storage.getSettlementSecret(paymentHash) ?? '',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Retry an expired lease. This is only correct when the redeem
|
|
35
|
+
// implementation is idempotent for the same paymentHash.
|
|
36
|
+
const pendingClaim = deps.storage.tryAcquireRecoveryLease(paymentHash, REDEEM_LEASE_MS);
|
|
37
|
+
if (pendingClaim) {
|
|
38
|
+
try {
|
|
39
|
+
const credited = await withLeaseKeepAlive(deps.storage, paymentHash, () => deps.redeem(pendingClaim.token, paymentHash));
|
|
40
|
+
const settlementSecret = globalThis.crypto.randomUUID();
|
|
41
|
+
const newlySettled = deps.storage.settleWithCredit(paymentHash, credited, settlementSecret);
|
|
42
|
+
return {
|
|
43
|
+
success: true,
|
|
44
|
+
credited: newlySettled ? credited : 0,
|
|
45
|
+
tokenSuffix: newlySettled ? settlementSecret : deps.storage.getSettlementSecret(paymentHash) ?? '',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { success: false, state: 'pending', retryAfterMs: 2000 };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return { success: false, state: 'pending', retryAfterMs: 2000 };
|
|
53
|
+
}
|
|
54
|
+
// We hold the exclusive claim — call the external mint
|
|
55
|
+
try {
|
|
56
|
+
const credited = await withLeaseKeepAlive(deps.storage, paymentHash, () => deps.redeem(token, paymentHash));
|
|
57
|
+
const settlementSecret = globalThis.crypto.randomUUID();
|
|
58
|
+
const newlySettled = deps.storage.settleWithCredit(paymentHash, credited, settlementSecret);
|
|
59
|
+
return {
|
|
60
|
+
success: true,
|
|
61
|
+
credited: newlySettled ? credited : 0,
|
|
62
|
+
tokenSuffix: newlySettled ? settlementSecret : deps.storage.getSettlementSecret(paymentHash) ?? '',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return { success: false, state: 'pending', retryAfterMs: 2000 };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error('[toll-booth] Cashu redeem error:', err instanceof Error ? err.message : err);
|
|
71
|
+
return { success: false, error: 'Cashu redemption failed', status: 500 };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function withLeaseKeepAlive(storage, paymentHash, fn) {
|
|
75
|
+
const timer = setInterval(() => {
|
|
76
|
+
storage.extendRecoveryLease(paymentHash, REDEEM_LEASE_MS);
|
|
77
|
+
}, REDEEM_LEASE_RENEW_MS);
|
|
78
|
+
try {
|
|
79
|
+
return await fn();
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
clearInterval(timer);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=cashu-redeem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cashu-redeem.js","sourceRoot":"","sources":["../../src/core/cashu-redeem.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LightningBackend, CreditTier } from '../types.js';
|
|
2
|
+
import type { StorageBackend } from '../storage/interface.js';
|
|
3
|
+
import type { CreateInvoiceRequest, CreateInvoiceResult } from './types.js';
|
|
4
|
+
export interface CreateInvoiceDeps {
|
|
5
|
+
backend?: LightningBackend;
|
|
6
|
+
storage: StorageBackend;
|
|
7
|
+
rootKey: string;
|
|
8
|
+
tiers: CreditTier[];
|
|
9
|
+
defaultAmount: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Framework-agnostic invoice creation handler.
|
|
13
|
+
*
|
|
14
|
+
* Validates the requested amount against configured tiers (if any),
|
|
15
|
+
* creates a new Lightning invoice, mints a macaroon, stores everything,
|
|
16
|
+
* and returns structured result data.
|
|
17
|
+
*/
|
|
18
|
+
export declare function handleCreateInvoice(deps: CreateInvoiceDeps, request: CreateInvoiceRequest): Promise<CreateInvoiceResult>;
|
|
19
|
+
//# sourceMappingURL=create-invoice.d.ts.map
|