From df2d6a207713fd9d19a4136f81ada8a469dabe9d Mon Sep 17 00:00:00 2001 From: vvillait88 Date: Thu, 14 May 2026 11:27:15 -0700 Subject: [PATCH] flatten payment directive + headers + override builders to kwargs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops the wrapper construction for the 5 payment-side builders in directive.py / headers.py / settlement_override.py: - build_payment_request_blob(PaymentRequestInput(...)) → build_payment_request_blob(*, amount_usd, rail=None, ...) - payment_directive(PaymentDirectiveInput(...)) → payment_directive(*, id, realm, request, rail=None, ...) - build_payment_directive(BuildPaymentDirectiveInput(...)) → build_payment_directive(*, rail, id, realm, amount_usd, ...) - build_payment_headers(BuildPaymentHeadersInput(...)) → build_payment_headers(*, rails=None, order_id="", realm="", x402=None) - settlement_override_header(SettlementOverrides(amount=X)) → settlement_override_header(*, amount=X) Deleted from public exports: PaymentRequestInput, PaymentDirectiveInput, BuildPaymentDirectiveInput, BuildPaymentHeadersInput, SettlementOverrides. Kept (data shapes consumers construct): PaymentHeadersRail, PaymentHeadersResult, X402AcceptsBlock. Tests: 1031 passed / 3 skipped, 95.05% coverage. ty + ruff clean. Migrated callers in tests/ + examples/api_provider.py + examples/variable_cost_merchant.py + discovery/probe.py. Co-Authored-By: Claude Opus 4.7 (1M context) --- agentscore_commerce/discovery/probe.py | 8 +- agentscore_commerce/payment/__init__.py | 10 -- agentscore_commerce/payment/directive.py | 148 ++++++++---------- agentscore_commerce/payment/headers.py | 85 +++++----- .../payment/settlement_override.py | 14 +- examples/api_provider.py | 13 +- examples/variable_cost_merchant.py | 10 +- tests/test_payment_directive.py | 17 +- tests/test_payment_headers.py | 68 +++----- tests/test_payment_misc.py | 3 +- 10 files changed, 153 insertions(+), 223 deletions(-) diff --git a/agentscore_commerce/discovery/probe.py b/agentscore_commerce/discovery/probe.py index de3b4cd..aa12176 100644 --- a/agentscore_commerce/discovery/probe.py +++ b/agentscore_commerce/discovery/probe.py @@ -7,8 +7,6 @@ from typing import Any, Literal, Protocol from agentscore_commerce.payment.directive import ( - PaymentDirectiveInput, - PaymentRequestInput, build_payment_request_blob, payment_directive, ) @@ -122,12 +120,10 @@ def build_discovery_probe_response(opts: DiscoveryProbeOptions) -> DiscoveryProb probe_id = f"probe_{int(datetime.now(UTC).timestamp() * 1000)}" expires = (datetime.now(UTC) + timedelta(seconds=opts.ttl_seconds)).isoformat().replace("+00:00", "Z") request = build_payment_request_blob( - PaymentRequestInput(rail=opts.sample_rail, amount_usd=opts.sample_amount_usd, recipient=opts.sample_recipient) + rail=opts.sample_rail, amount_usd=opts.sample_amount_usd, recipient=opts.sample_recipient ) directive = payment_directive( - PaymentDirectiveInput( - rail=opts.sample_rail, id=probe_id, realm=opts.realm, intent=opts.intent, expires=expires, request=request - ) + rail=opts.sample_rail, id=probe_id, realm=opts.realm, intent=opts.intent, expires=expires, request=request ) body_obj: dict[str, Any] = { "error": { diff --git a/agentscore_commerce/payment/__init__.py b/agentscore_commerce/payment/__init__.py index a8c46da..b030c23 100644 --- a/agentscore_commerce/payment/__init__.py +++ b/agentscore_commerce/payment/__init__.py @@ -2,16 +2,12 @@ from agentscore_commerce.payment.amounts import usd_to_atomic from agentscore_commerce.payment.directive import ( - BuildPaymentDirectiveInput, - PaymentDirectiveInput, - PaymentRequestInput, build_payment_directive, build_payment_request_blob, payment_directive, ) from agentscore_commerce.payment.dispatch import detect_rail_from_headers, dispatch_settlement_by_network from agentscore_commerce.payment.headers import ( - BuildPaymentHeadersInput, PaymentHeadersRail, PaymentHeadersResult, X402AcceptsBlock, @@ -29,7 +25,6 @@ from agentscore_commerce.payment.rails import RailDefinition, lookup_rail, rails from agentscore_commerce.payment.settlement_override import ( SETTLEMENT_OVERRIDES_HEADER, - SettlementOverrides, settlement_override_header, ) from agentscore_commerce.payment.signer import ( @@ -85,24 +80,19 @@ "SETTLEMENT_OVERRIDES_HEADER", "USDC", "X402_SUPPORTED_BASE_NETWORKS", - "BuildPaymentDirectiveInput", - "BuildPaymentHeadersInput", "ClassifiedX402Error", "CreateX402ServerOptions", "CustomScheme", "MppxRails", "NetworkFamily", - "PaymentDirectiveInput", "PaymentHeadersRail", "PaymentHeadersResult", - "PaymentRequestInput", "PaymentRequiredHeaderInput", "PaymentSigner", "ProcessX402SettleFailure", "ProcessX402SettleResult", "ProcessX402SettleSuccess", "RailDefinition", - "SettlementOverrides", "SignerNetwork", "StripeRail", "TempoChargeRail", diff --git a/agentscore_commerce/payment/directive.py b/agentscore_commerce/payment/directive.py index 8031259..0bbf9d0 100644 --- a/agentscore_commerce/payment/directive.py +++ b/agentscore_commerce/payment/directive.py @@ -6,40 +6,38 @@ import base64 import json -from dataclasses import dataclass from datetime import UTC, datetime, timedelta from agentscore_commerce.payment.rails import lookup_rail -@dataclass -class PaymentRequestInput: - amount_usd: float | str - rail: str | None = None - currency: str | None = None - decimals: int | None = None - recipient: str | None = None - chain_id: int | None = None - network_id: str | None = None # Stripe profile_id; camelCase per link-cli mpp decode validator - - -def build_payment_request_blob(input: PaymentRequestInput) -> str: +def build_payment_request_blob( + *, + amount_usd: float | str, + rail: str | None = None, + currency: str | None = None, + decimals: int | None = None, + recipient: str | None = None, + chain_id: int | None = None, + # Stripe profile_id; camelCase per link-cli mpp decode validator. + network_id: str | None = None, +) -> str: """Build the base64url-encoded `request` blob for an MPP Payment directive.""" - rail_def = lookup_rail(input.rail) if input.rail else None - decimals = input.decimals if input.decimals is not None else (rail_def.decimals if rail_def else 6) - currency = input.currency or (rail_def.currency if rail_def else "usd") - chain_id = input.chain_id if input.chain_id is not None else (rail_def.chain_id if rail_def else None) - - amount_num = float(input.amount_usd) if isinstance(input.amount_usd, str) else input.amount_usd - amount_raw = str(round(amount_num * 10**decimals)) - blob: dict[str, object] = {"amount": amount_raw, "currency": currency, "decimals": decimals} - if input.recipient: - blob["recipient"] = input.recipient + rail_def = lookup_rail(rail) if rail else None + resolved_decimals = decimals if decimals is not None else (rail_def.decimals if rail_def else 6) + resolved_currency = currency or (rail_def.currency if rail_def else "usd") + resolved_chain_id = chain_id if chain_id is not None else (rail_def.chain_id if rail_def else None) + + amount_num = float(amount_usd) if isinstance(amount_usd, str) else amount_usd + amount_raw = str(round(amount_num * 10**resolved_decimals)) + blob: dict[str, object] = {"amount": amount_raw, "currency": resolved_currency, "decimals": resolved_decimals} + if recipient: + blob["recipient"] = recipient method_details: dict[str, object] = {} - if chain_id is not None: - method_details["chainId"] = chain_id - if input.network_id: - method_details["networkId"] = input.network_id + if resolved_chain_id is not None: + method_details["chainId"] = resolved_chain_id + if network_id: + method_details["networkId"] = network_id if method_details: blob["methodDetails"] = method_details @@ -47,65 +45,57 @@ def build_payment_request_blob(input: PaymentRequestInput) -> str: return base64.urlsafe_b64encode(raw).rstrip(b"=").decode() -@dataclass -class PaymentDirectiveInput: - id: str - realm: str - request: str - rail: str | None = None - method: str | None = None - intent: str = "charge" - expires: str | None = None - - -def payment_directive(input: PaymentDirectiveInput) -> str: +def payment_directive( + *, + id: str, + realm: str, + request: str, + rail: str | None = None, + method: str | None = None, + intent: str = "charge", + expires: str | None = None, +) -> str: """Format an MPP Payment directive string for the WWW-Authenticate header.""" - rail_def = lookup_rail(input.rail) if input.rail else None - method = input.method or (rail_def.method if rail_def else "unknown") - expires = input.expires or (datetime.now(UTC) + timedelta(minutes=5)).isoformat().replace("+00:00", "Z") + rail_def = lookup_rail(rail) if rail else None + resolved_method = method or (rail_def.method if rail_def else "unknown") + resolved_expires = expires or (datetime.now(UTC) + timedelta(minutes=5)).isoformat().replace("+00:00", "Z") return ( - f'Payment id="{input.id}", realm="{input.realm}", method="{method}", ' - f'intent="{input.intent}", expires="{expires}", request="{input.request}"' + f'Payment id="{id}", realm="{realm}", method="{resolved_method}", ' + f'intent="{intent}", expires="{resolved_expires}", request="{request}"' ) -@dataclass -class BuildPaymentDirectiveInput: - rail: str - id: str - realm: str - amount_usd: float | str - currency: str | None = None - decimals: int | None = None - recipient: str | None = None - chain_id: int | None = None - network_id: str | None = None - method: str | None = None - intent: str = "charge" - expires: str | None = None - - -def build_payment_directive(input: BuildPaymentDirectiveInput) -> str: +def build_payment_directive( + *, + rail: str, + id: str, + realm: str, + amount_usd: float | str, + currency: str | None = None, + decimals: int | None = None, + recipient: str | None = None, + chain_id: int | None = None, + network_id: str | None = None, + method: str | None = None, + intent: str = "charge", + expires: str | None = None, +) -> str: """Convenience: build the request blob + directive in one call.""" request = build_payment_request_blob( - PaymentRequestInput( - rail=input.rail, - amount_usd=input.amount_usd, - currency=input.currency, - decimals=input.decimals, - recipient=input.recipient, - chain_id=input.chain_id, - network_id=input.network_id, - ) + rail=rail, + amount_usd=amount_usd, + currency=currency, + decimals=decimals, + recipient=recipient, + chain_id=chain_id, + network_id=network_id, ) return payment_directive( - PaymentDirectiveInput( - rail=input.rail, - id=input.id, - realm=input.realm, - method=input.method, - intent=input.intent, - expires=input.expires, - request=request, - ) + rail=rail, + id=id, + realm=realm, + method=method, + intent=intent, + expires=expires, + request=request, ) diff --git a/agentscore_commerce/payment/headers.py b/agentscore_commerce/payment/headers.py index ec41cc1..93dcd54 100644 --- a/agentscore_commerce/payment/headers.py +++ b/agentscore_commerce/payment/headers.py @@ -11,13 +11,10 @@ from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass from typing import Any, Literal, TypedDict -from agentscore_commerce.payment.directive import ( - BuildPaymentDirectiveInput, - build_payment_directive, -) +from agentscore_commerce.payment.directive import build_payment_directive from agentscore_commerce.payment.wwwauthenticate import ( PaymentRequiredHeaderInput, payment_required_header, @@ -69,23 +66,6 @@ class X402AcceptsBlock: resource: dict[str, str] | None = None -@dataclass -class BuildPaymentHeadersInput: - """Input shape for :func:`build_payment_headers`.""" - - rails: list[PaymentHeadersRail] = field(default_factory=list) - order_id: str = "" - """Order id used as the directive challenge id (per-rail it becomes ``{order_id}-{rail}``).""" - - realm: str = "" - """Realm — the host of the merchant URL (e.g. ``agents.merchant.example``).""" - - x402: X402AcceptsBlock | None = None - """Optional x402 ``accepts`` array — included as the standard PAYMENT-REQUIRED header - so x402 clients (``x402[fastapi]``, ``agentscore-pay``) can parse the binary-friendly - format. Pass ``None`` (or omit) to skip the PAYMENT-REQUIRED header.""" - - class PaymentHeadersResult(TypedDict, total=False): """Header dict returned by :func:`build_payment_headers`.""" @@ -98,19 +78,31 @@ class PaymentHeadersResult(TypedDict, total=False): were provided.""" -def build_payment_headers(input: BuildPaymentHeadersInput) -> PaymentHeadersResult: +def build_payment_headers( + *, + rails: list[PaymentHeadersRail] | None = None, + order_id: str = "", + realm: str = "", + x402: X402AcceptsBlock | None = None, +) -> PaymentHeadersResult: """Compose WWW-Authenticate + PAYMENT-REQUIRED headers from a single rails declaration. Returns a dict with snake_case keys — callers map to actual HTTP header names:: - headers = build_payment_headers(BuildPaymentHeadersInput(...)) + headers = build_payment_headers(...) response.headers["www-authenticate"] = headers["www_authenticate"] if "payment_required" in headers: response.headers["PAYMENT-REQUIRED"] = headers["payment_required"] + ``order_id`` is used as the directive challenge id (per-rail it becomes + ``"{order_id}-{rail}"``). ``realm`` is the host of the merchant URL (e.g. + ``agents.merchant.example``). ``x402`` is optional — pass an ``X402AcceptsBlock`` + to include the standard PAYMENT-REQUIRED header so x402 clients (``x402[fastapi]``, + ``agentscore-pay``) can parse the binary-friendly format. Omit to skip. + Example:: - headers = build_payment_headers(BuildPaymentHeadersInput( + headers = build_payment_headers( order_id="ord_123", realm="agents.merchant.example", rails=[ @@ -119,37 +111,37 @@ def build_payment_headers(input: BuildPaymentHeadersInput) -> PaymentHeadersResu PaymentHeadersRail(rail="stripe", amount_usd=25, network_id=STRIPE_PROFILE_ID), ], x402=X402AcceptsBlock(accepts=x402_accepts, version=2), - )) + ) """ directives = [] - for rail in input.rails: - # `intent` is non-Optional on BuildPaymentDirectiveInput (defaults to "charge"); + for rail_entry in rails or []: + # `intent` is non-Optional on build_payment_directive (defaults to "charge"); # only forward when the rail explicitly sets it so the default applies otherwise. kwargs: dict[str, Any] = { - "id": f"{input.order_id}-{rail.rail}", - "realm": input.realm, - "rail": rail.rail, - "amount_usd": rail.amount_usd, - "recipient": rail.recipient, - "network_id": rail.network_id, - "chain_id": rail.chain_id, - "currency": rail.currency, - "decimals": rail.decimals, - "method": rail.method, - "expires": rail.expires, + "id": f"{order_id}-{rail_entry.rail}", + "realm": realm, + "rail": rail_entry.rail, + "amount_usd": rail_entry.amount_usd, + "recipient": rail_entry.recipient, + "network_id": rail_entry.network_id, + "chain_id": rail_entry.chain_id, + "currency": rail_entry.currency, + "decimals": rail_entry.decimals, + "method": rail_entry.method, + "expires": rail_entry.expires, } - if rail.intent is not None: - kwargs["intent"] = rail.intent - directives.append(build_payment_directive(BuildPaymentDirectiveInput(**kwargs))) + if rail_entry.intent is not None: + kwargs["intent"] = rail_entry.intent + directives.append(build_payment_directive(**kwargs)) result: PaymentHeadersResult = {"www_authenticate": www_authenticate_header(directives)} - if input.x402 is not None: + if x402 is not None: result["payment_required"] = payment_required_header( PaymentRequiredHeaderInput( - x402_version=input.x402.version, - accepts=input.x402.accepts, - resource=input.x402.resource, + x402_version=x402.version, + accepts=x402.accepts, + resource=x402.resource, ), ) @@ -157,7 +149,6 @@ def build_payment_headers(input: BuildPaymentHeadersInput) -> PaymentHeadersResu __all__ = [ - "BuildPaymentHeadersInput", "PaymentHeadersRail", "PaymentHeadersResult", "X402AcceptsBlock", diff --git a/agentscore_commerce/payment/settlement_override.py b/agentscore_commerce/payment/settlement_override.py index a11a541..a231351 100644 --- a/agentscore_commerce/payment/settlement_override.py +++ b/agentscore_commerce/payment/settlement_override.py @@ -8,16 +8,14 @@ """ import json -from dataclasses import dataclass SETTLEMENT_OVERRIDES_HEADER = "Settlement-Overrides" -@dataclass -class SettlementOverrides: - amount: str # raw atomic units, '%' percentage, or '$X.YZ' dollar price +def settlement_override_header(*, amount: str) -> tuple[str, str]: + """Build a (name, value) pair for the x402 Settlement-Overrides response header. - -def settlement_override_header(overrides: SettlementOverrides) -> tuple[str, str]: - """Build a (name, value) pair for the x402 Settlement-Overrides response header.""" - return SETTLEMENT_OVERRIDES_HEADER, json.dumps({"amount": overrides.amount}, separators=(",", ":")) + ``amount`` is the override value: raw atomic units, ``"%"`` percentage, or ``"$X.YZ"`` + dollar price. + """ + return SETTLEMENT_OVERRIDES_HEADER, json.dumps({"amount": amount}, separators=(",", ":")) diff --git a/examples/api_provider.py b/examples/api_provider.py index 14c6c1f..1b838e7 100644 --- a/examples/api_provider.py +++ b/examples/api_provider.py @@ -46,7 +46,6 @@ ) from agentscore_commerce.payment import ( USDC, - PaymentDirectiveInput, networks, payment_directive, www_authenticate_header, @@ -109,15 +108,9 @@ async def search(request: Request): "mpp-solana-devnet" if networks.solana.devnet.caip2 == SOLANA_NETWORK_CAIP2 else "mpp-solana-mainnet" ) directives = [ - payment_directive( - PaymentDirectiveInput(rail=_TEMPO_RAIL, id=f"{challenge_id}_tempo", realm=REALM, request="") - ), - payment_directive( - PaymentDirectiveInput(rail=x402_base_rail, id=f"{challenge_id}_base", realm=REALM, request="") - ), - payment_directive( - PaymentDirectiveInput(rail=solana_mpp_rail, id=f"{challenge_id}_solana", realm=REALM, request="") - ), + payment_directive(rail=_TEMPO_RAIL, id=f"{challenge_id}_tempo", realm=REALM, request=""), + payment_directive(rail=x402_base_rail, id=f"{challenge_id}_base", realm=REALM, request=""), + payment_directive(rail=solana_mpp_rail, id=f"{challenge_id}_solana", realm=REALM, request=""), ] accepts = [ { diff --git a/examples/variable_cost_merchant.py b/examples/variable_cost_merchant.py index 293f872..266e5bd 100644 --- a/examples/variable_cost_merchant.py +++ b/examples/variable_cost_merchant.py @@ -39,8 +39,6 @@ from fastapi.responses import JSONResponse from agentscore_commerce.payment import ( - PaymentDirectiveInput, - SettlementOverrides, payment_directive, settlement_override_header, www_authenticate_header, @@ -54,10 +52,8 @@ def _build_402_body(url: str) -> tuple[dict, dict]: directives = [ - payment_directive(PaymentDirectiveInput(rail="x402-base-mainnet-upto", id="chg_upto", realm=REALM, request="")), - payment_directive( - PaymentDirectiveInput(rail="tempo-mainnet", id="chg_session", realm=REALM, intent="session", request="") - ), + payment_directive(rail="x402-base-mainnet-upto", id="chg_upto", realm=REALM, request=""), + payment_directive(rail="tempo-mainnet", id="chg_session", realm=REALM, intent="session", request=""), ] body = { "payment_required": True, @@ -87,7 +83,7 @@ async def complete(request: Request): actual_atomic = str(int(actual_usd * 1_000_000)) # USDC atomic units # Tell the facilitator to settle for `actual_atomic` instead of the authorized max. - name, value = settlement_override_header(SettlementOverrides(amount=actual_atomic)) + name, value = settlement_override_header(amount=actual_atomic) return JSONResponse( {"text": text, "tokens_used": tokens_used, "charged_usd": actual_usd}, headers={name: value}, diff --git a/tests/test_payment_directive.py b/tests/test_payment_directive.py index bd1250c..354baa4 100644 --- a/tests/test_payment_directive.py +++ b/tests/test_payment_directive.py @@ -2,9 +2,6 @@ import json from agentscore_commerce.payment import ( - BuildPaymentDirectiveInput, - PaymentDirectiveInput, - PaymentRequestInput, build_payment_directive, build_payment_request_blob, payment_directive, @@ -17,7 +14,7 @@ def _decode(blob: str) -> dict: def test_build_payment_request_blob_with_rail(): - blob = build_payment_request_blob(PaymentRequestInput(rail="x402-base-mainnet", amount_usd=1.0)) + blob = build_payment_request_blob(rail="x402-base-mainnet", amount_usd=1.0) decoded = _decode(blob) assert decoded["amount"] == "1000000" # 1 USDC at 6 decimals assert decoded["currency"] == "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" @@ -27,7 +24,7 @@ def test_build_payment_request_blob_with_rail(): def test_build_payment_request_blob_overrides_take_precedence(): blob = build_payment_request_blob( - PaymentRequestInput(rail="x402-base-mainnet", amount_usd=2, decimals=2, currency="usd", network_id="acct_x") + rail="x402-base-mainnet", amount_usd=2, decimals=2, currency="usd", network_id="acct_x" ) decoded = _decode(blob) assert decoded["amount"] == "200" @@ -41,7 +38,7 @@ def test_build_payment_request_blob_includes_decimals_for_node_parity(): (mppx tempo schema requires it). If this assertion fails, node-commerce and python-commerce are emitting different request blobs for the same payment, which breaks cross-SDK interop. """ - blob = build_payment_request_blob(PaymentRequestInput(rail="tempo-mainnet", amount_usd="1.50", recipient="0xabc")) + blob = build_payment_request_blob(rail="tempo-mainnet", amount_usd="1.50", recipient="0xabc") decoded = _decode(blob) # Output keys must match buildPaymentRequestBlob exactly across both SDK languages assert set(decoded.keys()) >= {"amount", "currency", "decimals"} @@ -50,9 +47,7 @@ def test_build_payment_request_blob_includes_decimals_for_node_parity(): def test_payment_directive_format(): - directive = payment_directive( - PaymentDirectiveInput(rail="tempo-mainnet", id="chg_1", realm="ex.com", request="abc") - ) + directive = payment_directive(rail="tempo-mainnet", id="chg_1", realm="ex.com", request="abc") assert directive.startswith('Payment id="chg_1"') assert 'method="tempo"' in directive assert 'intent="charge"' in directive @@ -61,9 +56,7 @@ def test_payment_directive_format(): def test_build_payment_directive_combines_blob_and_directive(): directive = build_payment_directive( - BuildPaymentDirectiveInput( - rail="tempo-mainnet", id="chg_2", realm="ex.com", amount_usd="0.5", recipient="0xabc" - ) + rail="tempo-mainnet", id="chg_2", realm="ex.com", amount_usd="0.5", recipient="0xabc" ) assert 'method="tempo"' in directive assert "request=" in directive diff --git a/tests/test_payment_headers.py b/tests/test_payment_headers.py index 7603c42..bf94efd 100644 --- a/tests/test_payment_headers.py +++ b/tests/test_payment_headers.py @@ -4,26 +4,18 @@ import json from agentscore_commerce.payment import ( - BuildPaymentHeadersInput, PaymentHeadersRail, X402AcceptsBlock, build_payment_headers, ) -def _input(rails, x402=None) -> BuildPaymentHeadersInput: - return BuildPaymentHeadersInput( - rails=rails, - order_id="ord_1", - realm="agents.example", - x402=x402, - ) +def _call(rails, x402=None): + return build_payment_headers(rails=rails, order_id="ord_1", realm="agents.example", x402=x402) def test_emits_single_directive_when_one_rail(): - result = build_payment_headers( - _input([PaymentHeadersRail(rail="tempo-mainnet", amount_usd=10, recipient="0xrecipient")]), - ) + result = _call([PaymentHeadersRail(rail="tempo-mainnet", amount_usd=10, recipient="0xrecipient")]) assert "Payment " in result["www_authenticate"] assert 'id="ord_1-tempo-mainnet"' in result["www_authenticate"] assert 'realm="agents.example"' in result["www_authenticate"] @@ -31,37 +23,31 @@ def test_emits_single_directive_when_one_rail(): def test_joins_multiple_directives_per_rfc_7235(): - result = build_payment_headers( - _input( - [ - PaymentHeadersRail(rail="tempo-mainnet", amount_usd=10, recipient="0xtempo"), - PaymentHeadersRail(rail="x402-base-mainnet", amount_usd=10, recipient="0xbase"), - ], - ), + result = _call( + [ + PaymentHeadersRail(rail="tempo-mainnet", amount_usd=10, recipient="0xtempo"), + PaymentHeadersRail(rail="x402-base-mainnet", amount_usd=10, recipient="0xbase"), + ], ) directives = [s for s in result["www_authenticate"].split(", ") if s.startswith("Payment ")] assert len(directives) == 2 def test_unique_challenge_ids_per_rail(): - result = build_payment_headers( - _input( - [ - PaymentHeadersRail(rail="tempo-mainnet", amount_usd=1, recipient="0xa"), - PaymentHeadersRail(rail="mpp-solana-mainnet", amount_usd=1, recipient="0xb"), - ], - ), + result = _call( + [ + PaymentHeadersRail(rail="tempo-mainnet", amount_usd=1, recipient="0xa"), + PaymentHeadersRail(rail="mpp-solana-mainnet", amount_usd=1, recipient="0xb"), + ], ) assert 'id="ord_1-tempo-mainnet"' in result["www_authenticate"] assert 'id="ord_1-mpp-solana-mainnet"' in result["www_authenticate"] def test_emits_payment_required_header_when_x402_provided(): - result = build_payment_headers( - _input( - [PaymentHeadersRail(rail="x402-base-mainnet", amount_usd=1, recipient="0xa")], - x402=X402AcceptsBlock(accepts=[{"scheme": "exact", "network": "eip155:8453"}], version=1), - ), + result = _call( + [PaymentHeadersRail(rail="x402-base-mainnet", amount_usd=1, recipient="0xa")], + x402=X402AcceptsBlock(accepts=[{"scheme": "exact", "network": "eip155:8453"}], version=1), ) assert "payment_required" in result decoded = json.loads(base64.b64decode(result["payment_required"]).decode()) @@ -71,18 +57,16 @@ def test_emits_payment_required_header_when_x402_provided(): def test_passes_through_intent_and_expires(): expires = "2099-12-31T23:59:59Z" - result = build_payment_headers( - _input( - [ - PaymentHeadersRail( - rail="tempo-mainnet", - amount_usd=1, - recipient="0xa", - intent="session", - expires=expires, - ), - ], - ), + result = _call( + [ + PaymentHeadersRail( + rail="tempo-mainnet", + amount_usd=1, + recipient="0xa", + intent="session", + expires=expires, + ), + ], ) assert 'intent="session"' in result["www_authenticate"] assert f'expires="{expires}"' in result["www_authenticate"] diff --git a/tests/test_payment_misc.py b/tests/test_payment_misc.py index 0516597..17f56af 100644 --- a/tests/test_payment_misc.py +++ b/tests/test_payment_misc.py @@ -5,7 +5,6 @@ SETTLEMENT_OVERRIDES_HEADER, USDC, PaymentRequiredHeaderInput, - SettlementOverrides, lookup_rail, network_family, networks, @@ -96,7 +95,7 @@ def test_payment_required_header_emits_v1_alias_for_v2_clients(): def test_settlement_override_header_returns_name_value_pair(): - name, value = settlement_override_header(SettlementOverrides(amount="1500")) + name, value = settlement_override_header(amount="1500") assert name == SETTLEMENT_OVERRIDES_HEADER assert json.loads(value) == {"amount": "1500"}