fix: python v2 x402HTTPAdapter retry logic #978
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
_is_retryflag fromHttpxHookswith per-request retry checking usingresponse.request.extensions.get("x402_is_retry")#399, feat: add Python SDK v2 server examples (FastAPI, Flask) #879Problem
Root Cause
Solution
This aligns with the patterns used in other language implementations (Go, httpx).
Flow Diagram
Before (Buggy)
sequenceDiagram participant Client participant Adapter participant Server Note over Adapter: _is_retry = False rect rgb(200, 255, 200) Note over Client,Server: Request 1 (Success) Client->>Adapter: send(request1) Adapter->>Server: HTTP GET Server-->>Adapter: 402 Payment Required Note over Adapter: _is_retry = True Adapter->>Server: HTTP GET + Payment Server-->>Adapter: 200 OK Adapter-->>Client: 200 OK Note over Adapter: _is_retry = True ⚠️ (not reset!) end rect rgb(255, 200, 200) Note over Client,Server: Request 2 (FAIL!) Client->>Adapter: send(request2) Note over Adapter: if _is_retry: return early Adapter->>Server: HTTP GET (no payment!) Server-->>Adapter: 402 Payment Required Adapter-->>Client: 402 ❌ Note over Adapter: _is_retry = False end rect rgb(200, 255, 200) Note over Client,Server: Request 3 (Success) Client->>Adapter: send(request3) Note over Adapter: _is_retry = False Adapter->>Server: HTTP GET Server-->>Adapter: 402 Payment Required Note over Adapter: _is_retry = True Adapter->>Server: HTTP GET + Payment Server-->>Adapter: 200 OK Adapter-->>Client: 200 OK endAfter (Fixed)
sequenceDiagram participant Client participant Adapter participant Server rect rgb(200, 255, 200) Note over Client,Server: Request 1 Client->>Adapter: send(request1) Adapter->>Server: HTTP GET Server-->>Adapter: 402 Payment Required Note over Adapter: retry_request = request1.copy() Note over Adapter: retry_request.headers[RETRY_HEADER] = "1" Adapter->>Server: HTTP GET + Payment + RETRY_HEADER Server-->>Adapter: 200 OK Adapter-->>Client: 200 OK end rect rgb(200, 255, 200) Note over Client,Server: Request 2 (per-request state) Client->>Adapter: send(request2) Note over Adapter: Check request2.headers[RETRY_HEADER] Note over Adapter: No header → fresh request Adapter->>Server: HTTP GET Server-->>Adapter: 402 Payment Required Note over Adapter: retry_request = request2.copy() Note over Adapter: retry_request.headers[RETRY_HEADER] = "1" Adapter->>Server: HTTP GET + Payment + RETRY_HEADER Server-->>Adapter: 200 OK Adapter-->>Client: 200 OK ✅ end rect rgb(200, 255, 200) Note over Client,Server: Request 3 Client->>Adapter: send(request3) Adapter->>Server: HTTP GET Server-->>Adapter: 402 Payment Required Note over Adapter: retry_request = request3.copy() Adapter->>Server: HTTP GET + Payment + RETRY_HEADER Server-->>Adapter: 200 OK Adapter-->>Client: 200 OK ✅ endTests
Checklist