@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.
@@ -1767,7 +1767,7 @@ const buildX402PaymentSignature = (scheme, network, invoice, requirements) => {
1767
1767
  return btoa(unescape(encodeURIComponent(json)));
1768
1768
  };
1769
1769
 
1770
- const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1770
+ const decodeX402Header = (x402Header) => {
1771
1771
  let parsed;
1772
1772
  try {
1773
1773
  parsed = JSON.parse(decodeURIComponent(escape(atob(x402Header))));
@@ -1778,9 +1778,31 @@ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) =>
1778
1778
  if (!Array.isArray(parsed.accepts) || parsed.accepts.length === 0) {
1779
1779
  throw new Error("x402: PAYMENT-REQUIRED header contains no payment options");
1780
1780
  }
1781
- const requirements = parsed.accepts.find((e) => {
1782
- return e.extra?.paymentMethod === "lightning";
1783
- });
1781
+ return { accepts: parsed.accepts };
1782
+ };
1783
+ /**
1784
+ * Probe a PAYMENT-REQUIRED header for a lightning-payable offer without
1785
+ * throwing. Returns the matching requirements, or null if the header has no
1786
+ * lightning entry (e.g. USDC-only endpoints) or is malformed. Used by the
1787
+ * top-level fetch402 dispatcher to decide whether to attempt payment or hand
1788
+ * the 402 back to the caller.
1789
+ */
1790
+ const findX402LightningRequirements = (x402Header) => {
1791
+ let accepts;
1792
+ try {
1793
+ ({ accepts } = decodeX402Header(x402Header));
1794
+ }
1795
+ catch (_) {
1796
+ return null;
1797
+ }
1798
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1799
+ if (!requirements?.extra?.invoice)
1800
+ return null;
1801
+ return requirements;
1802
+ };
1803
+ const handleX402Payment = async (x402Header, url, fetchArgs, headers, wallet) => {
1804
+ const { accepts } = decodeX402Header(x402Header);
1805
+ const requirements = accepts.find((e) => e?.extra?.paymentMethod === "lightning");
1784
1806
  if (!requirements) {
1785
1807
  throw new Error("x402: unsupported x402 network, only Bitcoin lightning network is supported.");
1786
1808
  }
@@ -1999,19 +2021,28 @@ const fetch402 = async (url, fetchArgs, options) => {
1999
2021
  const headers = new Headers(fetchArgs.headers ?? undefined);
2000
2022
  fetchArgs.headers = headers;
2001
2023
  const initResp = await fetch(url, fetchArgs);
2024
+ // L402 / LSAT: dedicated scheme, dispatch directly.
2002
2025
  const wwwAuthHeader = initResp.headers.get("www-authenticate");
2003
2026
  if (wwwAuthHeader) {
2004
2027
  const trimmed = wwwAuthHeader.trimStart().toLowerCase();
2005
- if (trimmed.startsWith("payment")) {
2006
- return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);
2007
- }
2008
2028
  if (trimmed.startsWith("l402") || trimmed.startsWith("lsat")) {
2009
2029
  return handleL402Payment(wwwAuthHeader, url, fetchArgs, headers, wallet);
2010
2030
  }
2011
- throw new Error(`fetch402: unsupported WWW-Authenticate scheme: ${wwwAuthHeader}`);
2012
2031
  }
2032
+ // A server may advertise multiple payment options at once (e.g. an MPP
2033
+ // USDC challenge in WWW-Authenticate alongside an x402 PAYMENT-REQUIRED
2034
+ // header that lists both USDC and lightning). Try each lightning-payable
2035
+ // handler in turn; only if none matches do we hand the original 402 back
2036
+ // to the caller so they can decide what to do with non-lightning offers.
2037
+ // 1. MPP-lightning challenge (Payment method="lightning" intent="charge").
2038
+ // parseMppChallenge returns null for any other method, which lets us
2039
+ // fall through to x402 instead of throwing.
2040
+ if (wwwAuthHeader && parseMppChallenge(wwwAuthHeader)) {
2041
+ return handleMppChargePayment(wwwAuthHeader, url, fetchArgs, headers, wallet);
2042
+ }
2043
+ // 2. x402 PAYMENT-REQUIRED with a lightning entry in `accepts`.
2013
2044
  const x402Header = initResp.headers.get("PAYMENT-REQUIRED");
2014
- if (x402Header) {
2045
+ if (x402Header && findX402LightningRequirements(x402Header)) {
2015
2046
  return handleX402Payment(x402Header, url, fetchArgs, headers, wallet);
2016
2047
  }
2017
2048
  return initResp;
@@ -2155,6 +2186,7 @@ exports.fetch402 = fetch402;
2155
2186
  exports.fetchWithL402 = fetchWithL402;
2156
2187
  exports.fetchWithMpp = fetchWithMpp;
2157
2188
  exports.fetchWithX402 = fetchWithX402;
2189
+ exports.findX402LightningRequirements = findX402LightningRequirements;
2158
2190
  exports.fromHexString = fromHexString;
2159
2191
  exports.generateZapEvent = generateZapEvent;
2160
2192
  exports.getEventHash = getEventHash;