Skip to content

feat: x402 payment support — signature caching and JSON-RPC 402 retry#119

Merged
MQ37 merged 2 commits into
mainfrom
feat/x402-payment-support
Mar 27, 2026
Merged

feat: x402 payment support — signature caching and JSON-RPC 402 retry#119
MQ37 merged 2 commits into
mainfrom
feat/x402-payment-support

Conversation

@MQ37

@MQ37 MQ37 commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Cache x402 payment signature at session level, reuse across all tool calls in a session
  • Detect x402 payment-required tool results (per x402 MCP transport spec) and auto-retry with fresh payment
  • Inject payment in both PAYMENT-SIGNATURE HTTP header and params._meta["x402/payment"] JSON-RPC body

How it works

  1. First tool call: middleware signs a fresh x402 payment using the tool's _meta.x402 metadata, caches it
  2. Subsequent calls: reuse the cached signature (acts as prepaid balance session key)
  3. On payment-required tool result (isError: true + structuredContent with x402Version/accepts): bridge invalidates cache, signs fresh from the accepts array, retries once
  4. HTTP 402 fallback: fetch middleware also handles raw HTTP 402 responses with PAYMENT-REQUIRED header

x402 MCP transport spec compatibility

Payment-required responses follow the x402 MCP transport spec used by @x402/mcp:

  • Server returns a tool result with isError: true
  • structuredContent contains the PaymentRequired object (x402Version, accepts, resource)
  • content[0].text contains the same as JSON string (fallback)
  • Client detects via extractPaymentRequiredFromResult, NOT via JSON-RPC error codes

MQ37 added 2 commits March 25, 2026 15:32
- Cache x402 payment signature at session level, reuse across tool calls
- On JSON-RPC 402 error, invalidate cache, sign fresh payment, retry once
- Extract payment requirements from SDK error via extractPaymentRequiredFromError
- Inject payment in both PAYMENT-SIGNATURE header and _meta["x402/payment"] body
Per the x402 MCP transport spec, payment-required is now signaled as a
tool result (isError: true + structuredContent/content[0].text with
PaymentRequired) instead of a JSON-RPC 402 error. This matches the
@x402/mcp client implementation.

- Replace extractPaymentRequiredFromError with extractPaymentRequiredFromResult
- Rename extractAcceptFromErrorData to extractAcceptFromPaymentRequired
- Bridge inspects tool results instead of catching thrown errors
- Remove JSON-RPC 402 error unwrapping code (no longer needed)
@MQ37

MQ37 commented Mar 26, 2026

Copy link
Copy Markdown
Collaborator Author

@jancurn Thank you for approve - I will merge this once we release the Apify MCP server with the x402 support.

@jancurn

jancurn commented Mar 27, 2026

Copy link
Copy Markdown
Member

Why wait and not merge right away?

@MQ37

MQ37 commented Mar 27, 2026

Copy link
Copy Markdown
Collaborator Author

Why wait and not merge right away?

oke, let's merge it 😎 Let's keep the momentum going 🧙

@MQ37 MQ37 merged commit 0a539d6 into main Mar 27, 2026
6 checks passed
@jancurn jancurn deleted the feat/x402-payment-support branch May 12, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants