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