agentscore-commerce 1.3.2__tar.gz → 1.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/PKG-INFO +1 -1
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402_settle.py +49 -4
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/pyproject.toml +1 -1
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_lifted_helpers.py +214 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/uv.lock +1 -1
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/dependabot.yml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/workflows/ci.yml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/workflows/publish.yml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/workflows/security.yml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.gitignore +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/CLAUDE.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/CODE_OF_CONDUCT.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/CONTRIBUTING.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/LICENSE +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/README.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/SECURITY.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/api/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/accepted_methods.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/agent_instructions.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/agent_memory.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/body.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/how_to_pay.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/identity.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/order_receipt.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/pricing.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/respond_402.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/validation_error.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/bazaar.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/llms_txt.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/openapi.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/probe.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/robots_tag.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/skill_md.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/well_known_mpp.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/well_known_x402.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/_denial.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/_response.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/a2a.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/address.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/aiohttp.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/cache.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/client.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/django.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/fastapi.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/flask.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/middleware.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/policy.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/py.typed +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/sanic.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/sessions.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/signer.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/types.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/ucp.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/directive.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/dispatch.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/headers.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/idempotency.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/mppx_server.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/networks.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/rails.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/settlement_override.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/signer.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/usdc.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/wwwauthenticate.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402_server.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402_validation.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/stripe_multichain/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/stripe_multichain/mppx_stripe.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/stripe_multichain/payment_intent.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/stripe_multichain/pi_cache.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/stripe_multichain/simulate_deposit.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/README.md +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/api_provider.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/compliance_merchant.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/identity_only.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/multi_rail_merchant.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/per_product_policy_merchant.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/stripe_multichain_merchant.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/variable_cost_merchant.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/lefthook.yml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/ruff.toml +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/__init__.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/conftest.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_a2a.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_address.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_agent_memory_emitter.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_aiohttp.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_api_reexport.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_cache.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_challenge.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_client.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_coverage_fillers.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_denial.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_discovery.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_django.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_fastapi.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_flask.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_gate_quota_info.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_idempotency_helper.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_integration.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_middleware.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_directive.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_dispatch.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_headers.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_misc.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_servers.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_payment_signer.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_policy.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_pricing.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_response.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_robots_tag.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_sanic.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_sessions.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_signer_match.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_skill_md.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_stripe_multichain.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_ucp.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/tests/test_validation_error.py +0 -0
- {agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/vulture_whitelist.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentscore-commerce
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce.
|
|
5
5
|
Project-URL: Homepage, https://agentscore.sh
|
|
6
6
|
Project-URL: Repository, https://github.com/agentscore/python-commerce
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402_settle.py
RENAMED
|
@@ -220,10 +220,40 @@ def _coerce_resource_config(config: Any) -> Any:
|
|
|
220
220
|
return config
|
|
221
221
|
|
|
222
222
|
|
|
223
|
+
def _coerce_payment_payload(payload: Any) -> Any:
|
|
224
|
+
"""Best-effort dict → x402 ``PaymentPayload`` (v1 or v2) coercion.
|
|
225
|
+
|
|
226
|
+
``verify_x402_request`` returns ``payload`` as a plain dict (the result of
|
|
227
|
+
``json.loads(base64.b64decode(X-Payment))``), but x402 2.9's
|
|
228
|
+
``server.verify_payment`` / ``server.settle_payment`` call ``payload.get_scheme()``
|
|
229
|
+
and other typed-model methods on it. Without coercion, the dict raises
|
|
230
|
+
``AttributeError("'dict' object has no attribute 'get_scheme'")`` on the verify leg.
|
|
231
|
+
|
|
232
|
+
Routes by the ``x402Version`` field: ``1`` → ``PaymentPayloadV1`` (flat shape with
|
|
233
|
+
top-level ``scheme`` / ``network``); anything else → ``PaymentPayload`` (v2 shape
|
|
234
|
+
nested under ``accepted``). Falls back to the original dict on any failure so callers
|
|
235
|
+
that already pass typed instances or unusual shapes still flow through unchanged.
|
|
236
|
+
"""
|
|
237
|
+
if not isinstance(payload, dict):
|
|
238
|
+
return payload
|
|
239
|
+
try:
|
|
240
|
+
from x402.schemas import PaymentPayload
|
|
241
|
+
from x402.schemas.v1 import PaymentPayloadV1
|
|
242
|
+
except ImportError:
|
|
243
|
+
return payload
|
|
244
|
+
version = payload.get("x402Version")
|
|
245
|
+
model = PaymentPayloadV1 if version == 1 else PaymentPayload
|
|
246
|
+
try:
|
|
247
|
+
return model.model_validate(payload)
|
|
248
|
+
except Exception:
|
|
249
|
+
return payload
|
|
250
|
+
|
|
251
|
+
|
|
223
252
|
async def process_x402_settle(input: ProcessX402SettleInput) -> ProcessX402SettleResult:
|
|
224
253
|
"""Run the x402 verify→settle flow and return a tagged outcome."""
|
|
225
254
|
server = input.x402_server
|
|
226
255
|
resource_config = _coerce_resource_config(input.resource_config)
|
|
256
|
+
payload = _coerce_payment_payload(input.payload)
|
|
227
257
|
|
|
228
258
|
try:
|
|
229
259
|
built_requirements = server.build_payment_requirements(resource_config)
|
|
@@ -267,7 +297,7 @@ async def process_x402_settle(input: ProcessX402SettleInput) -> ProcessX402Settl
|
|
|
267
297
|
# — not ``process_payment_request`` (a fictional method that earlier versions of this
|
|
268
298
|
# helper called and only ever worked against test stubs).
|
|
269
299
|
try:
|
|
270
|
-
verify_result = await server.verify_payment(
|
|
300
|
+
verify_result = await server.verify_payment(payload, matched_requirement)
|
|
271
301
|
except Exception as err:
|
|
272
302
|
return ProcessX402SettleFailure(phase="facilitator_error", step="verify_payment", error=err)
|
|
273
303
|
|
|
@@ -288,11 +318,10 @@ async def process_x402_settle(input: ProcessX402SettleInput) -> ProcessX402Settl
|
|
|
288
318
|
return ProcessX402SettleFailure(phase="verify_failed", verify_result=verify_result)
|
|
289
319
|
|
|
290
320
|
try:
|
|
291
|
-
settle_result = await server.settle_payment(
|
|
321
|
+
settle_result = await server.settle_payment(payload, matched_requirement)
|
|
292
322
|
payment_response_header: str | None = None
|
|
293
323
|
if settle_result is not None:
|
|
294
|
-
|
|
295
|
-
payment_response_header = base64.b64encode(payload_bytes).decode()
|
|
324
|
+
payment_response_header = base64.b64encode(_settle_result_to_json_bytes(settle_result)).decode()
|
|
296
325
|
return ProcessX402SettleSuccess(
|
|
297
326
|
matched_requirement=matched_requirement,
|
|
298
327
|
settle_result=settle_result,
|
|
@@ -301,3 +330,19 @@ async def process_x402_settle(input: ProcessX402SettleInput) -> ProcessX402Settl
|
|
|
301
330
|
)
|
|
302
331
|
except Exception as err:
|
|
303
332
|
return ProcessX402SettleFailure(phase="settle_failed", error=err, matched_requirement=matched_requirement)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _settle_result_to_json_bytes(settle_result: Any) -> bytes:
|
|
336
|
+
"""Serialize the settle result to a base64-friendly JSON byte string.
|
|
337
|
+
|
|
338
|
+
x402 2.9's ``settle_payment`` returns a Pydantic ``SettleResponse`` model that
|
|
339
|
+
``json.dumps`` rejects with ``TypeError: Object of type SettleResponse is not
|
|
340
|
+
JSON serializable``. Use ``model_dump_json(by_alias=True)`` for Pydantic models
|
|
341
|
+
(so emitted keys match the wire shape — ``errorReason`` / ``errorMessage`` rather
|
|
342
|
+
than the snake_case attrs) and fall through to ``json.dumps`` for plain dicts
|
|
343
|
+
(used by older x402 / test stubs).
|
|
344
|
+
"""
|
|
345
|
+
model_dump_json = getattr(settle_result, "model_dump_json", None)
|
|
346
|
+
if callable(model_dump_json):
|
|
347
|
+
return model_dump_json(by_alias=True).encode()
|
|
348
|
+
return json.dumps(settle_result, separators=(",", ":")).encode()
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentscore-commerce"
|
|
7
|
-
version = "1.3.
|
|
7
|
+
version = "1.3.3"
|
|
8
8
|
description = "Agent commerce SDK for Python — identity middleware (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI) + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for AgentScore-powered agent commerce."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -534,6 +534,220 @@ async def test_process_x402_settle_passes_typed_resource_config_unchanged():
|
|
|
534
534
|
assert captured["cfg"] is typed
|
|
535
535
|
|
|
536
536
|
|
|
537
|
+
@pytest.mark.asyncio
|
|
538
|
+
async def test_process_x402_settle_coerces_dict_payload_v2_to_typed_payment_payload():
|
|
539
|
+
"""Same fix as resource_config: ``verify_x402_request`` returns ``payload`` as a
|
|
540
|
+
plain dict (the result of ``json.loads(base64.b64decode(X-Payment))``); x402 2.9's
|
|
541
|
+
``server.verify_payment`` calls ``payload.get_scheme()`` and other typed-model methods
|
|
542
|
+
on it. Coerce dicts → ``PaymentPayload`` (or ``PaymentPayloadV1`` when ``x402Version=1``)
|
|
543
|
+
so the helper doesn't ``AttributeError`` at the verify leg.
|
|
544
|
+
"""
|
|
545
|
+
captured: dict = {}
|
|
546
|
+
|
|
547
|
+
class _CapturingServer:
|
|
548
|
+
def build_payment_requirements(self, _cfg: object, _ext: object = None) -> list:
|
|
549
|
+
return [{"id": "req1"}]
|
|
550
|
+
|
|
551
|
+
async def verify_payment(self, payload: object, _req: object) -> dict:
|
|
552
|
+
captured["verify_payload"] = payload
|
|
553
|
+
return {"is_valid": True}
|
|
554
|
+
|
|
555
|
+
async def settle_payment(self, payload: object, _req: object) -> dict:
|
|
556
|
+
captured["settle_payload"] = payload
|
|
557
|
+
return {"tx_hash": "0xabc"}
|
|
558
|
+
|
|
559
|
+
payload_dict = {
|
|
560
|
+
"x402Version": 2,
|
|
561
|
+
"accepted": {
|
|
562
|
+
"scheme": "exact",
|
|
563
|
+
"network": "eip155:8453",
|
|
564
|
+
"amount": "100000",
|
|
565
|
+
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
566
|
+
"payTo": "0x000000000000000000000000000000000000dEaD",
|
|
567
|
+
"maxTimeoutSeconds": 300,
|
|
568
|
+
},
|
|
569
|
+
"payload": {
|
|
570
|
+
"authorization": {
|
|
571
|
+
"from": "0xeb2Ca790F72787c7e61bC6c861353a1e4ACDFCa5",
|
|
572
|
+
"to": "0x000000000000000000000000000000000000dEaD",
|
|
573
|
+
"value": "100000",
|
|
574
|
+
"validAfter": 0,
|
|
575
|
+
"validBefore": 9999999999,
|
|
576
|
+
"nonce": "0x" + "aa" * 32,
|
|
577
|
+
},
|
|
578
|
+
"signature": "0x" + "cc" * 65,
|
|
579
|
+
},
|
|
580
|
+
}
|
|
581
|
+
res = await process_x402_settle(
|
|
582
|
+
ProcessX402SettleInput(
|
|
583
|
+
x402_server=_CapturingServer(),
|
|
584
|
+
payload=payload_dict,
|
|
585
|
+
resource_config={
|
|
586
|
+
"scheme": "exact",
|
|
587
|
+
"network": "eip155:8453",
|
|
588
|
+
"price": "$0.10",
|
|
589
|
+
"payTo": "0x" + "00" * 19 + "dE" + "aD",
|
|
590
|
+
"maxTimeoutSeconds": 300,
|
|
591
|
+
},
|
|
592
|
+
resource_meta=_RESOURCE_META,
|
|
593
|
+
)
|
|
594
|
+
)
|
|
595
|
+
assert isinstance(res, ProcessX402SettleSuccess)
|
|
596
|
+
# Both verify and settle legs received the typed Pydantic model, not the raw dict.
|
|
597
|
+
verify_payload = captured["verify_payload"]
|
|
598
|
+
settle_payload = captured["settle_payload"]
|
|
599
|
+
assert type(verify_payload).__name__ == "PaymentPayload"
|
|
600
|
+
assert type(settle_payload).__name__ == "PaymentPayload"
|
|
601
|
+
# The typed model exposes the get_scheme() method that x402's facilitator calls.
|
|
602
|
+
assert hasattr(verify_payload, "get_scheme")
|
|
603
|
+
assert verify_payload.get_scheme() == "exact"
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
@pytest.mark.asyncio
|
|
607
|
+
async def test_process_x402_settle_serializes_pydantic_settle_result_to_payment_response_header():
|
|
608
|
+
"""x402 2.9's ``settle_payment`` returns a Pydantic ``SettleResponse`` model;
|
|
609
|
+
plain ``json.dumps`` rejects it with ``TypeError``. The helper must call
|
|
610
|
+
``model_dump_json(by_alias=True)`` so the X-Payment-Response header stays
|
|
611
|
+
base64'd JSON with the right wire keys (``errorReason`` not ``error_reason``).
|
|
612
|
+
"""
|
|
613
|
+
from x402.schemas import SettleResponse
|
|
614
|
+
|
|
615
|
+
pydantic_settle = SettleResponse(
|
|
616
|
+
success=True,
|
|
617
|
+
transaction="0xabc",
|
|
618
|
+
network="eip155:8453",
|
|
619
|
+
payer="0x000000000000000000000000000000000000dEaD",
|
|
620
|
+
amount=None,
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
class _PydanticSettleServer:
|
|
624
|
+
def build_payment_requirements(self, _cfg: object, _ext: object = None) -> list:
|
|
625
|
+
return [{"id": "req1"}]
|
|
626
|
+
|
|
627
|
+
async def verify_payment(self, _payload: object, _req: object) -> dict:
|
|
628
|
+
return {"is_valid": True}
|
|
629
|
+
|
|
630
|
+
async def settle_payment(self, _payload: object, _req: object) -> SettleResponse:
|
|
631
|
+
return pydantic_settle
|
|
632
|
+
|
|
633
|
+
res = await process_x402_settle(
|
|
634
|
+
ProcessX402SettleInput(
|
|
635
|
+
x402_server=_PydanticSettleServer(),
|
|
636
|
+
payload={},
|
|
637
|
+
resource_config={
|
|
638
|
+
"scheme": "exact",
|
|
639
|
+
"network": "eip155:8453",
|
|
640
|
+
"price": "$0.10",
|
|
641
|
+
"payTo": "0x" + "00" * 19 + "dE" + "aD",
|
|
642
|
+
"maxTimeoutSeconds": 300,
|
|
643
|
+
},
|
|
644
|
+
resource_meta=_RESOURCE_META,
|
|
645
|
+
)
|
|
646
|
+
)
|
|
647
|
+
assert isinstance(res, ProcessX402SettleSuccess)
|
|
648
|
+
assert res.payment_response_header is not None
|
|
649
|
+
decoded = json.loads(base64.b64decode(res.payment_response_header).decode())
|
|
650
|
+
assert decoded["success"] is True
|
|
651
|
+
assert decoded["transaction"] == "0xabc"
|
|
652
|
+
assert decoded["network"] == "eip155:8453"
|
|
653
|
+
# by_alias=True: wire shape uses errorReason / errorMessage (camelCase) — not snake_case.
|
|
654
|
+
assert "errorReason" in decoded
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
@pytest.mark.asyncio
|
|
658
|
+
async def test_process_x402_settle_serializes_dict_settle_result_for_legacy_stubs():
|
|
659
|
+
"""Plain-dict settle results (from older x402 / test stubs) still serialize."""
|
|
660
|
+
|
|
661
|
+
class _DictSettleServer:
|
|
662
|
+
def build_payment_requirements(self, _cfg: object, _ext: object = None) -> list:
|
|
663
|
+
return [{"id": "req1"}]
|
|
664
|
+
|
|
665
|
+
async def verify_payment(self, _payload: object, _req: object) -> dict:
|
|
666
|
+
return {"is_valid": True}
|
|
667
|
+
|
|
668
|
+
async def settle_payment(self, _payload: object, _req: object) -> dict:
|
|
669
|
+
return {"success": True, "transaction": "0xdef"}
|
|
670
|
+
|
|
671
|
+
res = await process_x402_settle(
|
|
672
|
+
ProcessX402SettleInput(
|
|
673
|
+
x402_server=_DictSettleServer(),
|
|
674
|
+
payload={},
|
|
675
|
+
resource_config={
|
|
676
|
+
"scheme": "exact",
|
|
677
|
+
"network": "eip155:8453",
|
|
678
|
+
"price": "$0.10",
|
|
679
|
+
"payTo": "0x" + "00" * 19 + "dE" + "aD",
|
|
680
|
+
"maxTimeoutSeconds": 300,
|
|
681
|
+
},
|
|
682
|
+
resource_meta=_RESOURCE_META,
|
|
683
|
+
)
|
|
684
|
+
)
|
|
685
|
+
assert isinstance(res, ProcessX402SettleSuccess)
|
|
686
|
+
assert res.payment_response_header is not None
|
|
687
|
+
decoded = json.loads(base64.b64decode(res.payment_response_header).decode())
|
|
688
|
+
assert decoded == {"success": True, "transaction": "0xdef"}
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
@pytest.mark.asyncio
|
|
692
|
+
async def test_process_x402_settle_passes_typed_payment_payload_unchanged():
|
|
693
|
+
"""If the caller already provides a typed PaymentPayload, pass it through unchanged."""
|
|
694
|
+
from x402.schemas import PaymentPayload
|
|
695
|
+
|
|
696
|
+
typed = PaymentPayload.model_validate(
|
|
697
|
+
{
|
|
698
|
+
"x402Version": 2,
|
|
699
|
+
"accepted": {
|
|
700
|
+
"scheme": "exact",
|
|
701
|
+
"network": "eip155:8453",
|
|
702
|
+
"amount": "100000",
|
|
703
|
+
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
704
|
+
"payTo": "0x000000000000000000000000000000000000dEaD",
|
|
705
|
+
"maxTimeoutSeconds": 300,
|
|
706
|
+
},
|
|
707
|
+
"payload": {
|
|
708
|
+
"authorization": {
|
|
709
|
+
"from": "0xeb2Ca790F72787c7e61bC6c861353a1e4ACDFCa5",
|
|
710
|
+
"to": "0x000000000000000000000000000000000000dEaD",
|
|
711
|
+
"value": "100000",
|
|
712
|
+
"validAfter": 0,
|
|
713
|
+
"validBefore": 9999999999,
|
|
714
|
+
"nonce": "0x" + "aa" * 32,
|
|
715
|
+
},
|
|
716
|
+
"signature": "0x" + "cc" * 65,
|
|
717
|
+
},
|
|
718
|
+
}
|
|
719
|
+
)
|
|
720
|
+
captured: dict = {}
|
|
721
|
+
|
|
722
|
+
class _CapturingServer:
|
|
723
|
+
def build_payment_requirements(self, _cfg: object, _ext: object = None) -> list:
|
|
724
|
+
return [{"id": "req1"}]
|
|
725
|
+
|
|
726
|
+
async def verify_payment(self, payload: object, _req: object) -> dict:
|
|
727
|
+
captured["payload"] = payload
|
|
728
|
+
return {"is_valid": True}
|
|
729
|
+
|
|
730
|
+
async def settle_payment(self, _payload: object, _req: object) -> dict:
|
|
731
|
+
return {"tx_hash": "0xabc"}
|
|
732
|
+
|
|
733
|
+
res = await process_x402_settle(
|
|
734
|
+
ProcessX402SettleInput(
|
|
735
|
+
x402_server=_CapturingServer(),
|
|
736
|
+
payload=typed,
|
|
737
|
+
resource_config={
|
|
738
|
+
"scheme": "exact",
|
|
739
|
+
"network": "eip155:8453",
|
|
740
|
+
"price": "$0.10",
|
|
741
|
+
"payTo": "0x" + "00" * 19 + "dE" + "aD",
|
|
742
|
+
"maxTimeoutSeconds": 300,
|
|
743
|
+
},
|
|
744
|
+
resource_meta=_RESOURCE_META,
|
|
745
|
+
)
|
|
746
|
+
)
|
|
747
|
+
assert isinstance(res, ProcessX402SettleSuccess)
|
|
748
|
+
assert captured["payload"] is typed
|
|
749
|
+
|
|
750
|
+
|
|
537
751
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
538
752
|
# process_x402_settle: facilitator_error wrap
|
|
539
753
|
# ─────────────────────────────────────────────────────────────────────────────
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/ISSUE_TEMPLATE/bug_report.md
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/.github/ISSUE_TEMPLATE/feature_request.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/body.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/how_to_pay.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/identity.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/pricing.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/challenge/respond_402.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/__init__.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/bazaar.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/llms_txt.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/openapi.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/probe.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/robots_tag.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/discovery/skill_md.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/__init__.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/_denial.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/_response.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/address.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/aiohttp.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/cache.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/client.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/django.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/fastapi.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/flask.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/middleware.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/policy.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/py.typed
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/sanic.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/sessions.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/signer.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/identity/types.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/__init__.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/directive.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/dispatch.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/headers.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/idempotency.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/mppx_server.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/networks.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/rails.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/signer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/agentscore_commerce/payment/x402_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/per_product_policy_merchant.py
RENAMED
|
File without changes
|
{agentscore_commerce-1.3.2 → agentscore_commerce-1.3.3}/examples/stripe_multichain_merchant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|