Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions agentscore_commerce/challenge/respond_402.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from dataclasses import dataclass
from typing import Any

from agentscore_commerce.payment.wwwauthenticate import PaymentRequiredHeaderInput, payment_required_header
from agentscore_commerce.payment.wwwauthenticate import payment_required_header


@dataclass
Expand Down Expand Up @@ -61,8 +61,5 @@ def respond_402(
headers = {k.lower(): v for k, v in mppx_challenge_headers.items()}
headers["content-type"] = "application/json"
if x402 is not None:
# PaymentRequiredHeaderInput still exists pending the wwwauthenticate
# flatten in a subsequent PR; respond_402's public API takes a dict now
# and we adapt internally so the wrapper deletion is invisible to callers.
headers["payment-required"] = payment_required_header(PaymentRequiredHeaderInput(**x402))
headers["payment-required"] = payment_required_header(**x402)
return Respond402Result(body=body, headers=headers, status=402)
7 changes: 2 additions & 5 deletions agentscore_commerce/discovery/probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@
)
from agentscore_commerce.payment.networks import networks
from agentscore_commerce.payment.usdc import USDC
from agentscore_commerce.payment.wwwauthenticate import (
PaymentRequiredHeaderInput,
payment_required_header,
)
from agentscore_commerce.payment.wwwauthenticate import payment_required_header

# Placeholder payTo for x402 sample accepts in the discovery probe — the probe
# exists for crawlers to find that we support x402, not for actual payment.
Expand Down Expand Up @@ -156,7 +153,7 @@ def build_discovery_probe_response(opts: DiscoveryProbeOptions) -> DiscoveryProb
"url": opts.x402_sample.resource_url,
"mimeType": "application/json",
}
encoded = payment_required_header(PaymentRequiredHeaderInput(**header_kwargs))
encoded = payment_required_header(**header_kwargs)
headers["payment-required"] = encoded
# Mirror the aliased accepts in the body so clients that fall back from
# header → body (e.g. awal's discover) can still extract requirements.
Expand Down
2 changes: 0 additions & 2 deletions agentscore_commerce/payment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
)
from agentscore_commerce.payment.usdc import USDC
from agentscore_commerce.payment.wwwauthenticate import (
PaymentRequiredHeaderInput,
alias_amount_fields,
payment_required_header,
www_authenticate_header,
Expand Down Expand Up @@ -87,7 +86,6 @@
"NetworkFamily",
"PaymentHeadersRail",
"PaymentHeadersResult",
"PaymentRequiredHeaderInput",
"PaymentSigner",
"ProcessX402SettleFailure",
"ProcessX402SettleResult",
Expand Down
9 changes: 3 additions & 6 deletions agentscore_commerce/payment/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from agentscore_commerce.payment.directive import build_payment_directive
from agentscore_commerce.payment.wwwauthenticate import (
PaymentRequiredHeaderInput,
payment_required_header,
www_authenticate_header,
)
Expand Down Expand Up @@ -138,11 +137,9 @@ def build_payment_headers(

if x402 is not None:
result["payment_required"] = payment_required_header(
PaymentRequiredHeaderInput(
x402_version=x402.version,
accepts=x402.accepts,
resource=x402.resource,
),
x402_version=x402.version,
accepts=x402.accepts,
resource=x402.resource,
)

return result
Expand Down
21 changes: 9 additions & 12 deletions agentscore_commerce/payment/wwwauthenticate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import base64
import json
from dataclasses import dataclass
from typing import Any, Literal


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


@dataclass
class PaymentRequiredHeaderInput:
x402_version: Literal[1, 2]
accepts: list[Any]
resource: dict[str, str] | None = None


def payment_required_header(input: PaymentRequiredHeaderInput) -> str:
def payment_required_header(
*,
x402_version: Literal[1, 2],
accepts: list[Any],
resource: dict[str, str] | None = None,
) -> str:
"""Encode the standard x402 PAYMENT-REQUIRED header (base64-encoded JSON).

Each accepts entry is post-processed via :func:`alias_amount_fields` so v1-only
clients (e.g. awal) and v2-strict clients can both read it.
"""
body: dict[str, Any] = {"x402Version": input.x402_version, "accepts": alias_amount_fields(input.accepts)}
if input.resource is not None:
body["resource"] = input.resource
body: dict[str, Any] = {"x402Version": x402_version, "accepts": alias_amount_fields(accepts)}
if resource is not None:
body["resource"] = resource
raw = json.dumps(body, separators=(",", ":")).encode()
return base64.b64encode(raw).decode()
11 changes: 3 additions & 8 deletions tests/test_payment_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from agentscore_commerce.payment import (
SETTLEMENT_OVERRIDES_HEADER,
USDC,
PaymentRequiredHeaderInput,
lookup_rail,
network_family,
networks,
Expand Down Expand Up @@ -58,9 +57,7 @@ def test_www_authenticate_header_joins_directives():


def test_payment_required_header_base64_encodes_json():
h = payment_required_header(
PaymentRequiredHeaderInput(x402_version=2, accepts=[{"scheme": "exact"}], resource={"url": "https://x"})
)
h = payment_required_header(x402_version=2, accepts=[{"scheme": "exact"}], resource={"url": "https://x"})
decoded = json.loads(base64.b64decode(h))
assert decoded["x402Version"] == 2
assert decoded["accepts"] == [{"scheme": "exact"}]
Expand All @@ -75,10 +72,8 @@ def test_payment_required_header_emits_v1_alias_for_v2_clients():
from agentscore_commerce.payment import alias_amount_fields

h = payment_required_header(
PaymentRequiredHeaderInput(
x402_version=2,
accepts=[{"scheme": "exact", "network": "eip155:84532", "amount": "110000"}],
)
x402_version=2,
accepts=[{"scheme": "exact", "network": "eip155:84532", "amount": "110000"}],
)
decoded = json.loads(base64.b64decode(h))
assert decoded["accepts"][0]["amount"] == "110000"
Expand Down