@piprail/sdk 1.19.0 → 1.20.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/CHANGELOG.md CHANGED
@@ -4,6 +4,58 @@ 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.20.1] — 2026-06-11 — gate replay store: bounded + exception-safe
8
+
9
+ Patch — internal robustness on the gate's built-in replay protection. No API change, no visible
10
+ behaviour change, defaults identical.
11
+
12
+ ### Fixed
13
+ - **Bounded the default used-proof set.** It's now evicted past the replay window
14
+ (`maxTimeoutSeconds`) instead of growing for the life of the process — safe because the driver's
15
+ recency check rejects any proof that old anyway, so a dropped entry still can't be replayed. A
16
+ long-lived gate no longer slowly leaks memory. Custom `isUsed`/`markUsed` stores are unaffected
17
+ (give them a TTL = the window).
18
+ - **`onchain-proof` verification is now claim-release exception-safe.** If a driver's `verify()`
19
+ *throws* (an unexpected RPC exception) rather than returning a rejection, the gate now releases the
20
+ proof reservation before rethrowing — so a transient blip can't permanently burn an otherwise-valid
21
+ proof. This matches the `exact` path, which already did it.
22
+
23
+ ### Docs
24
+ - Rewrote **[Replay protection & recovery](https://docs.piprail.com/accepting-payments/replay-protection/)**
25
+ with the full "paid but didn't receive — what happens to the payment?" model (a recoverability
26
+ matrix, the at-most-once-by-design rationale, the bounded-memory behaviour, and the client's
27
+ never-re-pay `.ref` recovery).
28
+
29
+ ## [1.20.0] — 2026-06-11 — discovery hardening: conformance-locked, accurate timing, PipRail-attributed
30
+
31
+ A minor release focused on the discovery/registration subsystem — verified live against the real
32
+ 402 Index + the deployed demo's wire, then locked as a contract. No payment-path change; the lazy-chunk
33
+ invariant holds.
34
+
35
+ ### Changed — registration attribution is now ON by default (opt-out)
36
+ - `client.register()` / `register402Index()` now attribute the listing to PipRail by default: a
37
+ `via: '@piprail/sdk'` provenance field **plus** a tasteful `· Built with @piprail/sdk` suffix on the
38
+ **description** (the one field an index displays) — the same unobtrusive "Made with X" marker as the
39
+ OpenAPI `x-generator`. It's metadata only (never changes how a resource is paid, ranked, or found),
40
+ is **deduped** (never double-stamps a description already naming PipRail), never fabricates a missing
41
+ description, and is **length-guarded**. **Opt out with `attribution: false`.** (Was opt-in/off before
42
+ — this is the one default change in the release; everything else is additive.)
43
+ - New pure exports: **`appendAttribution(description)`** and **`REGISTER_ATTRIBUTION`**.
44
+
45
+ ### Fixed — registration timing/visibility now matches reality
46
+ - The 402 Index caveat + register `detail` overstated the gate ("not searchable until approved"). Live
47
+ evidence (the demo is self-registered, `domain_verified: 0`, yet fully searchable) shows a self-
48
+ registered listing becomes searchable once it passes 402 Index's automated health + payment-validity
49
+ checks — no domain verification required. Domain verification is the **instant, guaranteed** path
50
+ (+ a verified badge). Wording corrected across `DIRECTORY_INFO`, the docs, and the llms files.
51
+
52
+ ### Added — an x402 conformance contract (test)
53
+ - `test/discovery-conformance.test.ts` encodes, as executable predicates, the x402 v2 PaymentRequirements
54
+ envelope (CAIP-2 networks, atomic-unit string amounts, the EIP-712 domain on an `exact` rail) **and**
55
+ x402scan's `validateResource` gate (HTTPS · v2 · non-empty accepts · resolvable input schema · ≥1
56
+ Base/Solana rail), asserted against the live demo's captured wire and the SDK's own generated output —
57
+ so a regression that would make a PipRail endpoint un-listable fails CI.
58
+
7
59
  ## [1.19.0] — 2026-06-11 — gasless `exact` on 3 more chains (Monad · zkSync Era · Injective)
8
60
 
9
61
  A minor, fully additive release — defaults byte-identical (`exact` stays opt-in), no new dependency,
