@getalby/lightning-tools 8.1.0 → 8.1.1

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/dist/esm/402.js CHANGED
@@ -1350,7 +1350,7 @@ const buildX402PaymentSignature = (scheme, network, invoice, requirements) => {
1350
1350
  return btoa(unescape(encodeURIComponent(json)));
1351
1351
  };
1352
1352
 
1353
- const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1353
+ const decodeX402Header = (x402Header) => {
1354
1354
  let parsed;
1355
1355
  try {
1356
1356
  parsed = JSON.parse(decodeURIComponent(escape(atob(x402Header))));
@@ -1361,9 +1361,31 @@ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) =>
1361
1361
  if (!Array.isArray(parsed.accepts) || parsed.accepts.length === 0) {
1362
1362
  throw new Error("x402: PAYMENT-REQUIRED header contains no payment options");
1363
1363
  }
1364
- const requirements = parsed.accepts.find((e) => {
1365
- return e.extra?.paymentMethod === "lightning";
1366
- });
1364
+ return { accepts: parsed.accepts };
1365
+ };
1366
+ /**
1367
+ * Probe a PAYMENT-REQUIRED header for a lightning-payable offer without
1368
+ * throwing. Returns the matching requirements, or null if the header has no
1369
+ * lightning entry (e.g. USDC-only endpoints) or is malformed. Used by the
1370
+ * top-level fetch402 dispatcher to decide whether to attempt payment or hand
1371
+ * the 402 back to the caller.
1372
+ */
1373
+ const findX402LightningRequirements = (x402Header) => {
1374
+ let accepts;
1375
+ try {
1376
+ ({ accepts } = decodeX402Header(x402Header));
1377
+ }
1378
+ catch (_) {
1379
+ return null;
1380
+ }
1381
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1382
+ if (!requirements?.extra?.invoice)
1383
+ return null;
1384
+ return requirements;
1385
+ };
1386
+ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1387
+ const { accepts } = decodeX402Header(x402Header);
1388
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1367
1389
  if (!requirements) {
1368
1390
  throw new Error("x402: unsupported x402 network, only Bitcoin lightning network is supported.");
1369
1391
  }
@@ -1582,19 +1604,28 @@ const fetch402 = async (url, fetchArgs, options) => {
1582
1604
  const headers = new Headers(fetchArgs.headers ?? undefined);
1583
1605
  fetchArgs.headers = headers;
1584
1606
  const initResp = await fetch(url, fetchArgs);
1607
+ // L402 / LSAT: dedicated scheme, dispatch directly.
1585
1608
  const wwwAuthHeader = initResp.headers.get("www-authenticate");
1586
1609
  if (wwwAuthHeader) {
1587
1610
  const trimmed = wwwAuthHeader.trimStart().toLowerCase();
1588
- if (trimmed.startsWith("payment")) {
1589
- return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);
1590
- }
1591
1611
  if (trimmed.startsWith("l402") || trimmed.startsWith("lsat")) {
1592
1612
  return handleL402Payment(wwwAuthHeader, url, fetchArgs, headers, wallet);
1593
1613
  }
1594
- throw new Error(`fetch402: unsupported WWW-Authenticate scheme: ${wwwAuthHeader}`);
1595
1614
  }
1615
+ // A server may advertise multiple payment options at once (e.g. an MPP
1616
+ // USDC challenge in WWW-Authenticate alongside an x402 PAYMENT-REQUIRED
1617
+ // header that lists both USDC and lightning). Try each lightning-payable
1618
+ // handler in turn; only if none matches do we hand the original 402 back
1619
+ // to the caller so they can decide what to do with non-lightning offers.
1620
+ // 1. MPP-lightning challenge (Payment method="lightning" intent="charge").
1621
+ // parseMppChallenge returns null for any other method, which lets us
1622
+ // fall through to x402 instead of throwing.
1623
+ if (wwwAuthHeader && parseMppChallenge(wwwAuthHeader)) {
1624
+ return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);
1625
+ }
1626
+ // 2. x402 PAYMENT-REQUIRED with a lightning entry in `accepts`.
1596
1627
  const x402Header = initResp.headers.get("PAYMENT-REQUIRED");
1597
- if (x402Header) {
1628
+ if (x402Header && findX402LightningRequirements(x402Header)) {
1598
1629
  return handleX402Payment(x402Header, url, fetchArgs, headers, wallet);
1599
1630
  }
1600
1631
  return initResp;
@@ -1679,5 +1710,5 @@ function parseL402Authorization(input) {
1679
1710
  };
1680
1711
  }
1681
1712
 
1682
- export { createGuardedWallet, fetch402, fetchWithL402, fetchWithMpp, fetchWithX402, issueL402Macaroon, makeL402AuthenticateHeader, parseL402, parseL402Authorization, verifyL402Macaroon };
1713
+ export { createGuardedWallet, fetch402, fetchWithL402, fetchWithMpp, fetchWithX402, findX402LightningRequirements, issueL402Macaroon, makeL402AuthenticateHeader, parseL402, parseL402Authorization, verifyL402Macaroon };
1683
1714
  //# sourceMappingURL=402.js.map