agentpay-mcp 4.1.7 → 4.1.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 +17 -3
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/session/types.d.ts +1 -1
- package/dist/session/types.d.ts.map +1 -1
- package/dist/tools/budget.d.ts +14 -0
- package/dist/tools/budget.d.ts.map +1 -1
- package/dist/tools/budget.js +45 -15
- package/dist/tools/budget.js.map +1 -1
- package/dist/tools/deploy.js.map +1 -1
- package/dist/tools/history.d.ts.map +1 -1
- package/dist/tools/history.js.map +1 -1
- package/dist/tools/payments.d.ts.map +1 -1
- package/dist/tools/payments.js.map +1 -1
- package/dist/tools/session.d.ts.map +1 -1
- package/dist/tools/session.js +12 -19
- package/dist/tools/session.js.map +1 -1
- package/dist/tools/x402.d.ts.map +1 -1
- package/dist/tools/x402.js +9 -12
- package/dist/tools/x402.js.map +1 -1
- package/dist/utils/hosted-proxy-verification.d.ts +48 -0
- package/dist/utils/hosted-proxy-verification.d.ts.map +1 -0
- package/dist/utils/hosted-proxy-verification.js +147 -0
- package/dist/utils/hosted-proxy-verification.js.map +1 -0
- package/dist/utils/paid-mcp-gateway-hardening.d.ts +51 -0
- package/dist/utils/paid-mcp-gateway-hardening.d.ts.map +1 -0
- package/dist/utils/paid-mcp-gateway-hardening.js +60 -0
- package/dist/utils/paid-mcp-gateway-hardening.js.map +1 -0
- package/dist/utils/paid-provider-health-proof.d.ts +294 -0
- package/dist/utils/paid-provider-health-proof.d.ts.map +1 -0
- package/dist/utils/paid-provider-health-proof.js +191 -0
- package/dist/utils/paid-provider-health-proof.js.map +1 -0
- package/dist/utils/x402-buyer-flow.d.ts +81 -0
- package/dist/utils/x402-buyer-flow.d.ts.map +1 -0
- package/dist/utils/x402-buyer-flow.js +188 -0
- package/dist/utils/x402-buyer-flow.js.map +1 -0
- package/dist/utils/x402-v211-compatibility.d.ts +29 -0
- package/dist/utils/x402-v211-compatibility.d.ts.map +1 -0
- package/dist/utils/x402-v211-compatibility.js +71 -0
- package/dist/utils/x402-v211-compatibility.js.map +1 -0
- package/docs/agentpay-buyer-flow-parity.md +149 -0
- package/docs/dependency-pin-policy.md +53 -0
- package/docs/fixtures/paid-provider-health-proof-voidly-2026-05-02.json +104 -0
- package/docs/hosted-x402-proxy-verification.md +79 -0
- package/docs/mcp-registry-listing-proof.md +62 -0
- package/docs/mcp-registry-listing.json +40 -0
- package/docs/paid-mcp-gateway-hardening.md +121 -0
- package/docs/paid-provider-health-proof.md +71 -0
- package/docs/paid-provider-health-proof.schema.json +89 -0
- package/docs/settlegrid-paid-mcp-discovery-response.md +61 -0
- package/docs/x402-ecosystem-submission.md +22 -0
- package/docs/x402-native-vs-stripe-proxy.md +1 -1
- package/docs/x402-v211-paid-mcp-compatibility.md +75 -0
- package/llms.txt +21 -0
- package/package.json +10 -5
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createX402IdempotencyKey = createX402IdempotencyKey;
|
|
4
|
+
exports.classifyX402PaymentError = classifyX402PaymentError;
|
|
5
|
+
exports.verifyX402BuyerFlow = verifyX402BuyerFlow;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
function normalizeMethod(method) {
|
|
8
|
+
return method.trim().toUpperCase();
|
|
9
|
+
}
|
|
10
|
+
function createX402IdempotencyKey(input) {
|
|
11
|
+
const hash = (0, node_crypto_1.createHash)('sha256');
|
|
12
|
+
hash.update(normalizeMethod(input.method));
|
|
13
|
+
hash.update('\n');
|
|
14
|
+
hash.update(input.url.trim());
|
|
15
|
+
hash.update('\n');
|
|
16
|
+
hash.update(input.body ?? '');
|
|
17
|
+
hash.update('\n');
|
|
18
|
+
hash.update(input.signer.toLowerCase());
|
|
19
|
+
return hash.digest('hex');
|
|
20
|
+
}
|
|
21
|
+
function parsePositiveAmount(value) {
|
|
22
|
+
if (typeof value === 'bigint')
|
|
23
|
+
return value > 0n ? value : null;
|
|
24
|
+
if (!value || !/^\d+$/.test(value))
|
|
25
|
+
return null;
|
|
26
|
+
const parsed = BigInt(value);
|
|
27
|
+
return parsed > 0n ? parsed : null;
|
|
28
|
+
}
|
|
29
|
+
function isNonZeroEvmAddress(value) {
|
|
30
|
+
if (!value)
|
|
31
|
+
return false;
|
|
32
|
+
const normalized = value.toLowerCase();
|
|
33
|
+
return /^0x[a-f0-9]{40}$/.test(normalized) && normalized !== '0x0000000000000000000000000000000000000000';
|
|
34
|
+
}
|
|
35
|
+
function parseNonNegativeEnvelopeValue(value) {
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
return null;
|
|
38
|
+
if (typeof value === 'number') {
|
|
39
|
+
if (!Number.isInteger(value) || value < 0)
|
|
40
|
+
return null;
|
|
41
|
+
return BigInt(value);
|
|
42
|
+
}
|
|
43
|
+
if (!/^\d+$/.test(value))
|
|
44
|
+
return null;
|
|
45
|
+
return BigInt(value);
|
|
46
|
+
}
|
|
47
|
+
function hasQuotaVisibility(quota) {
|
|
48
|
+
if (!quota)
|
|
49
|
+
return false;
|
|
50
|
+
return Boolean(quota.limit !== undefined || quota.remaining !== undefined || quota.resetAt || Object.keys(quota.sourceHeaders ?? {}).length > 0);
|
|
51
|
+
}
|
|
52
|
+
function calculateRemainingAfterPayment(maxSpend, amountRequired) {
|
|
53
|
+
if (!maxSpend || !amountRequired || amountRequired > maxSpend)
|
|
54
|
+
return undefined;
|
|
55
|
+
return (maxSpend - amountRequired).toString();
|
|
56
|
+
}
|
|
57
|
+
function classifyX402PaymentError(error, quota) {
|
|
58
|
+
if (!error)
|
|
59
|
+
return 'do_not_retry';
|
|
60
|
+
switch (error.name) {
|
|
61
|
+
case 'PaymentRequiredError':
|
|
62
|
+
return 'retry_after_payment';
|
|
63
|
+
case 'QuotaExceededError':
|
|
64
|
+
return hasQuotaVisibility(quota) ? 'retry_after_quota_reset' : 'operator_review';
|
|
65
|
+
case 'TokenExpiredError':
|
|
66
|
+
return 'refresh_token_then_retry';
|
|
67
|
+
case 'ValidationFailureError':
|
|
68
|
+
case 'SpendLimitExceededError':
|
|
69
|
+
return 'do_not_retry';
|
|
70
|
+
case 'UnknownPaymentError':
|
|
71
|
+
default:
|
|
72
|
+
return 'operator_review';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function verifyX402BuyerFlow(input) {
|
|
76
|
+
const failures = [];
|
|
77
|
+
const warnings = [];
|
|
78
|
+
const expectedIdempotencyKey = createX402IdempotencyKey(input);
|
|
79
|
+
const maxSpend = parsePositiveAmount(input.maxSpendAtomic);
|
|
80
|
+
const amountRequired = parsePositiveAmount(input.challenge?.amountRequired);
|
|
81
|
+
const toolSet = new Set(input.mcpTools);
|
|
82
|
+
const quotaLimit = parseNonNegativeEnvelopeValue(input.quota?.limit);
|
|
83
|
+
const quotaRemaining = parseNonNegativeEnvelopeValue(input.quota?.remaining);
|
|
84
|
+
const quotaVisible = hasQuotaVisibility(input.quota);
|
|
85
|
+
const retryability = classifyX402PaymentError(input.typedError, input.quota);
|
|
86
|
+
try {
|
|
87
|
+
new URL(input.url);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
failures.push('Buyer flow URL must be an absolute URL before any payment check runs.');
|
|
91
|
+
}
|
|
92
|
+
if (input.challengeSource === 'none') {
|
|
93
|
+
failures.push('Buyer flow must discover or inspect a 402 challenge before payment.');
|
|
94
|
+
}
|
|
95
|
+
if (!input.challenge?.network || !input.allowedNetworks.includes(input.challenge.network)) {
|
|
96
|
+
failures.push('Challenge network must match the buyer allowlist.');
|
|
97
|
+
}
|
|
98
|
+
const asset = input.challenge?.asset?.toLowerCase();
|
|
99
|
+
const allowedAssets = input.allowedAssets.map((item) => item.toLowerCase());
|
|
100
|
+
if (!asset || !allowedAssets.includes(asset)) {
|
|
101
|
+
failures.push('Challenge asset must match the buyer allowlist.');
|
|
102
|
+
}
|
|
103
|
+
if (!isNonZeroEvmAddress(input.challenge?.payTo)) {
|
|
104
|
+
failures.push('Challenge payTo must be a non-zero EVM recipient before signing.');
|
|
105
|
+
}
|
|
106
|
+
if (!maxSpend) {
|
|
107
|
+
failures.push('Buyer flow must set a positive max spend cap.');
|
|
108
|
+
}
|
|
109
|
+
if (!amountRequired) {
|
|
110
|
+
failures.push('Challenge must include a positive amount before dry-run or pay.');
|
|
111
|
+
}
|
|
112
|
+
if (maxSpend && amountRequired && amountRequired > maxSpend) {
|
|
113
|
+
failures.push('Challenge amount exceeds the buyer max spend cap.');
|
|
114
|
+
}
|
|
115
|
+
if (!input.dryRunCompleted) {
|
|
116
|
+
failures.push('Buyer flow must complete a dry-run plan before signing.');
|
|
117
|
+
}
|
|
118
|
+
if (input.approvalState !== 'approved' && input.approvalState !== 'not_required') {
|
|
119
|
+
failures.push(`Buyer flow approval state must be approved or not_required before signing; received ${input.approvalState}.`);
|
|
120
|
+
}
|
|
121
|
+
if (input.idempotencyKey && input.idempotencyKey !== expectedIdempotencyKey) {
|
|
122
|
+
failures.push('Provided idempotency key does not match method, URL, body, and signer.');
|
|
123
|
+
}
|
|
124
|
+
if (input.typedError && !input.typedError.noCharge) {
|
|
125
|
+
failures.push('Typed payment errors must explicitly preserve no-charge failure semantics before retry or operator action.');
|
|
126
|
+
}
|
|
127
|
+
if (input.typedError?.name === 'QuotaExceededError' && !quotaVisible) {
|
|
128
|
+
failures.push('QuotaExceededError must include quota visibility from X-Quota-* headers or an equivalent envelope.');
|
|
129
|
+
}
|
|
130
|
+
if (input.typedError?.name === 'TokenExpiredError' && retryability !== 'refresh_token_then_retry') {
|
|
131
|
+
failures.push('TokenExpiredError must map to refresh_token_then_retry recovery guidance.');
|
|
132
|
+
}
|
|
133
|
+
if (input.quota?.limit !== undefined && quotaLimit === null) {
|
|
134
|
+
failures.push('Quota envelope limit must be a non-negative integer when present.');
|
|
135
|
+
}
|
|
136
|
+
if (input.quota?.remaining !== undefined && quotaRemaining === null) {
|
|
137
|
+
failures.push('Quota envelope remaining must be a non-negative integer when present.');
|
|
138
|
+
}
|
|
139
|
+
if (quotaLimit !== null && quotaRemaining !== null && quotaRemaining > quotaLimit) {
|
|
140
|
+
failures.push('Quota envelope remaining must not exceed quota limit.');
|
|
141
|
+
}
|
|
142
|
+
for (const requiredTool of ['x402_pay', 'check_budget', 'set_spend_policy', 'get_transaction_history']) {
|
|
143
|
+
if (!toolSet.has(requiredTool)) {
|
|
144
|
+
failures.push(`MCP exposure is missing required AgentPay tool: ${requiredTool}.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (!toolSet.has('queue_approval')) {
|
|
148
|
+
warnings.push('queue_approval is not listed. Human approval should be explicit for above-threshold payments.');
|
|
149
|
+
}
|
|
150
|
+
if (!input.audit.destination || !input.audit.correlationId || !input.audit.receiptSink) {
|
|
151
|
+
failures.push('Buyer flow audit must include destination, correlationId, and receiptSink.');
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ok: failures.length === 0,
|
|
155
|
+
idempotencyKey: expectedIdempotencyKey,
|
|
156
|
+
failures,
|
|
157
|
+
warnings,
|
|
158
|
+
parity: {
|
|
159
|
+
discover: input.challengeSource !== 'none',
|
|
160
|
+
check: Boolean(input.challenge?.network && input.challenge?.asset && input.challenge?.payTo && input.challenge?.amountRequired),
|
|
161
|
+
dryRun: input.dryRunCompleted,
|
|
162
|
+
pay: input.approvalState === 'approved' || input.approvalState === 'not_required',
|
|
163
|
+
spendLimit: Boolean(maxSpend && (!amountRequired || amountRequired <= maxSpend)),
|
|
164
|
+
idempotency: !input.idempotencyKey || input.idempotencyKey === expectedIdempotencyKey,
|
|
165
|
+
mcpExposure: ['x402_pay', 'check_budget', 'set_spend_policy', 'get_transaction_history'].every((tool) => toolSet.has(tool)),
|
|
166
|
+
audit: Boolean(input.audit.destination && input.audit.correlationId && input.audit.receiptSink),
|
|
167
|
+
typedErrors: Boolean(input.typedError?.name),
|
|
168
|
+
retryability: Boolean(input.typedError),
|
|
169
|
+
quotaEnvelope: quotaVisible,
|
|
170
|
+
noChargeFailures: input.typedError ? input.typedError.noCharge : true,
|
|
171
|
+
},
|
|
172
|
+
recovery: {
|
|
173
|
+
errorName: input.typedError?.name,
|
|
174
|
+
retryability,
|
|
175
|
+
noCharge: input.typedError ? input.typedError.noCharge : true,
|
|
176
|
+
quotaVisible,
|
|
177
|
+
},
|
|
178
|
+
envelope: {
|
|
179
|
+
quota: input.quota,
|
|
180
|
+
spend: {
|
|
181
|
+
maxSpendAtomic: maxSpend?.toString() ?? String(input.maxSpendAtomic),
|
|
182
|
+
amountRequiredAtomic: amountRequired?.toString(),
|
|
183
|
+
remainingAfterPaymentAtomic: calculateRemainingAfterPayment(maxSpend, amountRequired),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=x402-buyer-flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402-buyer-flow.js","sourceRoot":"","sources":["../../src/utils/x402-buyer-flow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x402 v2.11 paid MCP compatibility helpers.
|
|
3
|
+
*
|
|
4
|
+
* These helpers keep buyer-facing documentation, tests, and downstream
|
|
5
|
+
* Streamable HTTP gateways aligned on the same header names and receipt links.
|
|
6
|
+
*/
|
|
7
|
+
export declare const X402_V211_PAYMENT_SIGNATURE_HEADER = "Payment-Signature";
|
|
8
|
+
export declare const X402_V211_DEPRECATED_PAYMENT_HEADERS: string[];
|
|
9
|
+
export declare const X402_V211_RECEIPT_HEADER = "payment-response";
|
|
10
|
+
export declare const X402_V211_MCP_SESSION_HEADER = "mcp-session-id";
|
|
11
|
+
export declare const X402_V211_BROWSER_EXPOSED_HEADERS: string[];
|
|
12
|
+
export type SupportedReceiptChainId = 8453 | 84532;
|
|
13
|
+
export type X402V211CompatibilityProof = {
|
|
14
|
+
paymentSignatureHeader: typeof X402_V211_PAYMENT_SIGNATURE_HEADER;
|
|
15
|
+
deprecatedPaymentHeaders: string[];
|
|
16
|
+
responseHeaders: string[];
|
|
17
|
+
corsExposeHeaders: string;
|
|
18
|
+
streamableHttpSequence: string[];
|
|
19
|
+
supportedReceiptChains: Array<{
|
|
20
|
+
chainId: SupportedReceiptChainId;
|
|
21
|
+
name: string;
|
|
22
|
+
explorerTxBaseUrl: string;
|
|
23
|
+
x402Network: string;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export declare function buildCorsExposeHeaders(existing?: string[]): string;
|
|
27
|
+
export declare function buildReceiptLink(chainId: number, txHash: string): string;
|
|
28
|
+
export declare function x402V211CompatibilityProof(): X402V211CompatibilityProof;
|
|
29
|
+
//# sourceMappingURL=x402-v211-compatibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402-v211-compatibility.d.ts","sourceRoot":"","sources":["../../src/utils/x402-v211-compatibility.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,kCAAkC,sBAAsB,CAAC;AACtE,eAAO,MAAM,oCAAoC,UAAgB,CAAC;AAClE,eAAO,MAAM,wBAAwB,qBAAqB,CAAC;AAC3D,eAAO,MAAM,4BAA4B,mBAAmB,CAAC;AAE7D,eAAO,MAAM,iCAAiC,UAG7C,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG,IAAI,GAAG,KAAK,CAAC;AAEnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,sBAAsB,EAAE,OAAO,kCAAkC,CAAC;IAClE,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,sBAAsB,EAAE,KAAK,CAAC;QAC5B,OAAO,EAAE,uBAAuB,CAAC;QACjC,IAAI,EAAE,MAAM,CAAC;QACb,iBAAiB,EAAE,MAAM,CAAC;QAC1B,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;CACJ,CAAC;AAeF,wBAAgB,sBAAsB,CAAC,QAAQ,GAAE,MAAM,EAAO,GAAG,MAAM,CAOtE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CASxE;AAED,wBAAgB,0BAA0B,IAAI,0BAA0B,CAkBvE"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* x402 v2.11 paid MCP compatibility helpers.
|
|
4
|
+
*
|
|
5
|
+
* These helpers keep buyer-facing documentation, tests, and downstream
|
|
6
|
+
* Streamable HTTP gateways aligned on the same header names and receipt links.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.X402_V211_BROWSER_EXPOSED_HEADERS = exports.X402_V211_MCP_SESSION_HEADER = exports.X402_V211_RECEIPT_HEADER = exports.X402_V211_DEPRECATED_PAYMENT_HEADERS = exports.X402_V211_PAYMENT_SIGNATURE_HEADER = void 0;
|
|
10
|
+
exports.buildCorsExposeHeaders = buildCorsExposeHeaders;
|
|
11
|
+
exports.buildReceiptLink = buildReceiptLink;
|
|
12
|
+
exports.x402V211CompatibilityProof = x402V211CompatibilityProof;
|
|
13
|
+
exports.X402_V211_PAYMENT_SIGNATURE_HEADER = 'Payment-Signature';
|
|
14
|
+
exports.X402_V211_DEPRECATED_PAYMENT_HEADERS = ['X-Payment'];
|
|
15
|
+
exports.X402_V211_RECEIPT_HEADER = 'payment-response';
|
|
16
|
+
exports.X402_V211_MCP_SESSION_HEADER = 'mcp-session-id';
|
|
17
|
+
exports.X402_V211_BROWSER_EXPOSED_HEADERS = [
|
|
18
|
+
exports.X402_V211_RECEIPT_HEADER,
|
|
19
|
+
exports.X402_V211_MCP_SESSION_HEADER,
|
|
20
|
+
];
|
|
21
|
+
const RECEIPT_EXPLORERS = {
|
|
22
|
+
8453: {
|
|
23
|
+
name: 'Base mainnet',
|
|
24
|
+
explorerTxBaseUrl: 'https://basescan.org/tx',
|
|
25
|
+
x402Network: 'base',
|
|
26
|
+
},
|
|
27
|
+
84532: {
|
|
28
|
+
name: 'Base Sepolia',
|
|
29
|
+
explorerTxBaseUrl: 'https://sepolia.basescan.org/tx',
|
|
30
|
+
x402Network: 'base-sepolia',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
function buildCorsExposeHeaders(existing = []) {
|
|
34
|
+
const normalized = new Map();
|
|
35
|
+
for (const header of [...existing, ...exports.X402_V211_BROWSER_EXPOSED_HEADERS]) {
|
|
36
|
+
const clean = header.trim();
|
|
37
|
+
if (clean)
|
|
38
|
+
normalized.set(clean.toLowerCase(), clean);
|
|
39
|
+
}
|
|
40
|
+
return Array.from(normalized.values()).join(', ');
|
|
41
|
+
}
|
|
42
|
+
function buildReceiptLink(chainId, txHash) {
|
|
43
|
+
if (!/^0x[a-fA-F0-9]{64}$/.test(txHash)) {
|
|
44
|
+
throw new Error('txHash must be a 32-byte 0x-prefixed transaction hash');
|
|
45
|
+
}
|
|
46
|
+
const chain = RECEIPT_EXPLORERS[chainId];
|
|
47
|
+
if (!chain) {
|
|
48
|
+
throw new Error('Unsupported receipt chain. Use Base mainnet 8453 or Base Sepolia 84532.');
|
|
49
|
+
}
|
|
50
|
+
return `${chain.explorerTxBaseUrl}/${txHash}`;
|
|
51
|
+
}
|
|
52
|
+
function x402V211CompatibilityProof() {
|
|
53
|
+
return {
|
|
54
|
+
paymentSignatureHeader: exports.X402_V211_PAYMENT_SIGNATURE_HEADER,
|
|
55
|
+
deprecatedPaymentHeaders: [...exports.X402_V211_DEPRECATED_PAYMENT_HEADERS],
|
|
56
|
+
responseHeaders: [...exports.X402_V211_BROWSER_EXPOSED_HEADERS],
|
|
57
|
+
corsExposeHeaders: buildCorsExposeHeaders(),
|
|
58
|
+
streamableHttpSequence: [
|
|
59
|
+
'POST /mcp initialize with Payment-Signature when the gateway charges for initialize',
|
|
60
|
+
'Read payment-response and mcp-session-id from exposed response headers',
|
|
61
|
+
'Send notifications/initialized after initialize succeeds',
|
|
62
|
+
'Call tools/list only after initialize and initialized complete',
|
|
63
|
+
'Call tools/call with the returned mcp-session-id when the gateway requires session continuity',
|
|
64
|
+
],
|
|
65
|
+
supportedReceiptChains: Object.entries(RECEIPT_EXPLORERS).map(([chainId, value]) => ({
|
|
66
|
+
chainId: Number(chainId),
|
|
67
|
+
...value,
|
|
68
|
+
})),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=x402-v211-compatibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x402-v211-compatibility.js","sourceRoot":"","sources":["../../src/utils/x402-v211-compatibility.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAyCH,wDAOC;AAED,4CASC;AAED,gEAkBC;AA7EY,QAAA,kCAAkC,GAAG,mBAAmB,CAAC;AACzD,QAAA,oCAAoC,GAAG,CAAC,WAAW,CAAC,CAAC;AACrD,QAAA,wBAAwB,GAAG,kBAAkB,CAAC;AAC9C,QAAA,4BAA4B,GAAG,gBAAgB,CAAC;AAEhD,QAAA,iCAAiC,GAAG;IAC/C,gCAAwB;IACxB,oCAA4B;CAC7B,CAAC;AAkBF,MAAM,iBAAiB,GAAsG;IAC3H,IAAI,EAAE;QACJ,IAAI,EAAE,cAAc;QACpB,iBAAiB,EAAE,yBAAyB;QAC5C,WAAW,EAAE,MAAM;KACpB;IACD,KAAK,EAAE;QACL,IAAI,EAAE,cAAc;QACpB,iBAAiB,EAAE,iCAAiC;QACpD,WAAW,EAAE,cAAc;KAC5B;CACF,CAAC;AAEF,SAAgB,sBAAsB,CAAC,WAAqB,EAAE;IAC5D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,MAAM,IAAI,CAAC,GAAG,QAAQ,EAAE,GAAG,yCAAiC,CAAC,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,KAAK;YAAE,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,SAAgB,gBAAgB,CAAC,OAAe,EAAE,MAAc;IAC9D,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAkC,CAAC,CAAC;IACpE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,iBAAiB,IAAI,MAAM,EAAE,CAAC;AAChD,CAAC;AAED,SAAgB,0BAA0B;IACxC,OAAO;QACL,sBAAsB,EAAE,0CAAkC;QAC1D,wBAAwB,EAAE,CAAC,GAAG,4CAAoC,CAAC;QACnE,eAAe,EAAE,CAAC,GAAG,yCAAiC,CAAC;QACvD,iBAAiB,EAAE,sBAAsB,EAAE;QAC3C,sBAAsB,EAAE;YACtB,qFAAqF;YACrF,wEAAwE;YACxE,0DAA0D;YAC1D,gEAAgE;YAChE,+FAA+F;SAChG;QACD,sBAAsB,EAAE,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YACnF,OAAO,EAAE,MAAM,CAAC,OAAO,CAA4B;YACnD,GAAG,KAAK;SACT,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# AgentPay buyer-flow parity for one-command x402 tools
|
|
2
|
+
|
|
3
|
+
AgentScore Pay moved buyer-side x402 payments toward a single shell flow: discover, check, dry-run, pay, enforce local limits, preserve an idempotency key, and expose the flow through MCP tools. npm now shows `@agent-score/pay@0.1.0-rc.13`, one revision newer than the first 00:08 CT scan.
|
|
4
|
+
|
|
5
|
+
rc.13 raised the bar again. Its release commit added typed SDK errors, quota envelope surfacing from `X-Quota-*` headers on `assess`, deterministic recovery guidance, and payment-path security patches. That means buyer-side competition is no longer only "can the agent pay?" It is "can the agent recover safely when payment, token, quota, or spend policy checks fail?"
|
|
6
|
+
|
|
7
|
+
AgentPay MCP should not copy that CLI shape blindly. Its job is narrower and safer: make the payment decision auditable before an agent signs.
|
|
8
|
+
|
|
9
|
+
## Buyer flow AgentPay must prove
|
|
10
|
+
|
|
11
|
+
A buyer-agent payment path is acceptable only when these checks pass before signing:
|
|
12
|
+
|
|
13
|
+
1. Discover or inspect the protected endpoint.
|
|
14
|
+
2. Parse the HTTP 402 challenge from `payment-required`, `x-payment-required`, a trusted directory record, or a manual fixture.
|
|
15
|
+
3. Verify the offered network and asset against the buyer allowlist.
|
|
16
|
+
4. Verify `payTo` is non-zero and matches the expected recipient class.
|
|
17
|
+
5. Compare the required amount against the caller's max spend.
|
|
18
|
+
6. Produce a dry-run plan.
|
|
19
|
+
7. Require approval when policy says approval is needed.
|
|
20
|
+
8. Generate or verify a stable idempotency key from method, URL, body, and signer.
|
|
21
|
+
9. Classify typed payment errors into deterministic retry guidance.
|
|
22
|
+
10. Display quota and spend envelopes before retrying.
|
|
23
|
+
11. Preserve no-charge failure semantics for validation, quota, token, and spend-cap failures.
|
|
24
|
+
12. Record the audit destination, correlation ID, receipt sink, and payment result.
|
|
25
|
+
13. Run through MCP tools that are visible to the host.
|
|
26
|
+
|
|
27
|
+
The helper added in this response is `src/utils/x402-buyer-flow.ts`. It turns the checklist into a small fail-closed verifier:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { verifyX402BuyerFlow } from './src/utils/x402-buyer-flow.js';
|
|
31
|
+
|
|
32
|
+
const result = verifyX402BuyerFlow({
|
|
33
|
+
method: 'POST',
|
|
34
|
+
url: 'https://paid.example.com/mcp/search',
|
|
35
|
+
body: '{"query":"agent payments"}',
|
|
36
|
+
signer: '0x2222222222222222222222222222222222222222',
|
|
37
|
+
challengeSource: 'payment-required-header',
|
|
38
|
+
challenge: {
|
|
39
|
+
network: 'base-sepolia',
|
|
40
|
+
asset: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
|
|
41
|
+
amountRequired: '10000',
|
|
42
|
+
payTo: '0x1111111111111111111111111111111111111111',
|
|
43
|
+
},
|
|
44
|
+
allowedNetworks: ['base-sepolia'],
|
|
45
|
+
allowedAssets: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'],
|
|
46
|
+
maxSpendAtomic: '25000',
|
|
47
|
+
dryRunCompleted: true,
|
|
48
|
+
approvalState: 'approved',
|
|
49
|
+
typedError: {
|
|
50
|
+
name: 'PaymentRequiredError',
|
|
51
|
+
noCharge: true,
|
|
52
|
+
},
|
|
53
|
+
quota: {
|
|
54
|
+
limit: '100',
|
|
55
|
+
remaining: '99',
|
|
56
|
+
resetAt: '2026-05-02T10:00:00Z',
|
|
57
|
+
sourceHeaders: {
|
|
58
|
+
'X-Quota-Limit': '100',
|
|
59
|
+
'X-Quota-Remaining': '99',
|
|
60
|
+
'X-Quota-Reset': '2026-05-02T10:00:00Z',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
mcpTools: ['x402_pay', 'check_budget', 'set_spend_policy', 'get_transaction_history', 'queue_approval'],
|
|
64
|
+
audit: {
|
|
65
|
+
destination: 'otel',
|
|
66
|
+
correlationId: 'tool-call-abc',
|
|
67
|
+
receiptSink: 'transaction-history',
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (!result.ok) throw new Error(result.failures.join('\n'));
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The same helper now exposes `result.recovery` and `result.envelope`:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
{
|
|
78
|
+
recovery: {
|
|
79
|
+
errorName: 'PaymentRequiredError',
|
|
80
|
+
retryability: 'retry_after_payment',
|
|
81
|
+
noCharge: true,
|
|
82
|
+
quotaVisible: true,
|
|
83
|
+
},
|
|
84
|
+
envelope: {
|
|
85
|
+
spend: {
|
|
86
|
+
maxSpendAtomic: '25000',
|
|
87
|
+
amountRequiredAtomic: '10000',
|
|
88
|
+
remainingAfterPaymentAtomic: '15000',
|
|
89
|
+
},
|
|
90
|
+
quota: {
|
|
91
|
+
limit: '100',
|
|
92
|
+
remaining: '99',
|
|
93
|
+
resetAt: '2026-05-02T10:00:00Z',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Typed error recovery map
|
|
100
|
+
|
|
101
|
+
| Error | Agent recovery | Retryability | No-charge rule |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `PaymentRequiredError` | Obtain or approve payment, then retry with the same idempotency key | `retry_after_payment` | No charge until a valid signed payment is submitted |
|
|
104
|
+
| `QuotaExceededError` | Show quota limit, remaining units, reset time, and wait or change policy | `retry_after_quota_reset` when quota is visible, otherwise `operator_review` | No charge for quota rejection |
|
|
105
|
+
| `TokenExpiredError` | Refresh the buyer credential, then retry only after policy re-check | `refresh_token_then_retry` | No charge for expired-token rejection |
|
|
106
|
+
| `ValidationFailureError` | Do not retry automatically; fix recipient, chain, asset, amount, or challenge parsing | `do_not_retry` | No charge for validation failure |
|
|
107
|
+
| `SpendLimitExceededError` | Do not retry automatically; require operator or policy change | `do_not_retry` | No charge for local spend-cap rejection |
|
|
108
|
+
| `UnknownPaymentError` | Stop automation and route to operator review | `operator_review` | Treat charge state as unknown until audited |
|
|
109
|
+
|
|
110
|
+
## Quota and spend envelope display
|
|
111
|
+
|
|
112
|
+
Buyer-facing output should keep quota and spend in the same response object so an agent can decide without guessing:
|
|
113
|
+
|
|
114
|
+
- `quota.limit`, `quota.remaining`, and `quota.resetAt` from `X-Quota-*` headers or an equivalent trusted envelope.
|
|
115
|
+
- `spend.maxSpendAtomic`, `spend.amountRequiredAtomic`, and `spend.remainingAfterPaymentAtomic` from local policy plus the 402 challenge.
|
|
116
|
+
- `recovery.retryability` as a machine-readable value, not prose.
|
|
117
|
+
- `recovery.noCharge` as an explicit boolean for failed pre-payment checks.
|
|
118
|
+
|
|
119
|
+
If a quota error arrives without quota visibility, AgentPay should fail closed and return `operator_review`. If a typed payment error does not assert `noCharge: true`, the helper fails before retry logic can run.
|
|
120
|
+
|
|
121
|
+
## AgentScore Pay parity map
|
|
122
|
+
|
|
123
|
+
| Buyer need | AgentScore Pay signal | AgentPay MCP response |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| Discovery | `discover` queries x402 Bazaar and MPP services | Keep x402 Bazaar readback in docs and require a challenge source before pay |
|
|
126
|
+
| Check | `check` probes a protected endpoint | `verifyX402BuyerFlow` fails if network, asset, amount, or recipient is missing |
|
|
127
|
+
| Dry-run | `pay --dry-run` prints a plan | `dryRunCompleted` must be true before payment can pass verification |
|
|
128
|
+
| Spend cap | `--max-spend` and local limits | `maxSpendAtomic` and existing `set_spend_policy` / `check_budget` tools gate spend |
|
|
129
|
+
| Typed errors | `PaymentRequiredError`, `QuotaExceededError`, `TokenExpiredError` | `classifyX402PaymentError()` maps typed errors to deterministic retryability |
|
|
130
|
+
| Quota envelope | `X-Quota-*` surfacing on `assess` | `quota` keeps limit, remaining, reset, and source headers visible beside spend |
|
|
131
|
+
| No-charge failures | Deterministic recovery guidance | `typedError.noCharge` must be explicit before retry or operator action |
|
|
132
|
+
| Pay | One CLI command pays and retries | `x402_pay` stays the MCP payment tool, but only after policy checks pass |
|
|
133
|
+
| Idempotency | Stable `X-Idempotency-Key` for retries | `createX402IdempotencyKey()` derives the key from method, URL, body, and signer |
|
|
134
|
+
| MCP exposure | Every command can be an MCP tool | AgentPay exposes payment, budget, approval, session, and audit tools through MCP |
|
|
135
|
+
| Audit | Local history and structured output | `get_transaction_history`, receipt sink, and correlation ID make the result auditable |
|
|
136
|
+
|
|
137
|
+
## What AgentPay should not claim
|
|
138
|
+
|
|
139
|
+
- Do not claim AgentPay MCP has a one-command buyer CLI unless we ship that CLI.
|
|
140
|
+
- Do not claim Solana x402 signing support from this artifact. The verifier can detect allowlist mismatches, but AgentPay payment support remains tied to the configured supported network set.
|
|
141
|
+
- Do not treat MPP or opaque credits as the AgentPay default. The positioning stays x402-native buyer verification and non-custodial signing.
|
|
142
|
+
- Do not auto-retry quota, token, validation, or spend-limit failures unless the typed error, quota envelope, policy state, and audit trail make the retry safe.
|
|
143
|
+
|
|
144
|
+
## Validation proof
|
|
145
|
+
|
|
146
|
+
- Helper: `src/utils/x402-buyer-flow.ts`
|
|
147
|
+
- Tests: `tests/x402-buyer-flow.test.ts`
|
|
148
|
+
- README link: `README.md`
|
|
149
|
+
- Validation command: `npm test -- --run tests/x402-buyer-flow.test.ts`
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Payment-critical dependency pin policy
|
|
2
|
+
|
|
3
|
+
AgentPay MCP treats crypto verifier and signing dependencies as payment-critical infrastructure. A floating semver range is not acceptable when a fresh install can change the code path that parses a 402 challenge, derives an account, signs a payment, verifies a receipt, or maps a chain.
|
|
4
|
+
|
|
5
|
+
## Current pin
|
|
6
|
+
|
|
7
|
+
- `viem`: pinned exactly to `2.48.7`
|
|
8
|
+
- `package.json` dependency: `"viem": "2.48.7"`
|
|
9
|
+
- root npm override: `"viem": "2.48.7"`
|
|
10
|
+
- reason: `viem` `2.48.8` exposed a broken `@noble/curves` import path in the x402 payment ecosystem. AgentScore Pay rc.14 pinned `viem` `2.48.7`; AgentPay MCP follows the same buyer-safety posture and proves clean installs before release.
|
|
11
|
+
|
|
12
|
+
## Libraries covered by this policy
|
|
13
|
+
|
|
14
|
+
Pin exactly, or document an explicit hard override, for any package that touches:
|
|
15
|
+
|
|
16
|
+
- wallet/account derivation
|
|
17
|
+
- signature creation or verification
|
|
18
|
+
- x402 payment-required parsing
|
|
19
|
+
- receipt, transaction, or payment envelope validation
|
|
20
|
+
- chain metadata used to decide whether a payment can be signed
|
|
21
|
+
- crypto hash, curve, address, or ABI encoding paths
|
|
22
|
+
|
|
23
|
+
Today that includes `viem` and its crypto import surface. If AgentPay adds a direct dependency on `@noble/*`, `@scure/*`, `ox`, an x402 SDK package, or a facilitator client, that dependency must be reviewed under this policy before release.
|
|
24
|
+
|
|
25
|
+
## Release gate
|
|
26
|
+
|
|
27
|
+
Before publishing a package that changes x402 payment paths or crypto dependencies, run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm run build
|
|
31
|
+
npm run smoke:clean-install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The clean-install smoke creates a fresh temporary consumer project, installs the packed AgentPay MCP tarball, imports `viem`, `viem/accounts`, `viem/chains`, AgentPay's packaged `x402_pay` tool, and AgentPay's wallet client utility, then confirms the resolved `viem` version is exactly `2.48.7`.
|
|
35
|
+
|
|
36
|
+
A release must fail if:
|
|
37
|
+
|
|
38
|
+
- the root package uses `^`, `~`, `>=`, `latest`, or any non-exact range for `viem`
|
|
39
|
+
- the root override does not match the dependency pin
|
|
40
|
+
- a clean install resolves the x402 verifier path to a different `viem` version
|
|
41
|
+
- packaged AgentPay x402 imports fail in a fresh consumer project
|
|
42
|
+
- a payment path silently falls back to an unsupported chain or parser
|
|
43
|
+
|
|
44
|
+
## Upgrade process
|
|
45
|
+
|
|
46
|
+
1. Open a dependency-pin issue or PR explaining the market or security signal.
|
|
47
|
+
2. Change the exact dependency and override together.
|
|
48
|
+
3. Refresh `package-lock.json`.
|
|
49
|
+
4. Run typecheck, tests, build, pack dry-run, and clean-install smoke.
|
|
50
|
+
5. Preserve proof under `ops/proofs/` before publishing.
|
|
51
|
+
6. Publish only after the proof shows the same exact dependency version in a clean consumer install.
|
|
52
|
+
|
|
53
|
+
Do not relax this policy for convenience. Payment libraries can break buyer agents without changing AgentPay source code, so deterministic installs are part of the product contract.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "agentpay-paid-provider-health-proof/v1",
|
|
3
|
+
"generated_at": "2026-05-02T21:26:24.103Z",
|
|
4
|
+
"source": {
|
|
5
|
+
"name": "Voidly Pay public pay-health feed",
|
|
6
|
+
"url": "https://raw.githubusercontent.com/voidly-ai/voidly-pay/main/pay-health/latest.json",
|
|
7
|
+
"commit": "a907e56db0782e43ff36adf448e493d5632fba42",
|
|
8
|
+
"raw_schema": "voidly-pay-network-health/v1"
|
|
9
|
+
},
|
|
10
|
+
"health": {
|
|
11
|
+
"ok": true,
|
|
12
|
+
"latency_ms": 720
|
|
13
|
+
},
|
|
14
|
+
"ok": false,
|
|
15
|
+
"summary": {
|
|
16
|
+
"providers_probed": 5,
|
|
17
|
+
"providers_ok": 2,
|
|
18
|
+
"providers_failing": 3,
|
|
19
|
+
"success_rate": 0.4
|
|
20
|
+
},
|
|
21
|
+
"providers": [
|
|
22
|
+
{
|
|
23
|
+
"provider_id": "did:voidly:Ck56rQkhsBe3bJa62bg81t",
|
|
24
|
+
"capability": "echo.text-1777170057372",
|
|
25
|
+
"capability_id": "4f862bbb-050b-47a6-a189-71a23d10b936",
|
|
26
|
+
"status": "failed",
|
|
27
|
+
"stale_streak": 207,
|
|
28
|
+
"receipt_state": "missing",
|
|
29
|
+
"verified": false,
|
|
30
|
+
"latency_ms": 61778,
|
|
31
|
+
"error": "hire did not reach claimed state"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"provider_id": "did:voidly:3bzjV3jSMCuTwZ6NRzrS8m",
|
|
35
|
+
"capability": "echo.text-1777170066728",
|
|
36
|
+
"capability_id": "ea0ac3a8-b35c-44c2-bf76-4c964c8dd559",
|
|
37
|
+
"status": "failed",
|
|
38
|
+
"stale_streak": 207,
|
|
39
|
+
"receipt_state": "missing",
|
|
40
|
+
"verified": false,
|
|
41
|
+
"latency_ms": 61721,
|
|
42
|
+
"error": "hire did not reach claimed state"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"provider_id": "did:voidly:AsAVzZ2dtMrntgGRco8KkW",
|
|
46
|
+
"capability": "hash.sha256",
|
|
47
|
+
"capability_id": "e00a3caf-5090-4414-aab9-89e5420bf871",
|
|
48
|
+
"status": "ok",
|
|
49
|
+
"stale_streak": 0,
|
|
50
|
+
"receipt_state": "pending_acceptance_verified",
|
|
51
|
+
"receipt_id": "22640d76-9eca-4730-bbbc-b10e42e7a736",
|
|
52
|
+
"verified": true,
|
|
53
|
+
"latency_ms": 13750,
|
|
54
|
+
"x402_payment": {
|
|
55
|
+
"scheme": "exact",
|
|
56
|
+
"network": "base-sepolia",
|
|
57
|
+
"asset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
58
|
+
"payTo": "0x1111111111111111111111111111111111111111",
|
|
59
|
+
"maxAmountRequired": "800",
|
|
60
|
+
"resource": "https://api.voidly.example/pay/hash.sha256"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"provider_id": "did:voidly:Eg8JvTNrBLcpbX3r461jJB",
|
|
65
|
+
"capability": "hash.sha256",
|
|
66
|
+
"capability_id": "1b32805f-07e0-426e-a7a2-53ac41396303",
|
|
67
|
+
"status": "ok",
|
|
68
|
+
"stale_streak": 0,
|
|
69
|
+
"receipt_state": "pending_acceptance_verified",
|
|
70
|
+
"receipt_id": "df261343-afc2-49f4-8f25-4fc32e291d9e",
|
|
71
|
+
"verified": true,
|
|
72
|
+
"latency_ms": 8999,
|
|
73
|
+
"x402_payment": {
|
|
74
|
+
"scheme": "exact",
|
|
75
|
+
"network": "base-sepolia",
|
|
76
|
+
"asset": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
77
|
+
"payTo": "0x2222222222222222222222222222222222222222",
|
|
78
|
+
"maxAmountRequired": "1000",
|
|
79
|
+
"resource": "https://api.voidly.example/pay/hash.sha256"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"provider_id": "did:voidly:mkt-b-1776622972",
|
|
84
|
+
"capability": "translate",
|
|
85
|
+
"capability_id": "af7719a4-f4bf-4a8c-aafb-8c1a7b8e226c",
|
|
86
|
+
"status": "failed",
|
|
87
|
+
"stale_streak": 312,
|
|
88
|
+
"receipt_state": "missing",
|
|
89
|
+
"verified": false,
|
|
90
|
+
"latency_ms": 61627,
|
|
91
|
+
"error": "hire did not reach claimed state"
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"routing": {
|
|
95
|
+
"fail_closed": true,
|
|
96
|
+
"decision": "deny",
|
|
97
|
+
"reason": [
|
|
98
|
+
"Provider success_rate is 0.4 across five probed providers.",
|
|
99
|
+
"Three providers have repeated stale failure streaks.",
|
|
100
|
+
"Only two providers have verified receipt state.",
|
|
101
|
+
"Buyer must verify x402 network, asset, and payTo before signing."
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|