Skip to content

Commit 4396e9b

Browse files
vvillait88claude
andauthored
feat: flatten payment_required_header to kwargs; delete PaymentRequiredHeaderInput (#36)
## Summary Drops the wrapper for `payment_required_header` — the x402 PAYMENT-REQUIRED header builder. | Before | After | |---|---| | `payment_required_header(PaymentRequiredHeaderInput(x402_version=..., accepts=..., resource=...))` | `payment_required_header(*, x402_version, accepts, resource=None)` | Also drops the wrapper-bridge `respond_402` carried since #32 — its public `x402: dict` kwarg now flows straight to `payment_required_header(**x402)` without re-wrapping. ## Internal callers updated - `challenge/respond_402.py` - `payment/headers.py` (`build_payment_headers` x402 branch) - `discovery/probe.py` (discovery probe PAYMENT-REQUIRED header) ## Deleted from exports `PaymentRequiredHeaderInput`. ## Test plan - [x] `uv run pytest tests/` — 1031 passed / 3 skipped, 95.04% coverage - [x] `uv run ty check agentscore_commerce/` — clean - [x] `uv run ruff check . && uv run ruff format .` — clean - [x] `uv run vulture agentscore_commerce/ --min-confidence 80` — only the known string-cast / Protocol-param false positives remain (unchanged from main) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7f6b017 commit 4396e9b

6 files changed

Lines changed: 19 additions & 38 deletions

File tree

agentscore_commerce/challenge/respond_402.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from dataclasses import dataclass
3131
from typing import Any
3232

33-
from agentscore_commerce.payment.wwwauthenticate import PaymentRequiredHeaderInput, payment_required_header
33+
from agentscore_commerce.payment.wwwauthenticate import payment_required_header
3434

3535

3636
@dataclass
@@ -61,8 +61,5 @@ def respond_402(
6161
headers = {k.lower(): v for k, v in mppx_challenge_headers.items()}
6262
headers["content-type"] = "application/json"
6363
if x402 is not None:
64-
# PaymentRequiredHeaderInput still exists pending the wwwauthenticate
65-
# flatten in a subsequent PR; respond_402's public API takes a dict now
66-
# and we adapt internally so the wrapper deletion is invisible to callers.
67-
headers["payment-required"] = payment_required_header(PaymentRequiredHeaderInput(**x402))
64+
headers["payment-required"] = payment_required_header(**x402)
6865
return Respond402Result(body=body, headers=headers, status=402)

agentscore_commerce/discovery/probe.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
)
1313
from agentscore_commerce.payment.networks import networks
1414
from agentscore_commerce.payment.usdc import USDC
15-
from agentscore_commerce.payment.wwwauthenticate import (
16-
PaymentRequiredHeaderInput,
17-
payment_required_header,
18-
)
15+
from agentscore_commerce.payment.wwwauthenticate import payment_required_header
1916

2017
# Placeholder payTo for x402 sample accepts in the discovery probe — the probe
2118
# exists for crawlers to find that we support x402, not for actual payment.
@@ -156,7 +153,7 @@ def build_discovery_probe_response(opts: DiscoveryProbeOptions) -> DiscoveryProb
156153
"url": opts.x402_sample.resource_url,
157154
"mimeType": "application/json",
158155
}
159-
encoded = payment_required_header(PaymentRequiredHeaderInput(**header_kwargs))
156+
encoded = payment_required_header(**header_kwargs)
160157
headers["payment-required"] = encoded
161158
# Mirror the aliased accepts in the body so clients that fall back from
162159
# header → body (e.g. awal's discover) can still extract requirements.

agentscore_commerce/payment/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
)
3737
from agentscore_commerce.payment.usdc import USDC
3838
from agentscore_commerce.payment.wwwauthenticate import (
39-
PaymentRequiredHeaderInput,
4039
alias_amount_fields,
4140
payment_required_header,
4241
www_authenticate_header,
@@ -87,7 +86,6 @@
8786
"NetworkFamily",
8887
"PaymentHeadersRail",
8988
"PaymentHeadersResult",
90-
"PaymentRequiredHeaderInput",
9189
"PaymentSigner",
9290
"ProcessX402SettleFailure",
9391
"ProcessX402SettleResult",

agentscore_commerce/payment/headers.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
from agentscore_commerce.payment.directive import build_payment_directive
1818
from agentscore_commerce.payment.wwwauthenticate import (
19-
PaymentRequiredHeaderInput,
2019
payment_required_header,
2120
www_authenticate_header,
2221
)
@@ -138,11 +137,9 @@ def build_payment_headers(
138137

139138
if x402 is not None:
140139
result["payment_required"] = payment_required_header(
141-
PaymentRequiredHeaderInput(
142-
x402_version=x402.version,
143-
accepts=x402.accepts,
144-
resource=x402.resource,
145-
),
140+
x402_version=x402.version,
141+
accepts=x402.accepts,
142+
resource=x402.resource,
146143
)
147144

148145
return result

agentscore_commerce/payment/wwwauthenticate.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import base64
44
import json
5-
from dataclasses import dataclass
65
from typing import Any, Literal
76

87

@@ -39,21 +38,19 @@ def alias_amount_fields(accepts: list[Any]) -> list[Any]:
3938
return out
4039

4140

42-
@dataclass
43-
class PaymentRequiredHeaderInput:
44-
x402_version: Literal[1, 2]
45-
accepts: list[Any]
46-
resource: dict[str, str] | None = None
47-
48-
49-
def payment_required_header(input: PaymentRequiredHeaderInput) -> str:
41+
def payment_required_header(
42+
*,
43+
x402_version: Literal[1, 2],
44+
accepts: list[Any],
45+
resource: dict[str, str] | None = None,
46+
) -> str:
5047
"""Encode the standard x402 PAYMENT-REQUIRED header (base64-encoded JSON).
5148
5249
Each accepts entry is post-processed via :func:`alias_amount_fields` so v1-only
5350
clients (e.g. awal) and v2-strict clients can both read it.
5451
"""
55-
body: dict[str, Any] = {"x402Version": input.x402_version, "accepts": alias_amount_fields(input.accepts)}
56-
if input.resource is not None:
57-
body["resource"] = input.resource
52+
body: dict[str, Any] = {"x402Version": x402_version, "accepts": alias_amount_fields(accepts)}
53+
if resource is not None:
54+
body["resource"] = resource
5855
raw = json.dumps(body, separators=(",", ":")).encode()
5956
return base64.b64encode(raw).decode()

tests/test_payment_misc.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from agentscore_commerce.payment import (
55
SETTLEMENT_OVERRIDES_HEADER,
66
USDC,
7-
PaymentRequiredHeaderInput,
87
lookup_rail,
98
network_family,
109
networks,
@@ -58,9 +57,7 @@ def test_www_authenticate_header_joins_directives():
5857

5958

6059
def test_payment_required_header_base64_encodes_json():
61-
h = payment_required_header(
62-
PaymentRequiredHeaderInput(x402_version=2, accepts=[{"scheme": "exact"}], resource={"url": "https://x"})
63-
)
60+
h = payment_required_header(x402_version=2, accepts=[{"scheme": "exact"}], resource={"url": "https://x"})
6461
decoded = json.loads(base64.b64decode(h))
6562
assert decoded["x402Version"] == 2
6663
assert decoded["accepts"] == [{"scheme": "exact"}]
@@ -75,10 +72,8 @@ def test_payment_required_header_emits_v1_alias_for_v2_clients():
7572
from agentscore_commerce.payment import alias_amount_fields
7673

7774
h = payment_required_header(
78-
PaymentRequiredHeaderInput(
79-
x402_version=2,
80-
accepts=[{"scheme": "exact", "network": "eip155:84532", "amount": "110000"}],
81-
)
75+
x402_version=2,
76+
accepts=[{"scheme": "exact", "network": "eip155:84532", "amount": "110000"}],
8277
)
8378
decoded = json.loads(base64.b64decode(h))
8479
assert decoded["accepts"][0]["amount"] == "110000"

0 commit comments

Comments
 (0)