package/dist/index.cjs CHANGED
@@ -1881,7 +1881,7 @@ var DIRECTORY_INFO = {
1881
1881
  chains: null,
1882
1882
  onSuccess: "pending-review",
1883
1883
  readByDiscover: true,
1884
- caveat: "402 Index probes your URL on submit, then lists it as PENDING REVIEW \u2014 a self-registered resource is NOT in search until approved. Verify your domain on 402index.io for instant approval; otherwise it appears after manual review, so retry discover() later."
1884
+ caveat: "402 Index probes your URL on submit (rejecting anything that does not return a real 402), then lists it \u2014 a self-registered resource becomes searchable once it passes automated health + payment-validity checks, with NO domain verification required (observed live: searchable within ~2 days for a healthy endpoint). Verify your domain on 402index.io for instant, guaranteed approval + a verified badge, which also flips every pending listing on that domain live at once."
1885
1885
  },
1886
1886
  x402scan: {
1887
1887
  source: "x402scan",
@@ -2046,16 +2046,18 @@ function railFrom402IndexFields(o) {
2046
2046
  }
2047
2047
  async function register402Index(input) {
2048
2048
  try {
2049
+ const attributionOn = input.attribution !== false;
2050
+ const description = attributionOn ? appendAttribution(input.description) : input.description;
2049
2051
  const payload = {
2050
2052
  url: input.url,
2051
2053
  name: _nullishCoalesce(input.name, () => ( hostOf(input.url))),
2052
2054
  protocol: "x402",
2053
- ...input.description ? { description: input.description } : {},
2055
+ ...description ? { description } : {},
2054
2056
  ...typeof input.priceUsd === "number" ? { price_usd: input.priceUsd } : {},
2055
2057
  ...input.asset ? { payment_asset: input.asset } : {},
2056
2058
  ...input.network ? { payment_network: input.network } : {},
2057
2059
  ...input.method ? { http_method: input.method.toUpperCase() } : {},
2058
- ...input.attribution ? { via: "@piprail/sdk" } : {}
2060
+ ...attributionOn ? { via: "@piprail/sdk" } : {}
2059
2061
  };
2060
2062
  const res = await fetch(INDEX402_REGISTER, {
2061
2063
  method: "POST",
@@ -2071,7 +2073,7 @@ async function register402Index(input) {
2071
2073
  ok: true,
2072
2074
  status: res.status,
2073
2075
  ...live ? { visibility: "live" } : {},
2074
- detail: _nullishCoalesce(msg, () => ( (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval).")))
2076
+ detail: _nullishCoalesce(msg, () => ( (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 probed on submit, then searchable once it passes automated health + payment checks (verify your domain on 402index.io for instant approval + a verified badge).")))
2075
2077
  };
2076
2078
  }
2077
2079
  const why = await readIndexError(res);
@@ -2302,6 +2304,13 @@ function hostOf(url) {
2302
2304
  function errMsg(err) {
2303
2305
  return err instanceof Error ? err.message : String(err);
2304
2306
  }
2307
+ var REGISTER_ATTRIBUTION = "\xB7 Built with @piprail/sdk";
2308
+ function appendAttribution(description) {
2309
+ if (!description) return description;
2310
+ if (/piprail/i.test(description)) return description;
2311
+ const next = `${description.trimEnd()} ${REGISTER_ATTRIBUTION}`;
2312
+ return next.length <= 500 ? next : description;
2313
+ }
2305
2314
  function encodeBase642(str) {
2306
2315
  if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
2307
2316
  if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
@@ -2823,7 +2832,8 @@ var PipRailClient = (_class2 = class {
2823
2832
  ...opts.asset ? { asset: opts.asset } : {},
2824
2833
  ...networkSlug ? { network: networkSlug } : {},
2825
2834
  ...opts.method ? { method: opts.method } : {},
2826
- ...opts.attribution ? { attribution: true } : {}
2835
+ // Attribution is default-ON; forward an explicit opt-out, else let register402Index default it.
2836
+ ...opts.attribution === false ? { attribution: false } : {}
2827
2837
  })
2828
2838
  );
2829
2839
  } else if (target === "x402scan") {
@@ -4205,14 +4215,23 @@ function createPaymentGate(options) {
4205
4215
  };
4206
4216
  }
4207
4217
  const hasCustomStore = Boolean(options.isUsed || options.markUsed);
4208
- const localUsed = /* @__PURE__ */ new Set();
4218
+ const localUsed = /* @__PURE__ */ new Map();
4219
+ const replayWindowMs = maxTimeoutSeconds * 1e3;
4220
+ function pruneUsed(now) {
4221
+ for (const [key, expiry] of localUsed) {
4222
+ if (expiry > now) break;
4223
+ localUsed.delete(key);
4224
+ }
4225
+ }
4209
4226
  async function claimTx(ref) {
4210
4227
  if (hasCustomStore) {
4211
4228
  return options.isUsed ? Boolean(await options.isUsed(ref)) : false;
4212
4229
  }
4213
4230
  const key = ref.toLowerCase();
4231
+ const now = Date.now();
4232
+ pruneUsed(now);
4214
4233
  if (localUsed.has(key)) return true;
4215
- localUsed.add(key);
4234
+ localUsed.set(key, now + replayWindowMs);
4216
4235
  return false;
4217
4236
  }
4218
4237
  async function settleTx(ref, ok) {
@@ -4372,7 +4391,13 @@ function createPaymentGate(options) {
4372
4391
  }
4373
4392
  const ref = sig.payload.txHash;
4374
4393
  if (await claimTx(ref)) return rejection("tx_already_used", `Proof ${ref} was already redeemed.`);
4375
- const result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
4394
+ let result;
4395
+ try {
4396
+ result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
4397
+ } catch (err) {
4398
+ await settleTx(ref, false);
4399
+ throw err;
4400
+ }
4376
4401
  if (!result.ok) {
4377
4402
  await settleTx(ref, false);
4378
4403
  return rejection(result.error, result.detail);
@@ -4682,4 +4707,6 @@ async function deliverReceipt(receipt, options) {
4682
4707
 
4683
4708
 
4684
4709
 
4685
- exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkPA6YD3HLcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkPA6YD3HLcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkPA6YD3HLcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkPA6YD3HLcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkPA6YD3HLcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkPA6YD3HLcjs.PipRailError; exports.RecipientNotReadyError = _chunkPA6YD3HLcjs.RecipientNotReadyError; exports.SettlementError = _chunkPA6YD3HLcjs.SettlementError; exports.UnknownTokenError = _chunkPA6YD3HLcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkPA6YD3HLcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkPA6YD3HLcjs.UnsupportedSchemeError; exports.WrongChainError = _chunkPA6YD3HLcjs.WrongChainError; exports.WrongFamilyError = _chunkPA6YD3HLcjs.WrongFamilyError; exports.X402_EXACT_PERMIT2_PROXY = X402_EXACT_PERMIT2_PROXY; exports.agentGuide = agentGuide; 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 = _chunkPA6YD3HLcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
4710
+
4711
+
4712
+ exports.CHAINS = CHAINS; exports.ConfirmationTimeoutError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.InsufficientFundsError; exports.InvalidEnvelopeError = _chunkPA6YD3HLcjs.InvalidEnvelopeError; exports.MaxRetriesExceededError = _chunkPA6YD3HLcjs.MaxRetriesExceededError; exports.MissingDriverError = _chunkPA6YD3HLcjs.MissingDriverError; exports.NoCompatibleAcceptError = _chunkPA6YD3HLcjs.NoCompatibleAcceptError; exports.NonReplayableBodyError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.PaymentDeclinedError; exports.PaymentTimeoutError = _chunkPA6YD3HLcjs.PaymentTimeoutError; exports.PipRailClient = PipRailClient; exports.PipRailError = _chunkPA6YD3HLcjs.PipRailError; exports.REGISTER_ATTRIBUTION = REGISTER_ATTRIBUTION; exports.RecipientNotReadyError = _chunkPA6YD3HLcjs.RecipientNotReadyError; exports.SettlementError = _chunkPA6YD3HLcjs.SettlementError; exports.UnknownTokenError = _chunkPA6YD3HLcjs.UnknownTokenError; exports.UnsupportedNetworkError = _chunkPA6YD3HLcjs.UnsupportedNetworkError; exports.UnsupportedSchemeError = _chunkPA6YD3HLcjs.UnsupportedSchemeError; exports.WrongChainError = _chunkPA6YD3HLcjs.WrongChainError; exports.WrongFamilyError = _chunkPA6YD3HLcjs.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 = _chunkPA6YD3HLcjs.toInsufficientFundsError; exports.toInvalidBody = toInvalidBody; exports.verify402IndexDomain = verify402IndexDomain;
package/dist/index.d.cts CHANGED
@@ -4472,11 +4472,14 @@ interface RegisterInput {
4472
4472
  /** HTTP method the resource answers on. Default 'GET'. */
4473
4473
  method?: string;
4474
4474
  /**
4475
- * Opt-in (default off): add a `via: '@piprail/sdk'` tag to the listing payload. It's the
4476
- * MERCHANT's listing on a third-party index, so we never tag it by default; and it's
4477
- * **best-effort** an index may ignore an unrecognised field. The reliable, always-on
4478
- * attribution is the `User-Agent` on the request + the `x-generator` stamp in your
4479
- * emitted `/openapi.json`. Off by default keeps your listing clean and can't be seen as spam.
4475
+ * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the
4476
+ * payload gets a `via: '@piprail/sdk'` provenance field AND a compact Built with
4477
+ * @piprail/sdk` suffix on the description (see {@link appendAttribution}) the one field an
4478
+ * index displays, so the listing is *visibly* built with PipRail as it spreads, the same
4479
+ * unobtrusive "Made with X" marker as the `/openapi.json` `x-generator`. It's metadata only:
4480
+ * never changes how the resource is paid or ranked, never double-stamps a description that
4481
+ * already mentions PipRail, and never fabricates one you didn't provide. The `User-Agent`
4482
+ * carries PipRail on every request regardless.
4480
4483
  */
4481
4484
  attribution?: boolean;
4482
4485
  }
@@ -4603,6 +4606,16 @@ declare function claim402IndexDomain(domainOrUrl: string, opts?: {
4603
4606
  * `servicesCount` approved). No funds move. Never throws.
4604
4607
  */
4605
4608
  declare function verify402IndexDomain(domainOrUrl: string): Promise<DomainVerification>;
4609
+ /** The compact "Made with X" marker {@link appendAttribution} adds to a listing's
4610
+ * description by default (the elegant, universally-accepted Swagger/Hugo pattern).
4611
+ * Middot-separated so it reads as metadata, never as part of the merchant's prose. */
4612
+ declare const REGISTER_ATTRIBUTION = "\u00B7 Built with @piprail/sdk";
4613
+ /** Append {@link REGISTER_ATTRIBUTION} to a listing description — *tastefully*. Pure.
4614
+ * Returns the description unchanged when it's absent (we never fabricate one), already
4615
+ * mentions PipRail (never double-stamp), or the result would exceed a sane listing cap
4616
+ * (≤ 500 chars). This is the only attribution an index actually DISPLAYS, so it's how a
4617
+ * registered listing stays visibly "built with PipRail" — opt out with `attribution:false`. */
4618
+ declare function appendAttribution(description: string | undefined): string | undefined;
4606
4619
 
4607
4620
  interface PaymentPolicy {
4608
4621
  /** Per-payment ceiling, human-readable (e.g. '0.10'). Compared using the
@@ -5105,11 +5118,12 @@ interface RegisterOptions {
5105
5118
  */
5106
5119
  targets?: DiscoverySource[];
5107
5120
  /**
5108
- * Opt-in (default off): tag the listing as built with PipRail (`via: '@piprail/sdk'`).
5109
- * It's a third-party listing, so we never tag it by default and it's best-effort (the
5110
- * index may ignore the field). The always-on, reliable attribution is the request
5111
- * `User-Agent` + the `x-generator` stamp in your emitted `/openapi.json` (see
5112
- * `buildOpenApi`). Leave off unless you specifically want the listing tagged.
5121
+ * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the
5122
+ * listing gets a `via: '@piprail/sdk'` provenance field plus a compact Built with
5123
+ * @piprail/sdk` suffix on the description the same unobtrusive "Made with X" marker as the
5124
+ * `/openapi.json` `x-generator`. Metadata only: it never changes how the resource is paid or
5125
+ * ranked, never double-stamps a description that already names PipRail, and never fabricates
5126
+ * one. The request `User-Agent` carries PipRail regardless.
5113
5127
  */
5114
5128
  attribution?: boolean;
5115
5129
  }
@@ -6685,4 +6699,4 @@ declare const PERMIT2_WITNESS_TYPES: {
6685
6699
  }];
6686
6700
  };
6687
6701
 
6688
- export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DeclineReasonCode, type DeliverAttempt, type DeliverReceiptOptions, type DeliverResult, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactPaymentPayloadAny, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PERMIT2_ADDRESS, PERMIT2_PROXY_CHAIN_IDS, PERMIT2_WITNESS_TYPES, PIPRAIL_AGENT_GUIDE, type PaidReceipt, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, type Permit2Authorization, type Permit2PaymentPayload, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type PolicyDenyCode, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SessionBudget, type SettleOutcome, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendRemaining, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, X402_EXACT_PERMIT2_PROXY, type XrplToken, agentGuide, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildExactSignatureHeader, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createPaymentGate, decorateOutcome, deliverReceipt, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, formatSpendReport, getDirectoryInfo, isPermit2ProxyChain, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSettleResponse, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, summarizePlan, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
6702
+ export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DeclineReasonCode, type DeliverAttempt, type DeliverReceiptOptions, type DeliverResult, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactPaymentPayloadAny, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PERMIT2_ADDRESS, PERMIT2_PROXY_CHAIN_IDS, PERMIT2_WITNESS_TYPES, PIPRAIL_AGENT_GUIDE, type PaidReceipt, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, type Permit2Authorization, type Permit2PaymentPayload, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type PolicyDenyCode, REGISTER_ATTRIBUTION, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SessionBudget, type SettleOutcome, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendRemaining, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, X402_EXACT_PERMIT2_PROXY, type XrplToken, agentGuide, appendAttribution, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildExactSignatureHeader, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createPaymentGate, decorateOutcome, deliverReceipt, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, formatSpendReport, getDirectoryInfo, isPermit2ProxyChain, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSettleResponse, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, summarizePlan, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
package/dist/index.d.ts CHANGED
@@ -4472,11 +4472,14 @@ interface RegisterInput {
4472
4472
  /** HTTP method the resource answers on. Default 'GET'. */
4473
4473
  method?: string;
4474
4474
  /**
4475
- * Opt-in (default off): add a `via: '@piprail/sdk'` tag to the listing payload. It's the
4476
- * MERCHANT's listing on a third-party index, so we never tag it by default; and it's
4477
- * **best-effort** an index may ignore an unrecognised field. The reliable, always-on
4478
- * attribution is the `User-Agent` on the request + the `x-generator` stamp in your
4479
- * emitted `/openapi.json`. Off by default keeps your listing clean and can't be seen as spam.
4475
+ * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the
4476
+ * payload gets a `via: '@piprail/sdk'` provenance field AND a compact Built with
4477
+ * @piprail/sdk` suffix on the description (see {@link appendAttribution}) the one field an
4478
+ * index displays, so the listing is *visibly* built with PipRail as it spreads, the same
4479
+ * unobtrusive "Made with X" marker as the `/openapi.json` `x-generator`. It's metadata only:
4480
+ * never changes how the resource is paid or ranked, never double-stamps a description that
4481
+ * already mentions PipRail, and never fabricates one you didn't provide. The `User-Agent`
4482
+ * carries PipRail on every request regardless.
4480
4483
  */
4481
4484
  attribution?: boolean;
4482
4485
  }
@@ -4603,6 +4606,16 @@ declare function claim402IndexDomain(domainOrUrl: string, opts?: {
4603
4606
  * `servicesCount` approved). No funds move. Never throws.
4604
4607
  */
4605
4608
  declare function verify402IndexDomain(domainOrUrl: string): Promise<DomainVerification>;
4609
+ /** The compact "Made with X" marker {@link appendAttribution} adds to a listing's
4610
+ * description by default (the elegant, universally-accepted Swagger/Hugo pattern).
4611
+ * Middot-separated so it reads as metadata, never as part of the merchant's prose. */
4612
+ declare const REGISTER_ATTRIBUTION = "\u00B7 Built with @piprail/sdk";
4613
+ /** Append {@link REGISTER_ATTRIBUTION} to a listing description — *tastefully*. Pure.
4614
+ * Returns the description unchanged when it's absent (we never fabricate one), already
4615
+ * mentions PipRail (never double-stamp), or the result would exceed a sane listing cap
4616
+ * (≤ 500 chars). This is the only attribution an index actually DISPLAYS, so it's how a
4617
+ * registered listing stays visibly "built with PipRail" — opt out with `attribution:false`. */
4618
+ declare function appendAttribution(description: string | undefined): string | undefined;
4606
4619
 
4607
4620
  interface PaymentPolicy {
4608
4621
  /** Per-payment ceiling, human-readable (e.g. '0.10'). Compared using the
@@ -5105,11 +5118,12 @@ interface RegisterOptions {
5105
5118
  */
5106
5119
  targets?: DiscoverySource[];
5107
5120
  /**
5108
- * Opt-in (default off): tag the listing as built with PipRail (`via: '@piprail/sdk'`).
5109
- * It's a third-party listing, so we never tag it by default and it's best-effort (the
5110
- * index may ignore the field). The always-on, reliable attribution is the request
5111
- * `User-Agent` + the `x-generator` stamp in your emitted `/openapi.json` (see
5112
- * `buildOpenApi`). Leave off unless you specifically want the listing tagged.
5121
+ * Attribute the listing to PipRail. **Default ON** (set `false` to opt out). When on, the
5122
+ * listing gets a `via: '@piprail/sdk'` provenance field plus a compact Built with
5123
+ * @piprail/sdk` suffix on the description the same unobtrusive "Made with X" marker as the
5124
+ * `/openapi.json` `x-generator`. Metadata only: it never changes how the resource is paid or
5125
+ * ranked, never double-stamps a description that already names PipRail, and never fabricates
5126
+ * one. The request `User-Agent` carries PipRail regardless.
5113
5127
  */
5114
5128
  attribution?: boolean;
5115
5129
  }
@@ -6685,4 +6699,4 @@ declare const PERMIT2_WITNESS_TYPES: {
6685
6699
  }];
6686
6700
  };
6687
6701
 
6688
- export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DeclineReasonCode, type DeliverAttempt, type DeliverReceiptOptions, type DeliverResult, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactPaymentPayloadAny, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PERMIT2_ADDRESS, PERMIT2_PROXY_CHAIN_IDS, PERMIT2_WITNESS_TYPES, PIPRAIL_AGENT_GUIDE, type PaidReceipt, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, type Permit2Authorization, type Permit2PaymentPayload, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type PolicyDenyCode, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SessionBudget, type SettleOutcome, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendRemaining, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, X402_EXACT_PERMIT2_PROXY, type XrplToken, agentGuide, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildExactSignatureHeader, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createPaymentGate, decorateOutcome, deliverReceipt, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, formatSpendReport, getDirectoryInfo, isPermit2ProxyChain, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSettleResponse, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, summarizePlan, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
6702
+ export { type AcceptOption, type AddressId, type AgentTool, type AlgorandToken, type AptosToken, type AssetId, type BazaarExtension, type BuildExactParams, CHAINS, type Caip2, type ChainFamily, type ChainInput, type ChainName, type ChainPreset, type ChainSelector, type ChallengeTriage, type ChallengeVerdict, type ConfirmInfo, ConfirmationTimeoutError, type CostEstimate, DIRECTORY_INFO, type DeclineReasonCode, type DeliverAttempt, type DeliverReceiptOptions, type DeliverResult, type DirectoryInfo, type DiscoverOptions, type DiscoveredRail, type DiscoveredResource, type DiscoveryDescriptor, type DiscoverySigner, type DiscoverySource, type DomainClaim, type DomainVerification, EIP3009_TYPES, EXACT_NETWORK_SLUGS, type EvmToken, type ExactAccept, type ExactAuthorization, type ExactAuthorizationWire, type ExactPaymentPayload, type ExactPaymentPayloadAny, type ExactRailOption, type ExpressLikeMiddleware, type ExpressLikeNext, type ExpressLikeRequest, type ExpressLikeResponse, type FacilitatorConfig, type FacilitatorPaymentRequirements, GENERATOR, HEADER_REQUIRED, HEADER_RESPONSE, HEADER_RESPONSE_V1, HEADER_SIGNATURE, HEADER_SIGNATURE_V1, InsufficientFundsError, InvalidEnvelopeError, type ListingVisibility, type ManifestInput, MaxRetriesExceededError, MissingDriverError, type NearToken, NoCompatibleAcceptError, NonReplayableBodyError, type OpenApiDocument, type OpenApiOperation, PERMIT2_ADDRESS, PERMIT2_PROXY_CHAIN_IDS, PERMIT2_WITNESS_TYPES, PIPRAIL_AGENT_GUIDE, type PaidReceipt, type ParsedExactPayment, type PayBlocker, type PayOption, type PayWarning, PaymentDeclinedError, type PaymentDriver, type PaymentGate, type PaymentIntent, type PaymentPlan, type PaymentPolicy, type PaymentRail, type PaymentScheme, PaymentTimeoutError, type Permit2Authorization, type Permit2PaymentPayload, PipRailClient, type PipRailClientOptions, type PipRailCostQuote, PipRailError, type PipRailEvent, type PipRailQuote, type PolicyDecision, type PolicyDenyCode, REGISTER_ATTRIBUTION, RecipientNotReadyError, type RecipientReason, type RegisterInput, type RegisterOptions, type RegisterOutcome, type RequirePaymentOptions, type ResolveOptions, type ResolvedChain, type ResolvedNetwork, type ResolvedToken, type ResourceDescription, type SearchOpenIndexesOptions, type SessionBudget, type SettleOutcome, type SettleViaFacilitatorInput, SettlementError, type SolanaToken, type SpendAssetTotal, type SpendRecord, type SpendRemaining, type SpendSummary, type StellarToken, type SuiToken, type TokenInfo, type TokenInput, type TonToken, type ToolAnnotations, type TronToken, UnknownTokenError, UnsupportedNetworkError, UnsupportedSchemeError, type VerifyErrorCode, type VerifyPaymentResult, type VerifyResult, type WalletBalance, type WalletHandle, type WalletInput, type WellKnownX402, WrongChainError, WrongFamilyError, type X402AcceptEntry, type X402AnyAccept, type X402Challenge, type X402DnsRecord, type X402ExactAcceptEntry, type X402InvalidBody, type X402PaymentSignature, type X402Receipt, type X402ResourceObject, X402_EXACT_PERMIT2_PROXY, type XrplToken, agentGuide, appendAttribution, buildBazaarExtension, buildChallengeHeader, buildExactAuthorization, buildExactSignatureHeader, buildOpenApi, buildReceiptHeader, buildSignatureHeader, buildWellKnownX402, buildX402DnsTxt, chainIdForExactNetwork, claim402IndexDomain, classifyChallenge, createPaymentGate, decorateOutcome, deliverReceipt, eip3009Abi, encodeXPaymentHeader, evaluatePolicy, explainDecline, formatSpendReport, getDirectoryInfo, isPermit2ProxyChain, normalizeNetwork, parseChallenge, parseExactPaymentHeader, parseExactRequirements, parseReceipt, parseSettleResponse, parseSignatureHeader, paymentTools, pickAccept, planAcross, readExactDomain, register402Index, registerDriver, registerX402Scan, requirePayment, resolveChain, searchOpenIndexes, settleViaFacilitator, summarizePlan, toInsufficientFundsError, toInvalidBody, verify402IndexDomain };
package/dist/index.js CHANGED
@@ -1881,7 +1881,7 @@ var DIRECTORY_INFO = {
1881
1881
  chains: null,
1882
1882
  onSuccess: "pending-review",
1883
1883
  readByDiscover: true,
1884
- caveat: "402 Index probes your URL on submit, then lists it as PENDING REVIEW \u2014 a self-registered resource is NOT in search until approved. Verify your domain on 402index.io for instant approval; otherwise it appears after manual review, so retry discover() later."
1884
+ caveat: "402 Index probes your URL on submit (rejecting anything that does not return a real 402), then lists it \u2014 a self-registered resource becomes searchable once it passes automated health + payment-validity checks, with NO domain verification required (observed live: searchable within ~2 days for a healthy endpoint). Verify your domain on 402index.io for instant, guaranteed approval + a verified badge, which also flips every pending listing on that domain live at once."
1885
1885
  },
1886
1886
  x402scan: {
1887
1887
  source: "x402scan",
@@ -2046,16 +2046,18 @@ function railFrom402IndexFields(o) {
2046
2046
  }
2047
2047
  async function register402Index(input) {
2048
2048
  try {
2049
+ const attributionOn = input.attribution !== false;
2050
+ const description = attributionOn ? appendAttribution(input.description) : input.description;
2049
2051
  const payload = {
2050
2052
  url: input.url,
2051
2053
  name: input.name ?? hostOf(input.url),
2052
2054
  protocol: "x402",
2053
- ...input.description ? { description: input.description } : {},
2055
+ ...description ? { description } : {},
2054
2056
  ...typeof input.priceUsd === "number" ? { price_usd: input.priceUsd } : {},
2055
2057
  ...input.asset ? { payment_asset: input.asset } : {},
2056
2058
  ...input.network ? { payment_network: input.network } : {},
2057
2059
  ...input.method ? { http_method: input.method.toUpperCase() } : {},
2058
- ...input.attribution ? { via: "@piprail/sdk" } : {}
2060
+ ...attributionOn ? { via: "@piprail/sdk" } : {}
2059
2061
  };
2060
2062
  const res = await fetch(INDEX402_REGISTER, {
2061
2063
  method: "POST",
@@ -2071,7 +2073,7 @@ async function register402Index(input) {
2071
2073
  ok: true,
2072
2074
  status: res.status,
2073
2075
  ...live ? { visibility: "live" } : {},
2074
- detail: msg ?? (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 pending review (verify your domain on 402index.io for instant approval).")
2076
+ detail: msg ?? (live ? "Registered + live on 402 Index (domain verified)." : "Registered on 402 Index \u2014 probed on submit, then searchable once it passes automated health + payment checks (verify your domain on 402index.io for instant approval + a verified badge).")
2075
2077
  };
2076
2078
  }
2077
2079
  const why = await readIndexError(res);
@@ -2302,6 +2304,13 @@ function hostOf(url) {
2302
2304
  function errMsg(err) {
2303
2305
  return err instanceof Error ? err.message : String(err);
2304
2306
  }
2307
+ var REGISTER_ATTRIBUTION = "\xB7 Built with @piprail/sdk";
2308
+ function appendAttribution(description) {
2309
+ if (!description) return description;
2310
+ if (/piprail/i.test(description)) return description;
2311
+ const next = `${description.trimEnd()} ${REGISTER_ATTRIBUTION}`;
2312
+ return next.length <= 500 ? next : description;
2313
+ }
2305
2314
  function encodeBase642(str) {
2306
2315
  if (typeof Buffer !== "undefined") return Buffer.from(str, "utf8").toString("base64");
2307
2316
  if (typeof btoa === "function" && typeof TextEncoder !== "undefined") {
@@ -2823,7 +2832,8 @@ var PipRailClient = class {
2823
2832
  ...opts.asset ? { asset: opts.asset } : {},
2824
2833
  ...networkSlug ? { network: networkSlug } : {},
2825
2834
  ...opts.method ? { method: opts.method } : {},
2826
- ...opts.attribution ? { attribution: true } : {}
2835
+ // Attribution is default-ON; forward an explicit opt-out, else let register402Index default it.
2836
+ ...opts.attribution === false ? { attribution: false } : {}
2827
2837
  })
2828
2838
  );
2829
2839
  } else if (target === "x402scan") {
@@ -4205,14 +4215,23 @@ function createPaymentGate(options) {
4205
4215
  };
4206
4216
  }
4207
4217
  const hasCustomStore = Boolean(options.isUsed || options.markUsed);
4208
- const localUsed = /* @__PURE__ */ new Set();
4218
+ const localUsed = /* @__PURE__ */ new Map();
4219
+ const replayWindowMs = maxTimeoutSeconds * 1e3;
4220
+ function pruneUsed(now) {
4221
+ for (const [key, expiry] of localUsed) {
4222
+ if (expiry > now) break;
4223
+ localUsed.delete(key);
4224
+ }
4225
+ }
4209
4226
  async function claimTx(ref) {
4210
4227
  if (hasCustomStore) {
4211
4228
  return options.isUsed ? Boolean(await options.isUsed(ref)) : false;
4212
4229
  }
4213
4230
  const key = ref.toLowerCase();
4231
+ const now = Date.now();
4232
+ pruneUsed(now);
4214
4233
  if (localUsed.has(key)) return true;
4215
- localUsed.add(key);
4234
+ localUsed.set(key, now + replayWindowMs);
4216
4235
  return false;
4217
4236
  }
4218
4237
  async function settleTx(ref, ok) {
@@ -4372,7 +4391,13 @@ function createPaymentGate(options) {
4372
4391
  }
4373
4392
  const ref = sig.payload.txHash;
4374
4393
  if (await claimTx(ref)) return rejection("tx_already_used", `Proof ${ref} was already redeemed.`);
4375
- const result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
4394
+ let result;
4395
+ try {
4396
+ result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
4397
+ } catch (err) {
4398
+ await settleTx(ref, false);
4399
+ throw err;
4400
+ }
4376
4401
  if (!result.ok) {
4377
4402
  await settleTx(ref, false);
4378
4403
  return rejection(result.error, result.detail);
@@ -4629,6 +4654,7 @@ export {
4629
4654
  PaymentTimeoutError,
4630
4655
  PipRailClient,
4631
4656
  PipRailError,
4657
+ REGISTER_ATTRIBUTION,
4632
4658
  RecipientNotReadyError,
4633
4659
  SettlementError,
4634
4660
  UnknownTokenError,
@@ -4638,6 +4664,7 @@ export {
4638
4664
  WrongFamilyError,
4639
4665
  X402_EXACT_PERMIT2_PROXY,
4640
4666
  agentGuide,
4667
+ appendAttribution,
4641
4668
  buildBazaarExtension,
4642
4669
  buildChallengeHeader,
4643
4670
  buildExactAuthorization,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.19.0",
3
+ "version": "1.20.1",
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",