@piprail/sdk 1.22.0 → 1.23.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/CHANGELOG.md CHANGED
@@ -4,6 +4,53 @@ All notable changes to `@piprail/sdk` are documented here. The format
4
4
  follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
5
5
  versions follow [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [1.23.0] — 2026-06-14 — self-describing endpoints + discovery reach
8
+
9
+ ### Added — self-describing, more discoverable endpoints (discoverability plan: Phases 1, 2, 4, 5)
10
+
11
+ - **Self-describing 402s, ON by default.** Every challenge a gate builds now carries an
12
+ `extensions.piprail` block — identity, per-rail how-to-pay, `npm i @piprail/sdk` + a paste-ready
13
+ snippet, the MCP command, and docs + discovery pointers — so any human, AI agent, or crawler that
14
+ lands on a gated endpoint (even the `onchain-proof` scheme a stock x402 client can't pay) knows what
15
+ it is and how to pay it. **Purely-additive** metadata in the spec-opaque `extensions` bag, and it
16
+ rides in the response **body** only — the base64 `payment-required` **header stays slim** (just
17
+ `accepts[]` + a small `bazaar`/rejection block), so the header, pay path, `accepts[]`, and status are
18
+ byte-identical (a rejection's `{code,detail}` are deep-merged as siblings and still win). Opt out with
19
+ `requirePayment({ selfDescribe: false })`. New exports: `buildSelfDescription`, `describeChallenge`, `BRAND`.
20
+ - **Human landing page + HTTP pointers.** `gate.landingPage(challenge)` / `renderLandingPage()` render a
21
+ tiny, HTML-escaped 402 page for a browser (serve it on `Accept: text/html`; agents/crawlers keep the
22
+ JSON 402). It leads with **how to pay** and a clear **caution** that a manual send to the address won't
23
+ unlock the resource (pay via an x402 client — no custody, no manual-payment desk). `discoveryHeaders()`
24
+ + `POWERED_BY` emit a `Link` (rel `service-desc` / `x402-discovery`) + `x-powered-by` header bag to
25
+ spread on every response. The SDK serves nothing — the merchant does.
26
+ - **Facilitator-coverage map.** `KNOWN_FACILITATORS` (+ `knownFacilitatorsFor` /
27
+ `firstKeylessFacilitator`) — shipped data of which keyless facilitators settle `exact` on which
28
+ networks (seeded: PayAI on Base + Solana, dated-verified). `facilitatorCoverage(url)` /
29
+ `parseFacilitatorSupported(body)` read a facilitator's live `GET /supported` (best-effort, never throw).
30
+ - **Agent guide** gained a "landing cold — read the self-description" section (surfaced over the MCP).
31
+
32
+ All of the above is additive + opt-in; the zero-config pay path is byte-identical to before. New pure
33
+ modules (`selfdescribe.ts`, `landing.ts`, `facilitators.ts`) join the viem-free protocol-layer grep.
34
+
35
+ ### Removed
36
+ - **`base-sepolia` (84532) testnet entry** from `EXACT_NETWORK_SLUGS` and the Permit2 proxy chain-id set
37
+ — mainnets only, no testnet presets (it was dead reference data; nothing settles on a testnet).
38
+
39
+ ### Fixed
40
+ - **`x-powered-by` is now ASCII** (`PipRail x402 | https://piprail.com`) — the previous non-ASCII middot
41
+ mangled to `·` over Node's latin1 header transport. `GENERATOR` keeps its `·` (JSON body only).
42
+ - **`renderLandingPage` never throws** on a rail missing `amount`/`asset` (matches the module's
43
+ never-throw contract).
44
+ - **`fetchFacilitatorFeePayer` matches the network normalized** (CAIP-2 *or* slug) so a slug-reporting
45
+ facilitator's Solana exact fee-payer is found instead of silently dropped.
46
+
47
+ ## [1.22.1] — 2026-06-13 — docs: community links + integrations signposting
48
+
49
+ Docs-only patch — **no code change** (the SDK is byte-identical to 1.22.0). Refreshes the README:
50
+ adds a "Spread the word" section (GitHub · X · piprail.com · docs) and surfaces the first-party
51
+ integrations (the OpenClaw skill, `clawhub install piprail`) from the repo. Published so the npm
52
+ package page carries the updated front door.
53
+
7
54
  ## [1.22.0] — 2026-06-13 — optional wallet (read-only client)
8
55
 
9
56
  A purely **additive, opt-in** feature: `PipRailClient`'s `wallet` is now **optional**. Omit it for a
package/README.md CHANGED
@@ -55,6 +55,12 @@ The same app can **take** payments and **make** them. → [Making payments](http
55
55
  | **[MCP server](https://docs.piprail.com/mcp/overview/)** | Give any AI agent a budget-bound wallet |
56
56
  | **[Reference](https://docs.piprail.com/reference/api/)** | The complete API surface |
57
57
 
58
+ ## Spread the word
59
+
60
+ PipRail is free, open-source, and has no backend to sell you. If it saved you from building a payments backend, help other developers (and their agents) find it:
61
+
62
+ ⭐ **[Star on GitHub](https://github.com/piprail/piprail)**  ·  𝕏 **[Follow @piprailhq](https://x.com/piprailhq)**  ·  🌐 **[piprail.com](https://piprail.com)**  ·  📖 **[docs.piprail.com](https://docs.piprail.com)**
63
+
58
64
  ## License & trademark
59
65
 
60
66
  The code is **MIT** — use it, fork it, ship it. **PipRail™**, the logo, and the `@piprail` npm scope are trademarks of the PipRail project: build on the code freely, but please don't call a fork "PipRail" or imply it's official. See [TRADEMARK.md](https://github.com/piprail/piprail/blob/main/TRADEMARK.md).
package/dist/index.cjs CHANGED
@@ -511,7 +511,6 @@ function sumTransfersTo(logs, asset, payTo) {
511
511
  var EXACT_NETWORK_SLUGS = {
512
512
  ethereum: 1,
513
513
  base: 8453,
514
- "base-sepolia": 84532,
515
514
  arbitrum: 42161,
516
515
  optimism: 10,
517
516
  polygon: 137,
@@ -983,8 +982,6 @@ var PERMIT2_PROXY_CHAIN_IDS = /* @__PURE__ */ new Set([
983
982
  // Ethereum
984
983
  8453,
985
984
  // Base
986
- 84532,
987
- // Base Sepolia
988
985
  42161,
989
986
  // Arbitrum
990
987
  10,
@@ -3604,6 +3601,48 @@ async function readInvalidReason(response) {
3604
3601
  return null;
3605
3602
  }
3606
3603
 
3604
+ // src/selfdescribe.ts
3605
+ var BRAND = {
3606
+ name: "PipRail",
3607
+ home: "https://piprail.com",
3608
+ docs: "https://docs.piprail.com",
3609
+ payDocs: "https://docs.piprail.com/paying",
3610
+ sdkInstall: "npm i @piprail/sdk",
3611
+ sdkSnippet: "import { PipRailClient } from '@piprail/sdk'\nconst client = new PipRailClient({ chain: '<your-chain>', wallet })\nawait client.fetch('<this-url>')",
3612
+ mcpRun: "npx -y @piprail/mcp"
3613
+ };
3614
+ var WHAT = 'This is an x402 "402 Payment Required" endpoint. Pay one of the offered rails to access it.';
3615
+ function howFor(scheme) {
3616
+ return scheme === "exact" ? "Standard x402 exact rail \u2014 sign an EIP-3009 / Permit2 / SVM authorization; any stock x402 client (e.g. @x402/fetch) can pay this." : "Pay this amount on-chain to payTo, then resubmit with a payment-signature header carrying the proof ref + nonce. Easiest with @piprail/sdk (see sdk.install).";
3617
+ }
3618
+ function railOf(a) {
3619
+ const extra = _nullishCoalesce(a.extra, () => ( {}));
3620
+ return {
3621
+ scheme: a.scheme,
3622
+ network: a.network,
3623
+ asset: a.asset,
3624
+ payTo: a.payTo,
3625
+ amount: a.amount,
3626
+ ...extra.amountFormatted ? { amountFormatted: extra.amountFormatted } : {},
3627
+ ...extra.symbol ? { symbol: extra.symbol } : {},
3628
+ how: howFor(a.scheme)
3629
+ };
3630
+ }
3631
+ function buildSelfDescription(input) {
3632
+ return {
3633
+ name: "PipRail",
3634
+ protocol: "x402",
3635
+ version: "2",
3636
+ what: WHAT,
3637
+ pay: input.accepts.map(railOf),
3638
+ sdk: { install: BRAND.sdkInstall, snippet: BRAND.sdkSnippet },
3639
+ mcp: { run: BRAND.mcpRun, tool: "piprail_pay_request" },
3640
+ docs: { home: BRAND.home, agents: BRAND.docs, pay: BRAND.payDocs },
3641
+ discovery: { openapi: "/openapi.json", wellKnown: "/.well-known/x402" },
3642
+ ...input.instruction ? { instruction: input.instruction } : {}
3643
+ };
3644
+ }
3645
+
3607
3646
  // src/render.ts
3608
3647
  function summarizePlan(plan) {
3609
3648
  if (plan == null) return "No payment required \u2014 the URL is not payment-gated.";
@@ -3644,6 +3683,18 @@ function formatSpendReport(summary) {
3644
3683
  (a) => `${a.totalFormatted} ${_nullishCoalesce(a.symbol, () => ( a.asset))} on ${a.network} (${a.count} payment${a.count === 1 ? "" : "s"})`
3645
3684
  ).join("; ");
3646
3685
  }
3686
+ function describeChallenge(challenge) {
3687
+ const first = challenge.accepts[0];
3688
+ if (!first) {
3689
+ return `${BRAND.name} x402 payment endpoint. Pay with @piprail/sdk (${BRAND.sdkInstall}). Docs: ${BRAND.home}.`;
3690
+ }
3691
+ const extra = _nullishCoalesce(first.extra, () => ( {}));
3692
+ const amount = _nullishCoalesce(extra.amountFormatted, () => ( first.amount));
3693
+ const token = _nullishCoalesce(extra.symbol, () => ( first.asset));
3694
+ const hasExact = challenge.accepts.some((a) => a.scheme === "exact");
3695
+ const standard = hasExact ? "; or any standard x402 client (an exact rail is offered)" : "";
3696
+ return `${BRAND.name} x402 payment endpoint \u2014 pay ${amount} ${token} on ${first.network} to ${first.payTo}. Programmatic: ${BRAND.sdkInstall} then client.fetch(url)${standard}. Docs: ${BRAND.home}.`;
3697
+ }
3647
3698
 
3648
3699
  // src/agentGuide.ts
3649
3700
  var PIPRAIL_AGENT_GUIDE = `# Paying with PipRail \u2014 the agent contract
@@ -3651,6 +3702,13 @@ var PIPRAIL_AGENT_GUIDE = `# Paying with PipRail \u2014 the agent contract
3651
3702
  You can pay for x402 "402 Payment Required" resources autonomously. Money moves
3652
3703
  straight from your wallet to the server; PipRail custodies nothing. Follow this.
3653
3704
 
3705
+ ## Landing cold \u2014 read the self-description
3706
+ Every PipRail 402 self-describes. Read challenge.extensions.piprail for { name, what, pay[]
3707
+ (each rail's how-to-pay), sdk.install, mcp, docs } \u2014 never guess what an endpoint is. If your
3708
+ tooling can't pay a rail (e.g. a stock x402 client can't pay the onchain-proof scheme), the
3709
+ block says how: install @piprail/sdk (npm i @piprail/sdk) or run the MCP (npx -y @piprail/mcp)
3710
+ and pay with the tools below.
3711
+
3654
3712
  ## The loop: quote \u2192 plan \u2192 pay
3655
3713
  1. piprail_quote_payment(url) \u2014 PRICE it. Returns the amount, token, chain, and
3656
3714
  whether it is within your spend policy. No funds move. Use it to decide if a
@@ -4011,6 +4069,13 @@ function classifyChallenge(challenge, opts) {
4011
4069
 
4012
4070
  // src/discovery.ts
4013
4071
  var GENERATOR = "@piprail/sdk \xB7 https://piprail.com";
4072
+ var POWERED_BY = "PipRail x402 | https://piprail.com";
4073
+ function discoveryHeaders(opts = {}) {
4074
+ return {
4075
+ link: '</openapi.json>; rel="service-desc", </.well-known/x402>; rel="x402-discovery"',
4076
+ ...opts.attribution === false ? {} : { "x-powered-by": POWERED_BY }
4077
+ };
4078
+ }
4014
4079
  function buildBazaarExtension(descriptor = {}) {
4015
4080
  const method = (_nullishCoalesce(descriptor.method, () => ( "GET"))).toUpperCase();
4016
4081
  const queryParams = _nullishCoalesce(descriptor.queryParams, () => ( {}));
@@ -4090,6 +4155,57 @@ function buildX402DnsTxt(input) {
4090
4155
  };
4091
4156
  }
4092
4157
 
4158
+ // src/landing.ts
4159
+ function esc(s) {
4160
+ return String(_nullishCoalesce(s, () => ( ""))).replace(
4161
+ /[&<>"']/g,
4162
+ (c) => c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : c === '"' ? "&quot;" : "&#39;"
4163
+ );
4164
+ }
4165
+ var STYLE = `:root{color-scheme:dark}
4166
+ *{box-sizing:border-box}
4167
+ body{margin:0;background:#0a0e0f;color:#e6edf0;font:16px/1.6 ui-sans-serif,system-ui,-apple-system,Inter,sans-serif}
4168
+ main{max-width:680px;margin:0 auto;padding:48px 24px}
4169
+ h1{font-size:1.6rem;margin:0 0 .25rem}
4170
+ .lede{color:#9fb0b5;margin:.25rem 0 2rem}
4171
+ h2{font-size:1rem;text-transform:uppercase;letter-spacing:.05em;color:#34d399;margin:2rem 0 .75rem}
4172
+ table{width:100%;border-collapse:collapse;font-size:.92rem}
4173
+ th,td{text-align:left;padding:.5rem .6rem;border-bottom:1px solid #1c2426;vertical-align:top}
4174
+ th{color:#9fb0b5;font-weight:600}
4175
+ code,pre{font-family:ui-monospace,JetBrains Mono,monospace;font-size:.85rem}
4176
+ pre{background:#11181a;border:1px solid #1c2426;border-radius:8px;padding:14px;overflow-x:auto;color:#cfe9df}
4177
+ a{color:#34d399}
4178
+ .mono{font-family:ui-monospace,monospace;word-break:break-all}
4179
+ .warn{margin:0 0 2rem;padding:14px 16px;border:1px solid #5a4a1f;background:#17130a;border-radius:8px;color:#e8d9a8;font-size:.92rem;line-height:1.55}
4180
+ .warn strong{color:#f5d77a}
4181
+ .muted{color:#5b6b6f;font-weight:400;text-transform:none;letter-spacing:0;font-size:.8rem}
4182
+ footer{margin-top:2.5rem;color:#5b6b6f;font-size:.8rem}`;
4183
+ function renderLandingPage(sd) {
4184
+ const lede = esc(_nullishCoalesce(sd.instruction, () => ( sd.what)));
4185
+ const rows = sd.pay.map(
4186
+ (r) => `<tr><td><code>${esc(r.scheme)}</code></td><td>${esc(r.network)}</td><td>${esc(_nullishCoalesce(r.amountFormatted, () => ( r.amount)))} ${esc(_nullishCoalesce(r.symbol, () => ( r.asset)))}</td><td class="mono">${esc(r.payTo)}</td></tr>`
4187
+ ).join("");
4188
+ return `<!doctype html>
4189
+ <html lang="en"><head><meta charset="utf-8">
4190
+ <meta name="viewport" content="width=device-width,initial-scale=1">
4191
+ <title>${esc(sd.name)} \xB7 x402 payment required</title>
4192
+ <style>${STYLE}</style>
4193
+ </head><body><main>
4194
+ <h1>402 \u2014 Payment Required</h1>
4195
+ <p class="lede">${lede}</p>
4196
+ <div class="warn"><strong>Pay with an x402 client \u2014 not by hand.</strong> This endpoint is paid programmatically: an x402 client (the ${esc(sd.name)} SDK or MCP below, or any x402-compatible wallet) makes the payment <em>and proves it</em>, which is what unlocks the resource. Sending funds straight to the address below from an ordinary wallet will reach the merchant but <strong>will NOT unlock this resource and won't be matched to your request</strong> \u2014 there is no custody and no manual-payment desk. Use one of the methods below.</div>
4197
+ <h2>How to pay</h2>
4198
+ <pre>${esc(sd.sdk.install)}</pre>
4199
+ <pre>${esc(sd.sdk.snippet)}</pre>
4200
+ <p>For AI agents (MCP): <code>${esc(sd.mcp.run)}</code> &rarr; tool <code>${esc(sd.mcp.tool)}</code></p>
4201
+ <h2>Payment details <span class="muted">\u2014 what the client pays, NOT a manual-send address</span></h2>
4202
+ <table><thead><tr><th>Scheme</th><th>Chain</th><th>Amount</th><th>Settles to</th></tr></thead>
4203
+ <tbody>${rows}</tbody></table>
4204
+ <p>Docs: <a href="${esc(sd.docs.pay)}">paying</a> &middot; <a href="${esc(sd.docs.home)}">${esc(sd.docs.home)}</a> &middot; <a href="${esc(sd.discovery.openapi)}">${esc(sd.discovery.openapi)}</a></p>
4205
+ <footer>Powered by ${esc(sd.name)} &middot; x402 &middot; no backend, no fee, settled straight to the merchant's wallet (no custody).</footer>
4206
+ </main></body></html>`;
4207
+ }
4208
+
4093
4209
  // src/facilitator.ts
4094
4210
  async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
4095
4211
  const base2 = url.replace(/\/+$/, "");
@@ -4100,7 +4216,8 @@ async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
4100
4216
  if (!res.ok) return void 0;
4101
4217
  const body = await res.json();
4102
4218
  const kinds = Array.isArray(_optionalChain([body, 'optionalAccess', _64 => _64.kinds])) ? body.kinds : [];
4103
- const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _65 => _65.scheme]) === "exact" && _optionalChain([k, 'optionalAccess', _66 => _66.network]) === network);
4219
+ const want = normalizeNetwork(network);
4220
+ const kind = kinds.find((k) => _optionalChain([k, 'optionalAccess', _65 => _65.scheme]) === "exact" && normalizeNetwork(String(_nullishCoalesce(_optionalChain([k, 'optionalAccess', _66 => _66.network]), () => ( "")))) === want);
4104
4221
  const fp = _optionalChain([kind, 'optionalAccess', _67 => _67.extra, 'optionalAccess', _68 => _68.feePayer]);
4105
4222
  return typeof fp === "string" ? fp : void 0;
4106
4223
  } catch (e32) {
@@ -4109,6 +4226,33 @@ async function fetchFacilitatorFeePayer(url, network, timeoutMs = 8e3) {
4109
4226
  clearTimeout(timer);
4110
4227
  }
4111
4228
  }
4229
+ function parseFacilitatorSupported(body) {
4230
+ const kinds = _optionalChain([body, 'optionalAccess', _69 => _69.kinds]);
4231
+ if (!Array.isArray(kinds)) return [];
4232
+ const out = [];
4233
+ for (const k of kinds) {
4234
+ if (!k || typeof k !== "object") continue;
4235
+ const o = k;
4236
+ if (typeof o.scheme !== "string" || typeof o.network !== "string") continue;
4237
+ const fp = _optionalChain([o, 'access', _70 => _70.extra, 'optionalAccess', _71 => _71.feePayer]);
4238
+ out.push({ scheme: o.scheme, network: o.network, ...typeof fp === "string" ? { feePayer: fp } : {} });
4239
+ }
4240
+ return out;
4241
+ }
4242
+ async function facilitatorCoverage(url, timeoutMs = 8e3) {
4243
+ const base2 = url.replace(/\/+$/, "");
4244
+ const ctrl = new AbortController();
4245
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
4246
+ try {
4247
+ const res = await fetch(`${base2}/supported`, { signal: ctrl.signal });
4248
+ if (!res.ok) return [];
4249
+ return parseFacilitatorSupported(await res.json());
4250
+ } catch (e33) {
4251
+ return [];
4252
+ } finally {
4253
+ clearTimeout(timer);
4254
+ }
4255
+ }
4112
4256
  function safeStringify(value) {
4113
4257
  return JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v);
4114
4258
  }
@@ -4132,7 +4276,7 @@ async function post(url, body, headers) {
4132
4276
  let json = null;
4133
4277
  try {
4134
4278
  json = await res.json();
4135
- } catch (e33) {
4279
+ } catch (e34) {
4136
4280
  }
4137
4281
  return { status: res.status, json };
4138
4282
  }
@@ -4386,22 +4530,47 @@ function createPaymentGate(options) {
4386
4530
  const specs = await ready();
4387
4531
  const nonce = genNonce();
4388
4532
  const bazaar = options.discovery ? { bazaar: buildBazaarExtension(options.discovery === true ? {} : options.discovery) } : void 0;
4389
- const extensions = { ...bazaar, ..._optionalChain([opts, 'optionalAccess', _69 => _69.extensions]) };
4533
+ const accepts = buildAccepts(specs, nonce);
4534
+ const selfDescribe = options.selfDescribe === false ? void 0 : buildSelfDescription({
4535
+ accepts,
4536
+ instruction: describeChallenge({ x402Version: 2, resource: { url: resourceUrl }, accepts })
4537
+ });
4538
+ const rejectionExt = _nullishCoalesce(_optionalChain([opts, 'optionalAccess', _72 => _72.extensions]), () => ( {}));
4539
+ const rejectionPiprail = _nullishCoalesce(rejectionExt.piprail, () => ( {}));
4540
+ const bodyPiprail = { ..._nullishCoalesce(selfDescribe, () => ( {})), ...rejectionPiprail };
4541
+ const bodyExtensions = {
4542
+ ...bazaar,
4543
+ ...rejectionExt,
4544
+ ...Object.keys(bodyPiprail).length > 0 ? { piprail: bodyPiprail } : {}
4545
+ };
4546
+ const headerExtensions = {
4547
+ ...bazaar,
4548
+ ...Object.keys(rejectionPiprail).length > 0 ? { piprail: rejectionPiprail } : {}
4549
+ };
4390
4550
  const challenge2 = {
4391
4551
  x402Version: 2,
4392
4552
  resource: {
4393
4553
  url: resourceUrl,
4394
4554
  ...options.description ? { description: options.description } : {}
4395
4555
  },
4396
- accepts: buildAccepts(specs, nonce),
4397
- ..._optionalChain([opts, 'optionalAccess', _70 => _70.error]) ? { error: opts.error } : {},
4398
- ...Object.keys(extensions).length > 0 ? { extensions } : {}
4556
+ accepts,
4557
+ ..._optionalChain([opts, 'optionalAccess', _73 => _73.error]) ? { error: opts.error } : {},
4558
+ ...Object.keys(bodyExtensions).length > 0 ? { extensions: bodyExtensions } : {}
4559
+ };
4560
+ const headerChallenge = {
4561
+ ...challenge2,
4562
+ ...Object.keys(headerExtensions).length > 0 ? { extensions: headerExtensions } : { extensions: void 0 }
4399
4563
  };
4400
- return { challenge: challenge2, requiredHeader: buildChallengeHeader(challenge2) };
4564
+ return { challenge: challenge2, requiredHeader: buildChallengeHeader(headerChallenge) };
4401
4565
  }
4402
4566
  async function challenge(resourceUrl = "") {
4403
4567
  return makeChallenge(resourceUrl);
4404
4568
  }
4569
+ function landingPage(ch) {
4570
+ return renderLandingPage(
4571
+ buildSelfDescription({ accepts: ch.accepts, instruction: describeChallenge(ch) })
4572
+ );
4573
+ }
4405
4574
  async function asChallenge() {
4406
4575
  const { challenge: c, requiredHeader } = await makeChallenge("");
4407
4576
  return { kind: "challenge", challenge: c, requiredHeader, statusCode: 402 };
@@ -4417,7 +4586,7 @@ function createPaymentGate(options) {
4417
4586
  let amountFormatted = receipt.amount;
4418
4587
  try {
4419
4588
  amountFormatted = _chunkU35MG4TFcjs.formatUnits.call(void 0, BigInt(receipt.amount), spec.decimals);
4420
- } catch (e34) {
4589
+ } catch (e35) {
4421
4590
  }
4422
4591
  return {
4423
4592
  ...receipt,
@@ -4431,7 +4600,7 @@ function createPaymentGate(options) {
4431
4600
  if (!options.onPaidError) return;
4432
4601
  try {
4433
4602
  options.onPaidError(error, receipt);
4434
- } catch (e35) {
4603
+ } catch (e36) {
4435
4604
  }
4436
4605
  }
4437
4606
  function fireOnPaid(receipt) {
@@ -4528,7 +4697,7 @@ function createPaymentGate(options) {
4528
4697
  if ("transaction" in exact.payload) {
4529
4698
  try {
4530
4699
  nonce = Buffer.from(exact.payload.transaction, "base64").toString("base64");
4531
- } catch (e36) {
4700
+ } catch (e37) {
4532
4701
  nonce = exact.payload.transaction;
4533
4702
  }
4534
4703
  } else if ("permit2Authorization" in exact.payload) {
@@ -4597,7 +4766,7 @@ function createPaymentGate(options) {
4597
4766
  if (exact) return verifyExact(exact);
4598
4767
  return asChallenge();
4599
4768
  }
4600
- return { challenge, verify, describe };
4769
+ return { challenge, verify, describe, landingPage };
4601
4770
  }
4602
4771
  function requirePayment(options) {
4603
4772
  const gate = createPaymentGate(options);
@@ -4637,6 +4806,39 @@ function normaliseHeader(value) {
4637
4806
  return value;
4638
4807
  }
4639
4808
 
4809
+ // src/facilitators.ts
4810
+ var KNOWN_FACILITATORS = {
4811
+ // PayAI — keyless (no API key), sponsors the gas. Verified 2026-06-14 against
4812
+ // https://facilitator.payai.network/supported (exact · eip155:8453) + the live demo.
4813
+ "eip155:8453": [
4814
+ {
4815
+ url: "https://facilitator.payai.network",
4816
+ keyless: true,
4817
+ schemes: ["exact"],
4818
+ settles: ["eip3009"],
4819
+ note: "PayAI \u2014 keyless, sponsors gas (Base USDC EIP-3009)"
4820
+ }
4821
+ ],
4822
+ // PayAI on Solana — keyless fee-payer sponsor for the SVM exact rail. Verified 2026-06-14.
4823
+ "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp": [
4824
+ {
4825
+ url: "https://facilitator.payai.network",
4826
+ keyless: true,
4827
+ schemes: ["exact"],
4828
+ settles: ["svm"],
4829
+ note: "PayAI \u2014 keyless fee-payer sponsor (Solana SPL SVM)"
4830
+ }
4831
+ ]
4832
+ };
4833
+ function knownFacilitatorsFor(network) {
4834
+ return _nullishCoalesce(KNOWN_FACILITATORS[network], () => ( []));
4835
+ }
4836
+ function firstKeylessFacilitator(network, method) {
4837
+ return knownFacilitatorsFor(network).find(
4838
+ (f) => f.keyless && f.schemes.includes("exact") && (method === void 0 || f.settles.includes(method))
4839
+ );
4840
+ }
4841
+
4640
4842
  // src/receipts.ts
4641
4843
  var DEFAULT_RETRIES = 5;
4642
4844
  var DEFAULT_TIMEOUT_MS = 1e4;
@@ -4649,7 +4851,7 @@ function isRetryableStatus(status) {
4649
4851
  }
4650
4852
  var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
4651
4853
  async function signBody(secret, body) {
4652
- const subtle = _optionalChain([globalThis, 'access', _71 => _71.crypto, 'optionalAccess', _72 => _72.subtle]);
4854
+ const subtle = _optionalChain([globalThis, 'access', _74 => _74.crypto, 'optionalAccess', _75 => _75.subtle]);
4653
4855
  if (!subtle) return null;
4654
4856
  try {
4655
4857
  const enc = new TextEncoder();
@@ -4659,7 +4861,7 @@ async function signBody(secret, body) {
4659
4861
  const sig = await subtle.sign("HMAC", key, enc.encode(body));
4660
4862
  const hex = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
4661
4863
  return `sha256=${hex}`;
4662
- } catch (e37) {
4864
+ } catch (e38) {
4663
4865
  return null;
4664
4866
  }
4665
4867
  }
@@ -4719,8 +4921,8 @@ async function deliverReceipt(receipt, options) {
4719
4921
  const retryable = status === void 0 ? true : isRetryableStatus(status);
4720
4922
  const willRetry = !ok && retryable && attempt < maxAttempts;
4721
4923
  try {
4722
- _optionalChain([onAttempt, 'optionalCall', _73 => _73({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
4723
- } catch (e38) {
4924
+ _optionalChain([onAttempt, 'optionalCall', _76 => _76({ attempt, ok, ...status !== void 0 ? { status } : {}, ...error ? { error } : {}, willRetry })]);
4925
+ } catch (e39) {
4724
4926
  }
4725
4927
  if (ok) return { delivered: true, attempts: attempt, status };
4726
4928
  if (!willRetry) {
@@ -4822,4 +5024,15 @@ async function deliverReceipt(receipt, options) {
4822
5024
 
4823
5025
 
4824
5026
 
4825
- exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkU35MG4TFcjs.ConfirmationTimeoutError; exports.DIRECTORY_INFO = DIRECTORY_INFO; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.GENERATOR = GENERATOR; exports.HEADER_REQUIRED = HEADER_REQUIRED; exports.HEADER_RESPONSE = HEADER_RESPONSE; exports.HEADER_RESPONSE_V1 = HEADER_RESPONSE_V1; exports.HEADER_SIGNATURE = HEADER_SIGNATURE; exports.HEADER_SIGNATURE_V1 = HEADER_SIGNATURE_V1; exports.InsufficientFundsError = _chunkU35MG4TFcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkU35MG4TFcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkU35MG4TFcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkU35MG4TFcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkU35MG4TFcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkU35MG4TFcjs.NonReplayableBodyError; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.PERMIT2_PROXY_CHAIN_IDS = PERMIT2_PROXY_CHAIN_IDS; exports.PERMIT2_WITNESS_TYPES = PERMIT2_WITNESS_TYPES; exports.PIPRAIL_AGENT_GUIDE = PIPRAIL_AGENT_GUIDE; exports.PaymentDeclinedError = _chunkU35MG4TFcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkU35MG4TFcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkU35MG4TFcjs.PipRailError; exports.REGISTER_ATTRIBUTION = REGISTER_ATTRIBUTION; exports.RecipientNotReadyError = _chunkU35MG4TFcjs.RecipientNotReadyError; exports.SettlementError = _chunkU35MG4TFcjs.SettlementError; exports.UnknownTokenError = _chunkU35MG4TFcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkU35MG4TFcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkU35MG4TFcjs.UnsupportedSchemeError; exports.WalletRequiredError = _chunkU35MG4TFcjs.WalletRequiredError; exports.WrongChainError = _chunkU35MG4TFcjs.WrongChainError; exports.WrongFamilyError = _chunkU35MG4TFcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_PERMIT2_PROXY; exports.agentGuide = agentGuide; exports.appendAttribution = appendAttribution; exports.buildBazaarExtension = buildBazaarExtension; exports.buildChallengeHeader = buildChallengeHeader; exports.buildExactAuthorization = buildExactAuthorization; exports.buildExactSignatureHeader = buildExactSignatureHeader; exports.buildOpenApi = buildOpenApi; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSignatureHeader = buildSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.classifyChallenge = classifyChallenge; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.deliverReceipt = deliverReceipt; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.explainDecline = explainDecline; exports.formatSpendReport = formatSpendReport; exports.getDirectoryInfo = getDirectoryInfo; exports.isPermit2ProxyChain = isPermit2ProxyChain; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseReceipt = parseReceipt; exports.parseSettleResponse = parseSettleResponse; exports.parseSignatureHeader = parseSignatureHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.readExactDomain = readExactDomain; exports.register402Index = register402Index; exports.registerDriver = registerDriver; exports.registerX402Scan = registerX402Scan; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.searchOpenIndexes = searchOpenIndexes; exports.settleViaFacilitator = settleViaFacilitator; exports.summarizePlan = summarizePlan; exports.toInsufficientFundsError = _chunkU35MG4TFcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
5027
+
5028
+
5029
+
5030
+
5031
+
5032
+
5033
+
5034
+
5035
+
5036
+
5037
+
5038
+ exports.BRAND = BRAND; exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkU35MG4TFcjs.ConfirmationTimeoutError; exports.DIRECTORY_INFO = DIRECTORY_INFO; exports.EIP3009_TYPES = EIP3009_TYPES; exports.EXACT_NETWORK_SLUGS = EXACT_NETWORK_SLUGS; exports.GENERATOR = GENERATOR; exports.HEADER_REQUIRED = HEADER_REQUIRED; exports.HEADER_RESPONSE = HEADER_RESPONSE; exports.HEADER_RESPONSE_V1 = HEADER_RESPONSE_V1; exports.HEADER_SIGNATURE = HEADER_SIGNATURE; exports.HEADER_SIGNATURE_V1 = HEADER_SIGNATURE_V1; exports.InsufficientFundsError = _chunkU35MG4TFcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkU35MG4TFcjs.InvalidEnvelopeError; exports.KNOWN_FACILITATORS = KNOWN_FACILITATORS; exports.MaxRetriesExceededError = _chunkU35MG4TFcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkU35MG4TFcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkU35MG4TFcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkU35MG4TFcjs.NonReplayableBodyError; exports.PERMIT2_ADDRESS = PERMIT2_ADDRESS; exports.PERMIT2_PROXY_CHAIN_IDS = PERMIT2_PROXY_CHAIN_IDS; exports.PERMIT2_WITNESS_TYPES = PERMIT2_WITNESS_TYPES; exports.PIPRAIL_AGENT_GUIDE = PIPRAIL_AGENT_GUIDE; exports.POWERED_BY = POWERED_BY; exports.PaymentDeclinedError = _chunkU35MG4TFcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkU35MG4TFcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkU35MG4TFcjs.PipRailError; exports.REGISTER_ATTRIBUTION = REGISTER_ATTRIBUTION; exports.RecipientNotReadyError = _chunkU35MG4TFcjs.RecipientNotReadyError; exports.SettlementError = _chunkU35MG4TFcjs.SettlementError; exports.UnknownTokenError = _chunkU35MG4TFcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkU35MG4TFcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkU35MG4TFcjs.UnsupportedSchemeError; exports.WalletRequiredError = _chunkU35MG4TFcjs.WalletRequiredError; exports.WrongChainError = _chunkU35MG4TFcjs.WrongChainError; exports.WrongFamilyError = _chunkU35MG4TFcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_PERMIT2_PROXY; exports.agentGuide = agentGuide; exports.appendAttribution = appendAttribution; exports.buildBazaarExtension = buildBazaarExtension; exports.buildChallengeHeader = buildChallengeHeader; exports.buildExactAuthorization = buildExactAuthorization; exports.buildExactSignatureHeader = buildExactSignatureHeader; exports.buildOpenApi = buildOpenApi; exports.buildReceiptHeader = buildReceiptHeader; exports.buildSelfDescription = buildSelfDescription; exports.buildSignatureHeader = buildSignatureHeader; exports.buildWellKnownX402 = buildWellKnownX402; exports.buildX402DnsTxt = buildX402DnsTxt; exports.chainIdForExactNetwork = chainIdForExactNetwork; exports.claim402IndexDomain = claim402IndexDomain; exports.classifyChallenge = classifyChallenge; exports.createPaymentGate = createPaymentGate; exports.decorateOutcome = decorateOutcome; exports.deliverReceipt = deliverReceipt; exports.describeChallenge = describeChallenge; exports.discoveryHeaders = discoveryHeaders; exports.eip3009Abi = eip3009Abi; exports.encodeXPaymentHeader = encodeXPaymentHeader; exports.evaluatePolicy = evaluatePolicy; exports.explainDecline = explainDecline; exports.facilitatorCoverage = facilitatorCoverage; exports.firstKeylessFacilitator = firstKeylessFacilitator; exports.formatSpendReport = formatSpendReport; exports.getDirectoryInfo = getDirectoryInfo; exports.isPermit2ProxyChain = isPermit2ProxyChain; exports.knownFacilitatorsFor = knownFacilitatorsFor; exports.normalizeNetwork = normalizeNetwork; exports.parseChallenge = parseChallenge; exports.parseExactPaymentHeader = parseExactPaymentHeader; exports.parseExactRequirements = parseExactRequirements; exports.parseFacilitatorSupported = parseFacilitatorSupported; exports.parseReceipt = parseReceipt; exports.parseSettleResponse = parseSettleResponse; exports.parseSignatureHeader = parseSignatureHeader; exports.paymentTools = paymentTools; exports.pickAccept = pickAccept; exports.planAcross = planAcross; exports.readExactDomain = readExactDomain; exports.register402Index = register402Index; exports.registerDriver = registerDriver; exports.registerX402Scan = registerX402Scan; exports.renderLandingPage = renderLandingPage; exports.requirePayment = requirePayment; exports.resolveChain = resolveChain; exports.searchOpenIndexes = searchOpenIndexes; exports.settleViaFacilitator = settleViaFacilitator; exports.summarizePlan = summarizePlan; exports.toInsufficientFundsError = _chunkU35MG4TFcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;