@getalby/lightning-tools 8.1.1 → 8.2.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/README.md +78 -13
- package/dist/cjs/402/l402.cjs +1304 -2
- package/dist/cjs/402/l402.cjs.map +1 -1
- package/dist/cjs/402/mpp.cjs +1306 -5
- package/dist/cjs/402/mpp.cjs.map +1 -1
- package/dist/cjs/402/x402.cjs +46 -15
- package/dist/cjs/402/x402.cjs.map +1 -1
- package/dist/cjs/402.cjs +101 -10
- package/dist/cjs/402.cjs.map +1 -1
- package/dist/cjs/bip21.cjs +115 -0
- package/dist/cjs/bip21.cjs.map +1 -0
- package/dist/cjs/index.cjs +213 -10
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/402/l402.js +1304 -2
- package/dist/esm/402/l402.js.map +1 -1
- package/dist/esm/402/mpp.js +1306 -5
- package/dist/esm/402/mpp.js.map +1 -1
- package/dist/esm/402/x402.js +46 -15
- package/dist/esm/402/x402.js.map +1 -1
- package/dist/esm/402.js +98 -11
- package/dist/esm/402.js.map +1 -1
- package/dist/esm/bip21.js +112 -0
- package/dist/esm/bip21.js.map +1 -0
- package/dist/esm/index.js +208 -11
- package/dist/esm/index.js.map +1 -1
- package/dist/lightning-tools.umd.js +2 -2
- package/dist/lightning-tools.umd.js.map +1 -1
- package/dist/types/402/l402.d.ts +49 -3
- package/dist/types/402/mpp.d.ts +54 -6
- package/dist/types/402/x402.d.ts +49 -3
- package/dist/types/402.d.ts +68 -17
- package/dist/types/bip21.d.ts +46 -0
- package/dist/types/index.d.ts +112 -17
- package/package.json +6 -1
package/dist/cjs/402.cjs
CHANGED
|
@@ -1259,6 +1259,28 @@ class Invoice {
|
|
|
1259
1259
|
}
|
|
1260
1260
|
}
|
|
1261
1261
|
|
|
1262
|
+
/** Apply a previously-obtained credential to the outgoing request headers. */
|
|
1263
|
+
const applyCredentials = (headers, credentials) => {
|
|
1264
|
+
headers.set(credentials.header, credentials.value);
|
|
1265
|
+
};
|
|
1266
|
+
/** Attach payment metadata to a response and return it (typed). */
|
|
1267
|
+
const attachPayment = (response, payment) => {
|
|
1268
|
+
if (payment) {
|
|
1269
|
+
response.payment = payment;
|
|
1270
|
+
}
|
|
1271
|
+
return response;
|
|
1272
|
+
};
|
|
1273
|
+
/** Payment metadata describing a request authorized with a reused credential. */
|
|
1274
|
+
const reusedCredentialPayment = (credentials) => credentials ? { paid: false, amount: 0, credentials } : undefined;
|
|
1275
|
+
/** Satoshi amount of a BOLT11 invoice (0 when it cannot be decoded). */
|
|
1276
|
+
const getInvoiceAmount = (invoice) => {
|
|
1277
|
+
try {
|
|
1278
|
+
return new Invoice({ pr: invoice }).satoshi;
|
|
1279
|
+
}
|
|
1280
|
+
catch (_) {
|
|
1281
|
+
return 0;
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1262
1284
|
function createGuardedWallet(wallet, maxAmountSats) {
|
|
1263
1285
|
return {
|
|
1264
1286
|
payInvoice: async (args) => {
|
|
@@ -1310,6 +1332,9 @@ const handleL402Payment = async (l402Header, url, fetchArgs, headers, wallet) =>
|
|
|
1310
1332
|
const details = parseL402(l402Header);
|
|
1311
1333
|
const token = details.token || details.macaroon;
|
|
1312
1334
|
const invoice = details.invoice;
|
|
1335
|
+
// Preserve the scheme the server challenged with (L402 or LSAT) so the
|
|
1336
|
+
// retry's Authorization header matches what the server expects.
|
|
1337
|
+
const scheme = /^\s*LSAT\b/i.test(l402Header) ? "LSAT" : "L402";
|
|
1313
1338
|
if (!token) {
|
|
1314
1339
|
throw new Error("L402: missing token/macaroon in WWW-Authenticate header");
|
|
1315
1340
|
}
|
|
@@ -1317,8 +1342,16 @@ const handleL402Payment = async (l402Header, url, fetchArgs, headers, wallet) =>
|
|
|
1317
1342
|
throw new Error("L402: missing invoice in WWW-Authenticate header");
|
|
1318
1343
|
}
|
|
1319
1344
|
const invResp = await wallet.payInvoice({ invoice });
|
|
1320
|
-
|
|
1321
|
-
|
|
1345
|
+
const value = `${scheme} ${token}:${invResp.preimage}`;
|
|
1346
|
+
headers.set("Authorization", value);
|
|
1347
|
+
const response = await fetch(url, fetchArgs);
|
|
1348
|
+
return attachPayment(response, {
|
|
1349
|
+
paid: true,
|
|
1350
|
+
amount: getInvoiceAmount(invoice),
|
|
1351
|
+
feesPaid: invResp.fees_paid,
|
|
1352
|
+
preimage: invResp.preimage,
|
|
1353
|
+
credentials: { header: "Authorization", value },
|
|
1354
|
+
});
|
|
1322
1355
|
};
|
|
1323
1356
|
const fetchWithL402 = async (url, fetchArgs, options) => {
|
|
1324
1357
|
const wallet = options.wallet;
|
|
@@ -1332,6 +1365,15 @@ const fetchWithL402 = async (url, fetchArgs, options) => {
|
|
|
1332
1365
|
fetchArgs.mode = "cors";
|
|
1333
1366
|
const headers = new Headers(fetchArgs.headers ?? undefined);
|
|
1334
1367
|
fetchArgs.headers = headers;
|
|
1368
|
+
// If the caller supplied a credential, we MUST use it and never pay again —
|
|
1369
|
+
// even if the server still responds with a 402. Re-paying here is the exact
|
|
1370
|
+
// double-charge this API exists to prevent; the caller decides what to do
|
|
1371
|
+
// with a rejected credential (retry after settlement, top up, etc.).
|
|
1372
|
+
if (options.credentials) {
|
|
1373
|
+
applyCredentials(headers, options.credentials);
|
|
1374
|
+
const reusedResp = await fetch(url, fetchArgs);
|
|
1375
|
+
return attachPayment(reusedResp, reusedCredentialPayment(options.credentials));
|
|
1376
|
+
}
|
|
1335
1377
|
const initResp = await fetch(url, fetchArgs);
|
|
1336
1378
|
const header = initResp.headers.get("www-authenticate");
|
|
1337
1379
|
if (!header) {
|
|
@@ -1398,9 +1440,17 @@ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) =>
|
|
|
1398
1440
|
if (invoice.amountRaw != requirements.amount) {
|
|
1399
1441
|
throw new Error(`Invalid invoice amount: ${invoice.amountRaw}. expected ${requirements.amount}`);
|
|
1400
1442
|
}
|
|
1401
|
-
await wallet.payInvoice({ invoice: invoice.paymentRequest });
|
|
1402
|
-
|
|
1403
|
-
|
|
1443
|
+
const invResp = await wallet.payInvoice({ invoice: invoice.paymentRequest });
|
|
1444
|
+
const value = buildX402PaymentSignature(requirements.scheme, requirements.network, invoice.paymentRequest, requirements);
|
|
1445
|
+
headers.set("payment-signature", value);
|
|
1446
|
+
const response = await fetch(url, fetchArgs);
|
|
1447
|
+
return attachPayment(response, {
|
|
1448
|
+
paid: true,
|
|
1449
|
+
amount: invoice.satoshi,
|
|
1450
|
+
feesPaid: invResp.fees_paid,
|
|
1451
|
+
preimage: invResp.preimage,
|
|
1452
|
+
credentials: { header: "payment-signature", value },
|
|
1453
|
+
});
|
|
1404
1454
|
};
|
|
1405
1455
|
const fetchWithX402 = async (url, fetchArgs, options) => {
|
|
1406
1456
|
const wallet = options.wallet;
|
|
@@ -1411,6 +1461,15 @@ const fetchWithX402 = async (url, fetchArgs, options) => {
|
|
|
1411
1461
|
fetchArgs.mode = "cors";
|
|
1412
1462
|
const headers = new Headers(fetchArgs.headers ?? undefined);
|
|
1413
1463
|
fetchArgs.headers = headers;
|
|
1464
|
+
// If the caller supplied a credential, we MUST use it and never pay again —
|
|
1465
|
+
// even if the server still responds with a 402. Re-paying here is the exact
|
|
1466
|
+
// double-charge this API exists to prevent; the caller decides what to do
|
|
1467
|
+
// with a rejected credential (retry after settlement, top up, etc.).
|
|
1468
|
+
if (options.credentials) {
|
|
1469
|
+
applyCredentials(headers, options.credentials);
|
|
1470
|
+
const reusedResp = await fetch(url, fetchArgs);
|
|
1471
|
+
return attachPayment(reusedResp, reusedCredentialPayment(options.credentials));
|
|
1472
|
+
}
|
|
1414
1473
|
const initResp = await fetch(url, fetchArgs);
|
|
1415
1474
|
const header = initResp.headers.get("PAYMENT-REQUIRED");
|
|
1416
1475
|
if (!header) {
|
|
@@ -1557,8 +1616,16 @@ const handleMppChargePayment = async (wwwAuthHeader, url, fetchArgs, headers, wa
|
|
|
1557
1616
|
const invResp = await wallet.payInvoice({ invoice });
|
|
1558
1617
|
// Per spec: Authorization: Payment <base64url-token> (single token, no wrapper)
|
|
1559
1618
|
const credential = buildMppCredential(challenge, invResp.preimage);
|
|
1560
|
-
|
|
1561
|
-
|
|
1619
|
+
const value = `Payment ${credential}`;
|
|
1620
|
+
headers.set("Authorization", value);
|
|
1621
|
+
const response = await fetch(url, fetchArgs);
|
|
1622
|
+
return attachPayment(response, {
|
|
1623
|
+
paid: true,
|
|
1624
|
+
amount: getInvoiceAmount(invoice),
|
|
1625
|
+
feesPaid: invResp.fees_paid,
|
|
1626
|
+
preimage: invResp.preimage,
|
|
1627
|
+
credentials: { header: "Authorization", value },
|
|
1628
|
+
});
|
|
1562
1629
|
};
|
|
1563
1630
|
/**
|
|
1564
1631
|
* Fetch a resource protected by the draft-lightning-charge-00 payment
|
|
@@ -1569,9 +1636,11 @@ const handleMppChargePayment = async (wwwAuthHeader, url, fetchArgs, headers, wa
|
|
|
1569
1636
|
* the function pays the embedded BOLT11 invoice and retries with the
|
|
1570
1637
|
* resulting preimage as the credential.
|
|
1571
1638
|
*
|
|
1572
|
-
*
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1639
|
+
* Pass a previous credential via `options.credentials` to reuse it (e.g. when
|
|
1640
|
+
* polling); the credential is applied and the function NEVER pays again, even
|
|
1641
|
+
* if the server still responds with a 402 (that response is returned as-is).
|
|
1642
|
+
* Note: lightning-charge typically uses consume-once challenge semantics, so a
|
|
1643
|
+
* reused credential is only accepted by servers that explicitly support it.
|
|
1575
1644
|
*/
|
|
1576
1645
|
const fetchWithMpp = async (url, fetchArgs, options) => {
|
|
1577
1646
|
const wallet = options.wallet;
|
|
@@ -1585,6 +1654,15 @@ const fetchWithMpp = async (url, fetchArgs, options) => {
|
|
|
1585
1654
|
fetchArgs.mode = "cors";
|
|
1586
1655
|
const headers = new Headers(fetchArgs.headers ?? undefined);
|
|
1587
1656
|
fetchArgs.headers = headers;
|
|
1657
|
+
// If the caller supplied a credential, we MUST use it and never pay again —
|
|
1658
|
+
// even if the server still responds with a 402. Re-paying here is the exact
|
|
1659
|
+
// double-charge this API exists to prevent; the caller decides what to do
|
|
1660
|
+
// with a rejected credential (retry after settlement, top up, etc.).
|
|
1661
|
+
if (options.credentials) {
|
|
1662
|
+
applyCredentials(headers, options.credentials);
|
|
1663
|
+
const reusedResp = await fetch(url, fetchArgs);
|
|
1664
|
+
return attachPayment(reusedResp, reusedCredentialPayment(options.credentials));
|
|
1665
|
+
}
|
|
1588
1666
|
const initResp = await fetch(url, fetchArgs);
|
|
1589
1667
|
const wwwAuthHeader = initResp.headers.get("www-authenticate");
|
|
1590
1668
|
if (!wwwAuthHeader ||
|
|
@@ -1605,6 +1683,15 @@ const fetch402 = async (url, fetchArgs, options) => {
|
|
|
1605
1683
|
fetchArgs.mode = "cors";
|
|
1606
1684
|
const headers = new Headers(fetchArgs.headers ?? undefined);
|
|
1607
1685
|
fetchArgs.headers = headers;
|
|
1686
|
+
// If the caller supplied a credential, we MUST use it and never pay again —
|
|
1687
|
+
// even if the server still responds with a 402. Re-paying here is the exact
|
|
1688
|
+
// double-charge this API exists to prevent; the caller decides what to do
|
|
1689
|
+
// with a rejected credential (retry after settlement, top up, etc.).
|
|
1690
|
+
if (options.credentials) {
|
|
1691
|
+
applyCredentials(headers, options.credentials);
|
|
1692
|
+
const reusedResp = await fetch(url, fetchArgs);
|
|
1693
|
+
return attachPayment(reusedResp, reusedCredentialPayment(options.credentials));
|
|
1694
|
+
}
|
|
1608
1695
|
const initResp = await fetch(url, fetchArgs);
|
|
1609
1696
|
// L402 / LSAT: dedicated scheme, dispatch directly.
|
|
1610
1697
|
const wwwAuthHeader = initResp.headers.get("www-authenticate");
|
|
@@ -1712,15 +1799,19 @@ function parseL402Authorization(input) {
|
|
|
1712
1799
|
};
|
|
1713
1800
|
}
|
|
1714
1801
|
|
|
1802
|
+
exports.applyCredentials = applyCredentials;
|
|
1803
|
+
exports.attachPayment = attachPayment;
|
|
1715
1804
|
exports.createGuardedWallet = createGuardedWallet;
|
|
1716
1805
|
exports.fetch402 = fetch402;
|
|
1717
1806
|
exports.fetchWithL402 = fetchWithL402;
|
|
1718
1807
|
exports.fetchWithMpp = fetchWithMpp;
|
|
1719
1808
|
exports.fetchWithX402 = fetchWithX402;
|
|
1720
1809
|
exports.findX402LightningRequirements = findX402LightningRequirements;
|
|
1810
|
+
exports.getInvoiceAmount = getInvoiceAmount;
|
|
1721
1811
|
exports.issueL402Macaroon = issueL402Macaroon;
|
|
1722
1812
|
exports.makeL402AuthenticateHeader = makeL402AuthenticateHeader;
|
|
1723
1813
|
exports.parseL402 = parseL402;
|
|
1724
1814
|
exports.parseL402Authorization = parseL402Authorization;
|
|
1815
|
+
exports.reusedCredentialPayment = reusedCredentialPayment;
|
|
1725
1816
|
exports.verifyL402Macaroon = verifyL402Macaroon;
|
|
1726
1817
|
//# sourceMappingURL=402.cjs.map
|