x402-surface-check 0.2.6 → 0.2.8
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 +14 -1
- package/bin/x402-surface-check.mjs +63 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,13 +18,26 @@ npx --yes x402-surface-check --endpoint --method POST https://x402.rpc.ankr.com/
|
|
|
18
18
|
- No-payment HTTP 402 challenge shape
|
|
19
19
|
- x402 v1 and v2 price fields
|
|
20
20
|
- MPP `WWW-Authenticate: Payment` and x402 V2 `WWW-Authenticate: X402 requirements=...` challenges
|
|
21
|
-
- `amount` / `maxAmountRequired
|
|
21
|
+
- Atomic-unit `amount` / `maxAmountRequired` fields, plus legacy decimal `amount` + `token` x402 v1 challenges
|
|
22
|
+
- `asset` or token metadata, `network`, and `payTo`
|
|
22
23
|
- Placeholder recipients such as zero addresses and Solana system-program values
|
|
23
24
|
- Testnet or staging rails such as Base Sepolia and Solana devnet
|
|
24
25
|
- HTTPS resource URLs and stable resource metadata
|
|
25
26
|
- Browser CORS allowance for `X-PAYMENT`
|
|
26
27
|
- Over-broad public method surfaces
|
|
27
28
|
- Auth, validation, and free/trial responses that appear before a payment challenge, without piling on missing-field findings when no challenge was actually returned
|
|
29
|
+
- Object-valued document metadata such as facilitator objects, without `[object Object]` report artifacts
|
|
30
|
+
|
|
31
|
+
## Public Proof
|
|
32
|
+
|
|
33
|
+
Recent public no-payment checks have found and verified real launch fixes:
|
|
34
|
+
|
|
35
|
+
- TensorFeed: parameter-required premium routes moved behind canonical x402 V2 challenges, then verified clean. https://github.com/solana-foundation/pay-skills/pull/68#issuecomment-4455360068
|
|
36
|
+
- x402jp: weather routes that returned 500 now return structured Base x402 challenges. https://github.com/solana-foundation/pay-skills/pull/58#issuecomment-4455401355
|
|
37
|
+
- Spraay: resource echo and browser payment-header behavior verified clean. https://github.com/solana-foundation/pay-skills/pull/60#issuecomment-4455519760
|
|
38
|
+
- Agent Trust Bench: live discovery URL and browser-compatibility notes for adversarial agent-payment resources. https://github.com/solana-foundation/pay-skills/pull/23#issuecomment-4455484414
|
|
39
|
+
|
|
40
|
+
Field notes and browser version: https://tateprograms.com/x402-surface-check.html
|
|
28
41
|
|
|
29
42
|
## Options
|
|
30
43
|
|
|
@@ -88,6 +88,16 @@ function moneyFromAtomic(amount, decimals = 6) {
|
|
|
88
88
|
})}`
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
function moneyFromDecimal(amount) {
|
|
92
|
+
if (amount === '' || amount === null || amount === undefined) return ''
|
|
93
|
+
const numeric = Number(amount)
|
|
94
|
+
if (!Number.isFinite(numeric)) return String(amount ?? '')
|
|
95
|
+
return `$${numeric.toLocaleString(undefined, {
|
|
96
|
+
maximumFractionDigits: 6,
|
|
97
|
+
minimumFractionDigits: numeric < 0.01 ? 3 : 2,
|
|
98
|
+
})}`
|
|
99
|
+
}
|
|
100
|
+
|
|
91
101
|
function uniqueEntries(entries, limit) {
|
|
92
102
|
const seen = new Set()
|
|
93
103
|
return entries
|
|
@@ -264,6 +274,7 @@ function parsePaymentAuthenticate(value) {
|
|
|
264
274
|
maxTimeoutSeconds: '',
|
|
265
275
|
extra: {
|
|
266
276
|
description: request.description ?? '',
|
|
277
|
+
decimals: request.methodDetails?.decimals ?? '',
|
|
267
278
|
expires: params.expires ?? '',
|
|
268
279
|
id: params.id ?? '',
|
|
269
280
|
intent: params.intent ?? '',
|
|
@@ -365,6 +376,24 @@ function valueList(value) {
|
|
|
365
376
|
return []
|
|
366
377
|
}
|
|
367
378
|
|
|
379
|
+
function displayMetadataValue(value) {
|
|
380
|
+
if (value === null || value === undefined || value === '') return '-'
|
|
381
|
+
if (Array.isArray(value)) {
|
|
382
|
+
return value.map(displayMetadataValue).filter(item => item && item !== '-').join(', ') || '-'
|
|
383
|
+
}
|
|
384
|
+
if (typeof value === 'object') {
|
|
385
|
+
const parts = [
|
|
386
|
+
value.name,
|
|
387
|
+
value.operator,
|
|
388
|
+
value.url,
|
|
389
|
+
value.jurisdiction,
|
|
390
|
+
value.network,
|
|
391
|
+
].filter(item => item !== null && item !== undefined && item !== '').map(String)
|
|
392
|
+
return parts.join(' / ') || Object.keys(value).join(', ') || '-'
|
|
393
|
+
}
|
|
394
|
+
return String(value)
|
|
395
|
+
}
|
|
396
|
+
|
|
368
397
|
function capabilityList(value) {
|
|
369
398
|
if (!Array.isArray(value)) return []
|
|
370
399
|
return value.map(item => item?.id ?? item?.name ?? item).filter(Boolean).map(String)
|
|
@@ -374,6 +403,36 @@ function challengeAccepts(result) {
|
|
|
374
403
|
return Array.isArray(result.body.json?.accepts) ? result.body.json.accepts : []
|
|
375
404
|
}
|
|
376
405
|
|
|
406
|
+
function acceptAmountValue(accept) {
|
|
407
|
+
return accept.maxAmountRequired ?? accept.maxAmount ?? accept.amount ?? ''
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function acceptAssetValue(accept) {
|
|
411
|
+
return accept.asset ?? accept.token ?? accept.currency ?? ''
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function acceptDecimals(accept) {
|
|
415
|
+
const value = accept.decimals ?? accept.extra?.decimals ?? accept.methodDetails?.decimals
|
|
416
|
+
const numeric = Number(value)
|
|
417
|
+
return Number.isFinite(numeric) ? numeric : 6
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function usesDecimalAmount(accept, result) {
|
|
421
|
+
if (accept.maxAmountRequired !== undefined || accept.maxAmount !== undefined) return false
|
|
422
|
+
if (accept.amount === undefined || accept.amount === null || accept.amount === '') return false
|
|
423
|
+
const amount = String(accept.amount)
|
|
424
|
+
if (amount.includes('.')) return true
|
|
425
|
+
if (!accept.asset && (accept.token || result.headers?.['x-payment-token'])) return true
|
|
426
|
+
return result.headers?.['x-payment-amount'] === amount
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function challengePrice(accept, result) {
|
|
430
|
+
const amount = acceptAmountValue(accept)
|
|
431
|
+
return usesDecimalAmount(accept, result)
|
|
432
|
+
? moneyFromDecimal(amount)
|
|
433
|
+
: moneyFromAtomic(amount, acceptDecimals(accept))
|
|
434
|
+
}
|
|
435
|
+
|
|
377
436
|
function hasPaymentChallenge(result) {
|
|
378
437
|
const challenge = result.body.json
|
|
379
438
|
return challengeAccepts(result).length > 0 || Boolean(challenge?.resource || challenge?.payment || result.headers?.['www-authenticate'])
|
|
@@ -383,7 +442,7 @@ function challengeSummary(result) {
|
|
|
383
442
|
const challenge = result.body.json
|
|
384
443
|
const firstAccept = challenge?.accepts?.[0] ?? {}
|
|
385
444
|
const hasChallenge = hasPaymentChallenge(result)
|
|
386
|
-
const amount = firstAccept
|
|
445
|
+
const amount = acceptAmountValue(firstAccept)
|
|
387
446
|
const resourceUrl = challenge?.resource?.url ?? firstAccept.resource ?? ''
|
|
388
447
|
const extraResource = firstAccept.extra?.resource ?? firstAccept.resource ?? ''
|
|
389
448
|
|
|
@@ -393,9 +452,9 @@ function challengeSummary(result) {
|
|
|
393
452
|
resourceUrl,
|
|
394
453
|
network: firstAccept.network ?? '',
|
|
395
454
|
amount,
|
|
396
|
-
price:
|
|
455
|
+
price: hasChallenge ? challengePrice(firstAccept, result) : '',
|
|
397
456
|
payTo: firstAccept.payTo ?? '',
|
|
398
|
-
asset: firstAccept
|
|
457
|
+
asset: acceptAssetValue(firstAccept),
|
|
399
458
|
timeout: firstAccept.maxTimeoutSeconds ?? '',
|
|
400
459
|
extraResource,
|
|
401
460
|
}
|
|
@@ -520,7 +579,7 @@ function formatMarkdown(report) {
|
|
|
520
579
|
`- Type: ${report.directEndpoint ? 'direct endpoint' : (document.openapi ? 'OpenAPI' : 'x402 manifest or JSON document')}`,
|
|
521
580
|
`- Agent: ${document.agent?.name ?? '-'}`,
|
|
522
581
|
`- Wallet: ${document.agent?.wallet ?? '-'}`,
|
|
523
|
-
`- Facilitator: ${document.facilitator
|
|
582
|
+
`- Facilitator: ${displayMetadataValue(document.facilitator)}`,
|
|
524
583
|
`- Networks: ${valueList(document.networks).join(', ') || '-'}`,
|
|
525
584
|
`- Capabilities: ${capabilityList(document.capabilities).join(', ') || '-'}`,
|
|
526
585
|
`- Probed endpoints: ${report.entries.length}`,
|