x402-surface-check 0.2.9 → 0.2.10

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/README.md CHANGED
@@ -20,6 +20,7 @@ npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/
20
20
  - MPP `WWW-Authenticate: Payment` and x402 V2 `WWW-Authenticate: X402 requirements=...` challenges
21
21
  - Atomic-unit `amount` / `maxAmountRequired` fields, plus legacy decimal `amount` + `token` x402 v1 challenges
22
22
  - `asset` or token metadata, `network`, and `payTo`
23
+ - OpenAPI-declared `x-payment-info.price.amount` drift versus the live 402 challenge price
23
24
  - Placeholder recipients such as zero addresses and Solana system-program values
24
25
  - Testnet or staging rails such as Base Sepolia and Solana devnet
25
26
  - HTTPS resource URLs and stable resource metadata
@@ -98,6 +98,11 @@ function moneyFromDecimal(amount) {
98
98
  })}`
99
99
  }
100
100
 
101
+ function numberFromDecimal(amount) {
102
+ const numeric = Number(amount)
103
+ return Number.isFinite(numeric) ? numeric : null
104
+ }
105
+
101
106
  function uniqueEntries(entries, limit) {
102
107
  const seen = new Set()
103
108
  return entries
@@ -127,6 +132,15 @@ function endpointUrl(rawPath, baseUrl, sourceUrl) {
127
132
  return new URL(value, base).toString()
128
133
  }
129
134
 
135
+ function operationExpectedPrice(operation) {
136
+ const price = operation?.['x-payment-info']?.price
137
+ ?? operation?.['x-payment']?.price
138
+ ?? operation?.payment?.price
139
+ const amount = price?.amount ?? price?.amountUsd ?? price?.usd
140
+ const numeric = numberFromDecimal(amount)
141
+ return numeric === null ? null : numeric
142
+ }
143
+
130
144
  function endpointEntries(document, sourceUrl, limit) {
131
145
  const entries = []
132
146
  const baseUrl = documentBaseUrl(document, sourceUrl)
@@ -189,6 +203,7 @@ function endpointEntries(document, sourceUrl, limit) {
189
203
  name: operation.operationId ?? `${method.toUpperCase()} ${path}`,
190
204
  url,
191
205
  method: method.toUpperCase(),
206
+ expectedPriceUsd: operationExpectedPrice(operation),
192
207
  })
193
208
  }
194
209
  }
@@ -437,6 +452,14 @@ function challengePrice(accept, result) {
437
452
  : moneyFromAtomic(amount, acceptDecimals(accept))
438
453
  }
439
454
 
455
+ function challengePriceUsd(accept, result) {
456
+ const amount = acceptAmountValue(accept)
457
+ if (usesDecimalAmount(accept, result)) return numberFromDecimal(amount)
458
+ const numeric = Number(amount)
459
+ if (!Number.isFinite(numeric)) return null
460
+ return numeric / (10 ** acceptDecimals(accept))
461
+ }
462
+
440
463
  function hasPaymentChallenge(result) {
441
464
  const challenge = result.body.json
442
465
  return challengeAccepts(result).length > 0 || Boolean(challenge?.resource || challenge?.payment || result.headers?.['www-authenticate'])
@@ -457,6 +480,8 @@ function challengeSummary(result) {
457
480
  network: firstAccept.network ?? '',
458
481
  amount,
459
482
  price: hasChallenge ? challengePrice(firstAccept, result) : '',
483
+ priceUsd: hasChallenge ? challengePriceUsd(firstAccept, result) : null,
484
+ expectedPriceUsd: typeof result.expectedPriceUsd === 'number' ? result.expectedPriceUsd : null,
460
485
  payTo: firstAccept.payTo ?? '',
461
486
  asset: acceptAssetValue(firstAccept),
462
487
  timeout: firstAccept.maxTimeoutSeconds ?? '',
@@ -524,6 +549,12 @@ function findingList(documentResult, challengeResults, preflightResults, entries
524
549
  if (!summary.amount || !summary.payTo || !summary.asset) {
525
550
  findings.push(`P1 - ${result.name} challenge is missing amount/maxAmountRequired, payTo, or asset metadata.`)
526
551
  }
552
+ if (summary.expectedPriceUsd !== null && summary.priceUsd !== null) {
553
+ const delta = Math.abs(summary.expectedPriceUsd - summary.priceUsd)
554
+ if (delta > 0.000001) {
555
+ findings.push(`P1 - ${result.name} documented price ${moneyFromDecimal(summary.expectedPriceUsd)} does not match live 402 challenge price ${moneyFromDecimal(summary.priceUsd)}.`)
556
+ }
557
+ }
527
558
  for (const accept of challengeAccepts(result)) {
528
559
  if (looksLikePlaceholderPayTo(accept.payTo)) {
529
560
  findings.push(`P1 - ${result.name} challenge advertises placeholder-looking payTo ${accept.payTo}; production listings should not ask agents to pay placeholder recipients.`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-surface-check",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "description": "No-payment x402 public-surface checker for manifests, OpenAPI specs, and HTTP 402 challenges.",
5
5
  "type": "module",
6
6
  "bin": {