@piprail/sdk 1.22.1 → 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/dist/index.js CHANGED
@@ -511,7 +511,6 @@ import {
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 = 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} ${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 = first.extra ?? {};
3692
+ const amount = extra.amountFormatted ?? first.amount;
3693
+ const token = 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 = (descriptor.method ?? "GET").toUpperCase();
4016
4081
  const queryParams = 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(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(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(r.amountFormatted ?? r.amount)} ${esc(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(body?.kinds) ? body.kinds : [];
4103
- const kind = kinds.find((k) => k?.scheme === "exact" && k?.network === network);
4219
+ const want = normalizeNetwork(network);
4220
+ const kind = kinds.find((k) => k?.scheme === "exact" && normalizeNetwork(String(k?.network ?? "")) === want);
4104
4221
  const fp = kind?.extra?.feePayer;
4105
4222
  return typeof fp === "string" ? fp : void 0;
4106
4223
  } catch {
@@ -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 = body?.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 = o.extra?.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 {
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
  }
@@ -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, ...opts?.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 = opts?.extensions ?? {};
4539
+ const rejectionPiprail = rejectionExt.piprail ?? {};
4540
+ const bodyPiprail = { ...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),
4556
+ accepts,
4397
4557
  ...opts?.error ? { error: opts.error } : {},
4398
- ...Object.keys(extensions).length > 0 ? { extensions } : {}
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 };
@@ -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 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;
@@ -4741,6 +4943,7 @@ async function deliverReceipt(receipt, options) {
4741
4943
  };
4742
4944
  }
4743
4945
  export {
4946
+ BRAND,
4744
4947
  CHAINS,
4745
4948
  ConfirmationTimeoutError,
4746
4949
  DIRECTORY_INFO,
@@ -4754,6 +4957,7 @@ export {
4754
4957
  HEADER_SIGNATURE_V1,
4755
4958
  InsufficientFundsError,
4756
4959
  InvalidEnvelopeError,
4960
+ KNOWN_FACILITATORS,
4757
4961
  MaxRetriesExceededError,
4758
4962
  MissingDriverError,
4759
4963
  NoCompatibleAcceptError,
@@ -4762,6 +4966,7 @@ export {
4762
4966
  PERMIT2_PROXY_CHAIN_IDS,
4763
4967
  PERMIT2_WITNESS_TYPES,
4764
4968
  PIPRAIL_AGENT_GUIDE,
4969
+ POWERED_BY,
4765
4970
  PaymentDeclinedError,
4766
4971
  PaymentTimeoutError,
4767
4972
  PipRailClient,
@@ -4784,6 +4989,7 @@ export {
4784
4989
  buildExactSignatureHeader,
4785
4990
  buildOpenApi,
4786
4991
  buildReceiptHeader,
4992
+ buildSelfDescription,
4787
4993
  buildSignatureHeader,
4788
4994
  buildWellKnownX402,
4789
4995
  buildX402DnsTxt,
@@ -4793,17 +4999,23 @@ export {
4793
4999
  createPaymentGate,
4794
5000
  decorateOutcome,
4795
5001
  deliverReceipt,
5002
+ describeChallenge,
5003
+ discoveryHeaders,
4796
5004
  eip3009Abi,
4797
5005
  encodeXPaymentHeader,
4798
5006
  evaluatePolicy,
4799
5007
  explainDecline,
5008
+ facilitatorCoverage,
5009
+ firstKeylessFacilitator,
4800
5010
  formatSpendReport,
4801
5011
  getDirectoryInfo,
4802
5012
  isPermit2ProxyChain,
5013
+ knownFacilitatorsFor,
4803
5014
  normalizeNetwork,
4804
5015
  parseChallenge,
4805
5016
  parseExactPaymentHeader,
4806
5017
  parseExactRequirements,
5018
+ parseFacilitatorSupported,
4807
5019
  parseReceipt,
4808
5020
  parseSettleResponse,
4809
5021
  parseSignatureHeader,
@@ -4814,6 +5026,7 @@ export {
4814
5026
  register402Index,
4815
5027
  registerDriver,
4816
5028
  registerX402Scan,
5029
+ renderLandingPage,
4817
5030
  requirePayment,
4818
5031
  resolveChain,
4819
5032
  searchOpenIndexes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.22.1",
3
+ "version": "1.23.0",
4
4
  "description": "Accept x402 crypto payments across 29 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",