x402-surface-check 0.2.7 → 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 +44 -3
- 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 ?? '',
|
|
@@ -392,6 +403,36 @@ function challengeAccepts(result) {
|
|
|
392
403
|
return Array.isArray(result.body.json?.accepts) ? result.body.json.accepts : []
|
|
393
404
|
}
|
|
394
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
|
+
|
|
395
436
|
function hasPaymentChallenge(result) {
|
|
396
437
|
const challenge = result.body.json
|
|
397
438
|
return challengeAccepts(result).length > 0 || Boolean(challenge?.resource || challenge?.payment || result.headers?.['www-authenticate'])
|
|
@@ -401,7 +442,7 @@ function challengeSummary(result) {
|
|
|
401
442
|
const challenge = result.body.json
|
|
402
443
|
const firstAccept = challenge?.accepts?.[0] ?? {}
|
|
403
444
|
const hasChallenge = hasPaymentChallenge(result)
|
|
404
|
-
const amount = firstAccept
|
|
445
|
+
const amount = acceptAmountValue(firstAccept)
|
|
405
446
|
const resourceUrl = challenge?.resource?.url ?? firstAccept.resource ?? ''
|
|
406
447
|
const extraResource = firstAccept.extra?.resource ?? firstAccept.resource ?? ''
|
|
407
448
|
|
|
@@ -411,9 +452,9 @@ function challengeSummary(result) {
|
|
|
411
452
|
resourceUrl,
|
|
412
453
|
network: firstAccept.network ?? '',
|
|
413
454
|
amount,
|
|
414
|
-
price:
|
|
455
|
+
price: hasChallenge ? challengePrice(firstAccept, result) : '',
|
|
415
456
|
payTo: firstAccept.payTo ?? '',
|
|
416
|
-
asset: firstAccept
|
|
457
|
+
asset: acceptAssetValue(firstAccept),
|
|
417
458
|
timeout: firstAccept.maxTimeoutSeconds ?? '',
|
|
418
459
|
extraResource,
|
|
419
460
|
}
|