agentscore-commerce 3.0.0__tar.gz → 3.2.0__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-3.0.0 → agentscore_commerce-3.2.0}/CLAUDE.md +11 -11
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/PKG-INFO +9 -9
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/README.md +7 -7
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/SECURITY.md +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/__init__.py +4 -8
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/_headers.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/_mppx_receipt.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/_redis.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/_warnings.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/api/__init__.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/agent_instructions.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/agent_memory.py +2 -7
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/body.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/how_to_pay.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/pricing.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/respond_402.py +5 -5
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/validation_error.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/checkout.py +46 -533
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/checkout_compute_first.py +9 -9
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/__init__.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/agentscore_content.py +6 -6
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/llms_txt.py +5 -20
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/openapi.py +4 -22
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/probe.py +6 -6
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/robots_tag.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/skill_md.py +8 -23
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/well_known.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/forwarded_proto.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/__init__.py +2 -49
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/_denial.py +11 -11
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/_response.py +39 -58
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/a2a.py +7 -51
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/address.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/aiohttp.py +11 -119
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/core.py +36 -102
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/default_denied.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/django.py +15 -153
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/fastapi.py +17 -147
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/flask.py +15 -165
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/middleware.py +13 -135
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/policy.py +13 -15
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/sanic.py +10 -114
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/sessions.py +7 -7
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/signer.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/types.py +11 -51
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/ucp.py +7 -7
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/ucp_jwks.py +15 -15
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/asgi.py +5 -5
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/__init__.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/amounts.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/compose_rails.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/default_rails.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/dispatch.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/headers.py +10 -10
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/idempotency.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/mppx_server.py +8 -8
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/network_kind.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/payment_header.py +7 -13
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/settlement_override.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/solana.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/wwwauthenticate.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/x402_server.py +14 -14
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/x402_settle.py +5 -5
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/x402_validation.py +6 -6
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/zero_settle.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/quote_cache.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/__init__.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/mppx_stripe.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/pay_to_address.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/pi_cache.py +9 -9
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/simulate_deposit.py +6 -6
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/README.md +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/compliance_merchant.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/compute_first_merchant.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/identity_only.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/multi_rail_merchant.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/per_product_policy_merchant.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/stripe_multichain_merchant.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/osv-scanner.toml +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/pyproject.toml +13 -9
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/scripts/regenerate_cross_lang_fixtures.py +12 -12
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_a2a.py +8 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_address.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_agent_memory_emitter.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_aiohttp.py +4 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_challenge.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout.py +10 -10
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_compute_first.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_compute_first_settle.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_signer_match.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_wallet_ofac_default.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_classify_orchestration_error.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_core.py +5 -5
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_default_read_only_on_denied.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_denial.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_django.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_extract_owner_scope.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_fastapi.py +8 -7
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_flask.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_gate_quota_info.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_get_signer_verdict.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_idempotency_helper.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_lifted_helpers.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_load_ucp_signing_key_from_env.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_middleware.py +7 -6
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_network_kind.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_pay_to_address.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_directive.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_header.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_servers.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_signer.py +5 -5
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_policy.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_quote_cache_redis.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_rail_spec.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_redis_internal.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_response.py +17 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_robots_tag.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_sanic.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_seamless_helpers.py +13 -13
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_signer_match.py +7 -44
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_signer_verdict_request_scoped.py +4 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_solana.py +3 -3
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_tokens.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_ucp.py +2 -2
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_ucp_jwks.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_verification_session.py +5 -4
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_zero_settle.py +1 -1
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/uv.lock +426 -254
- agentscore_commerce-3.0.0/agentscore_commerce/aip/__init__.py +0 -168
- agentscore_commerce-3.0.0/agentscore_commerce/aip/gate.py +0 -429
- agentscore_commerce-3.0.0/agentscore_commerce/aip/http_signature.py +0 -654
- agentscore_commerce-3.0.0/agentscore_commerce/aip/jwks.py +0 -448
- agentscore_commerce-3.0.0/agentscore_commerce/aip/request.py +0 -253
- agentscore_commerce-3.0.0/agentscore_commerce/aip/types.py +0 -268
- agentscore_commerce-3.0.0/agentscore_commerce/aip/verify.py +0 -408
- agentscore_commerce-3.0.0/tests/test_aip_adapters.py +0 -303
- agentscore_commerce-3.0.0/tests/test_aip_agent_memory.py +0 -36
- agentscore_commerce-3.0.0/tests/test_aip_checkout.py +0 -815
- agentscore_commerce-3.0.0/tests/test_aip_gate.py +0 -254
- agentscore_commerce-3.0.0/tests/test_aip_http_signature.py +0 -597
- agentscore_commerce-3.0.0/tests/test_aip_jwks.py +0 -499
- agentscore_commerce-3.0.0/tests/test_aip_presentation.py +0 -120
- agentscore_commerce-3.0.0/tests/test_aip_request.py +0 -129
- agentscore_commerce-3.0.0/tests/test_aip_types.py +0 -135
- agentscore_commerce-3.0.0/tests/test_aip_verify.py +0 -372
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/dependabot.yml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/workflows/ci.yml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/workflows/publish.yml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.github/workflows/security.yml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/.gitignore +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/CODE_OF_CONDUCT.md +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/CONTRIBUTING.md +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/LICENSE +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/__init__.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/accepted_methods.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/identity.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/receipt.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/checkout_hooks.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/bazaar.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/redemption_md.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/well_known_mpp.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/discovery/well_known_x402.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/errors.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/cache.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/py.typed +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/identity/tokens.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/__init__.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/_core.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/aiohttp.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/django.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/fastapi.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/flask.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/middleware/sanic.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/constants.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/directive.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/lazy.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/mppx_failures.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/networks.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/rail_spec.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/rails.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/signer.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/usdc.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/payment/x402.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/py.typed +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/payment_intent.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/stripe_multichain/simulate_dispatch.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/api_provider.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/examples/signed_ucp_merchant.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/lefthook.yml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/ruff.toml +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/__init__.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/conftest.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-agentscore-gate-blocked.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-agentscore-gate-full.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-capability.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-emoji-keys.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-es256-rails.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-extras-int.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-int-boundary.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-minimal.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-multikey.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/node-unicode.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-agentscore-gate-blocked.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-agentscore-gate-full.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-capability.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-emoji-keys.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-es256-rails.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-extras-int.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-int-boundary.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-minimal.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-multikey.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/fixtures/cross-lang/py-unicode.json +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_a2a_canonical.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_a2a_jws_roundtrip.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_amounts.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_api_reexport.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_cache.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_compute_first_adapters.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_compute_first_ofac.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_coverage_gaps.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_checkout_verification_bootstrap.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_compose_rails.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_coverage_fillers.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_credential_precheck.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_default_denied.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_default_rails.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_discovery.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_dispatch.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_forwarded_proto.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_integration.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_internal_helpers.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_mppx_failures.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_mppx_receipt.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_operator_handle.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_dispatch.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_handlers.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_headers.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_payment_misc.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_pricing.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_public_surface.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_quote_cache.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_rate_limit.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_sessions.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_simulate_dispatch.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_skill_md.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_solana_mpp_rail_spec_post_init.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_stripe_multichain.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_ucp_cross_lang.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_validation_error.py +0 -0
- {agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/tests/test_x402_settle_extra.py +0 -0
|
@@ -8,14 +8,14 @@ Every helper is extracted from a real consumer, not speculated.
|
|
|
8
8
|
|
|
9
9
|
| Submodule | What it is |
|
|
10
10
|
|---|---|
|
|
11
|
-
| `agentscore_commerce` (top-level) | `Checkout` orchestrator
|
|
11
|
+
| `agentscore_commerce` (top-level) | `Checkout` orchestrator: one config object + hooks (pre_validate, compute_pricing, mint_recipients, compose_mppx, on_settled, gate), auto-derived x402+pympp servers, per-framework adapters `handle_fastapi`/`handle_flask`/`handle_django`/`handle_aiohttp`/`handle_sanic`, signed UCP routes via `mount_ucp_routes_{fastapi,flask,django,aiohttp,sanic}`, optional `discovery_probe` config for x402-crawler auto-routing. Plus `compute_first_checkout`: variable-cost pay-per-result helper (compute-first + exact-x402). Scope is exact-mode rails only (x402-exact Base, tempo/charge, solana/charge, Stripe SPT); does NOT use x402-upto (Permit2) or Settlement-Overrides: variable cost is captured by running the work pre-settle and emitting a 402 at the exact computed price. `create_quote_cache`: content-hash quote cache used by the compute-first helper (in-memory by default; pass `redis_url` for distributed deployments). `create_result_cache`: the neutral primitive under it: keyed JSON-value cache with the same body-hash key builder; use it to cache any probe-leg result a Checkout-class merchant replays on the settle leg (e.g. a paid upstream call made in `pre_validate`). `create_default_on_denied`: canonical `on_denied(reason)` factory matching `Checkout`'s gate hook (handles `wallet_signer_mismatch`, `wallet_not_trusted` unfixable fallback, `payment_required`, `token_expired`/`invalid_credential`/`api_error`); merchants pass `merchant_name` + `support_email` and override `wallet_not_trusted_message` / `payment_required_message` / `support_context` for vendor-specific copy. `should_run_conditional_gate` / `requests_verification_session` / `has_identity_header` / `VERIFICATION_SESSION_HEADER`: the conditional-gate predicate (payment credential or an opt-in pre-payment verification-session request) and its parts; `build_identity_bootstrap`: the matching `identity_bootstrap` 402 block. `has_payment_header`: discriminator that splits discovery legs (no payment credential → 402) from settle legs (`payment-signature` / `x-payment` / `Authorization: Payment <jwt>`); `has_x402_header` / `has_mppx_header`: granular dispatch helpers (x402 vs MPP credential present) for routes that branch on rail. `malformed_payment_credential`: wire-shape gate for payment credentials (not base64 JSON, not token-shaped → reject); `Checkout` runs it before any merchant hook by default (`credential_pre_check=False` opts out). `default_read_only_on_denied(reason)`: canonical `on_denied` for read-only resource gates (`GET /orders/:id`): collapses every denial to 401 `unauthorized` + `Cache-Control: no-store` while still spreading `denial_reason_to_body` so `agent_instructions` / `verify_url` ride through. Returns a `DefaultOnDeniedResult(body, status, headers)` dataclass; FastAPI / Flask / aiohttp / Sanic `on_denied` callbacks accept an optional 3-tuple `(body, status, headers)`: convert with `lambda req, reason: (r := default_read_only_on_denied(reason), (r.body, r.status, r.headers or {}))[1]` or a named wrapper. Django + ASGI middleware adapters return Response objects directly; construct `JsonResponse(r.body, status=r.status, headers=r.headers)` / `JSONResponse(content=r.body, status_code=r.status, headers=r.headers)`. `extract_owner_scope(headers) -> OwnerScope`: pull canonical owner identity from `X-Wallet-Address` / `X-Operator-Token` with safe token hashing; pair with a wallet-or-token-scoped resource query so plaintext tokens never leave the request. Plus factories: `pricing_result` (cents → typed `PricingResult`), `validation_response_{fastapi,flask,django,aiohttp,sanic}` (4xx envelope per framework) |
|
|
12
12
|
| `agentscore_commerce.identity.{fastapi,flask,django,aiohttp,sanic,middleware}` | Trust gate middleware (KYC, age, sanctions on both account name and signer wallet, jurisdiction). Each adapter exports a conditional variant that wraps the gate so it fires only on settle legs (anonymous discovery flows through and gets a 402 with all rails): FastAPI / ASGI expose `ConditionalAgentScoreGate`, Django exposes `ConditionalAgentScoreMiddleware`, Flask + Sanic expose `conditional_agentscore_gate(app, ...)`, aiohttp exposes `conditional_agentscore_gate_middleware(...)`. Adapters export ONLY framework-specific surface (gate classes / fns, accessors, `capture_wallet`); shared helpers like `has_payment_header` / `denial_reason_to_body` import from their canonical home (`agentscore_commerce.payment` and `agentscore_commerce.identity` respectively). The Flask / Sanic `agentscore_gate(app, ...)` function accepts an optional `condition=` callable for inline gating (`AgentScoreGate.__init__` does not). |
|
|
13
13
|
| `agentscore_commerce.identity.policy` | Per-product compliance helpers: `PolicyBlock`, `build_gate_from_policy`, `run_gate_with_enforcement`, `shipping_country_allowed`, `shipping_state_allowed`, `validate_shipping_against_policy` (one-call country+state validator that raises `CheckoutValidationError` with the canonical envelope on miss) |
|
|
14
|
-
| `agentscore_commerce.payment` | Networks/USDC/rails registries, paymentauth.org directive builders, `create_x402_server` (wraps `x402[evm]>=2.9` + `cdp-sdk` for `facilitator="coinbase"`; install via the `coinbase` extra), `build_x402_accepts_for_402` (build the 402's `accepts[]` from the registered scheme; derives the right `extra.name` per network), `build_default_checkout_rails(tempo=, x402_base=, solana_mpp=, stripe=)` (canonical 4-rail `rails` dict factory: merchants pass per-rail overrides instead of redeclaring the recipient sentinel + network/chain_id/token boilerplate. When a caller flips `network` without pinning `token` / `chain_id`, the underlying dataclass derives them from the network: Base Sepolia → Sepolia USDC + chain_id 84532, Solana devnet → devnet USDC mint. Explicit overrides always win. Solana's `network` field accepts both CAIP-2 (`solana:5eykt4UsFv8…` / `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) AND the raw `@solana/mpp` form (`mainnet-beta` / `devnet` / `localnet`)), `build_mppx_compose_rails(amount_usd=, tempo_recipient=, solana_recipient=, ...)` (per-call intent factory replacing the hand-rolled `[("tempo/charge", {...}), ("solana/charge", {...}), ("stripe/charge", {...})]` list; auto-handles USD→atomic conversion for Solana; auto-drops the `stripe/charge` rail with a one-time `logging.warning` when `amount_usd < 0.50` since Stripe's fixed ~$0.30 fee makes sub-50-cent charges unprofitable
|
|
14
|
+
| `agentscore_commerce.payment` | Networks/USDC/rails registries, paymentauth.org directive builders, `create_x402_server` (wraps `x402[evm]>=2.9` + `cdp-sdk` for `facilitator="coinbase"`; install via the `coinbase` extra), `build_x402_accepts_for_402` (build the 402's `accepts[]` from the registered scheme; derives the right `extra.name` per network), `build_default_checkout_rails(tempo=, x402_base=, solana_mpp=, stripe=)` (canonical 4-rail `rails` dict factory: merchants pass per-rail overrides instead of redeclaring the recipient sentinel + network/chain_id/token boilerplate. When a caller flips `network` without pinning `token` / `chain_id`, the underlying dataclass derives them from the network: Base Sepolia → Sepolia USDC + chain_id 84532, Solana devnet → devnet USDC mint. Explicit overrides always win. Solana's `network` field accepts both CAIP-2 (`solana:5eykt4UsFv8…` / `solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1`) AND the raw `@solana/mpp` form (`mainnet-beta` / `devnet` / `localnet`)), `build_mppx_compose_rails(amount_usd=, tempo_recipient=, solana_recipient=, ...)` (per-call intent factory replacing the hand-rolled `[("tempo/charge", {...}), ("solana/charge", {...}), ("stripe/charge", {...})]` list; auto-handles USD→atomic conversion for Solana; auto-drops the `stripe/charge` rail with a one-time `logging.warning` when `amount_usd < 0.50` since Stripe's fixed ~$0.30 fee makes sub-50-cent charges unprofitable: many Stripe accounts also reject PI creation below the floor with `amount_too_small`; sub-50-cent APIs pass `include_stripe=False` explicitly to silence the warning), `process_x402_settle` (verify+settle in one call), `create_mppx_server` (wraps `pympp[server,tempo,stripe]>=0.6`), `is_evm_network`/`is_solana_network` (CAIP-2 discriminators that hide the `startswith("eip155:")` / `startswith("solana:")` prefix matching), `has_payment_header` (settle-leg vs discovery-leg discriminator), `parse_did_pkh_address` (parses ``did:pkh:<family>:<chain>:<addr>`` into a `PaymentSigner`), dispatch-by-network, signer extraction, WWW-Authenticate header, Settlement-Overrides header |
|
|
15
15
|
| `agentscore_commerce.discovery` | Discovery probe (`is_discovery_probe_request`, `build_discovery_probe_response`; the x402 sample envelope always carries a v2 `resource` and threads optional `extensions`, and `Checkout` fills both from its own `url`/`resource_info`/`discovery_extensions`, because envelope validators (mppx, x402scan's shared engine) hard-require `resource` and read the Bazaar example input to build valid probe bodies), Bazaar wrapper, `/.well-known/mpp.json`, `llms.txt` builder, `skill.md` builder (Claude-Skill-compatible agent-discovery manifest), `build_redemption_skill_md` (delivery-neutral; printed/emailed/API-trial codes all covered via `delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides), `build_merchant_index_json` + `standard_endpoint_descriptions(kind=)` (canonical `/` discovery body for goods or API merchants), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps`, OpenAPI snippets, `NoindexNonDiscoveryMiddleware` ASGI middleware. Plus the UCP/JWKS publish surface: `build_signed_ucp_response`, `build_signed_jwks_response`, `well_known_preflight_response`, `default_a2a_services`, `bootstrap_ucp_signing_key`, framework-neutral `SignedDiscoveryResponse` + per-framework wrappers `signed_response_{fastapi,flask,django,aiohttp,sanic}` |
|
|
16
16
|
| `agentscore_commerce.challenge` | 402-body builders: accepted_methods, identity_metadata (auto-attached by `Checkout` when wallet header present), how_to_pay, agent_instructions, build_402_body, pricing, agent_memory, `build_validation_error` (4xx body builder), `Receipt`/`ReceiptNextSteps`/`ProductInfo`/`ShippingAddress` (canonical 200-receipt dataclasses) |
|
|
17
|
-
| `agentscore_commerce.stripe_multichain` | Multichain PaymentIntent helper (`create_multichain_payment_intent` returns `MultichainPaymentIntentResult`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, static_recipients=, metadata=, order_id=, preferred_network=)
|
|
18
|
-
| `agentscore_commerce.api` | Re-exports `AgentScore` from `agentscore` SDK |
|
|
17
|
+
| `agentscore_commerce.stripe_multichain` | Multichain PaymentIntent helper (`create_multichain_payment_intent` returns `MultichainPaymentIntentResult`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, static_recipients=, metadata=, order_id=, preferred_network=)`: one-call per-order payTo resolver matching `Checkout.mint_recipients`: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check OR a `static_recipients` match: the static address is always-accepted because the merchant owns it); on the discovery leg, mints a fresh PI for the rails NOT covered by `static_recipients`, caches the merged map, registers static addresses with `pi_cache.cache_address`. `mint_multichain_recipients(...same kwargs) -> MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)`: structured variant returning the full per-rail map; prefer this when the merchant's `mint_recipients` hook needs all rail addresses (typical multi-rail merchant), and to avoid the "returned-string-is-ambiguous" trap on the settle leg when `static_recipients` is configured. Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where rotating per-PI Solana addresses can't absorb MPP spec §13.6's ~$0.50 ATA rent per call: the SDK skips Stripe minting on that network and reuses the static recipient forever; pair with a one-time external USDC pre-funding of the recipient's ATA and every settle pays only the per-tx fee. Testnet simulator (`simulate_crypto_deposit`, `simulate_deposit_if_test_mode`), `simulate_deposit_for_outcome(outcome=, deposit_address=, get_payment_intent_id=, stripe_secret_key=, stripe_version=)` (dispatches the simulator based on a Checkout / compute_first_checkout settle outcome; replaces the per-merchant rail-switch + thin `simulate_deposit_if_testnet(addr, network)` wrapper), `network_for_outcome` (outcome → simulator network arg, handles both Checkout-shaped `rail_key` and compute-first-shaped `mpp_method`, accepts bare scheme names AND `<scheme>/charge` forms), `create_pi_cache`, `create_mppx_stripe` |
|
|
18
|
+
| `agentscore_commerce.api` | Re-exports `AgentScore`, `AgentScoreError`, `AGENTSCORE_TEST_ADDRESSES` and `is_agentscore_test_address` from the `agentscore` SDK |
|
|
19
19
|
| `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI exposes `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export; Flask exposes `rate_limit_flask(app, ...)` (installs a `before_request` hook); Django exposes a class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`; aiohttp exposes `rate_limit_aiohttp(...)` middleware factory; Sanic exposes `rate_limit_sanic(app, ...)` installer; `asgi.RateLimitMiddleware` is a generic ASGI middleware that works with any starlette-compatible app. Shared options: `window_seconds` (default 60), `max_requests` (default 60), `key_resolver` (default first hop of `x-forwarded-for`), `redis_url` (lazy-imports `redis.asyncio` when set, in-memory `dict` fallback otherwise), `key_prefix`. `redis` is an optional peer dep (install via the `redis` extra). |
|
|
20
20
|
|
|
21
21
|
## Architecture
|
|
@@ -48,11 +48,11 @@ Peer-dep pattern: payment/x402/mppx/stripe modules import lazily at runtime; ven
|
|
|
48
48
|
| `compute_first_merchant.py` | Pay-per-result variable-cost merchant via the compute-first + exact-x402 helper (`compute_first_checkout`). Exact-mode rails only (x402-exact Base, tempo/charge, solana/charge, Stripe SPT); deliberately scoped out of x402-upto (Permit2) and Settlement-Overrides. Probe runs the work + caches by body content-hash; settle replays the cached result at the computed exact price. Pairs with `rate_limit_fastapi` since the probe leg runs work pre-payment. |
|
|
49
49
|
| `compliance_merchant.py` | Regulated-goods merchant: full compliance gate + custom `on_denied` composing the denial helpers (`verification_agent_instructions`, `is_fixable_denial`, `build_contact_support_next_steps`, `denial_reason_to_body`/`denial_reason_status`) |
|
|
50
50
|
| `per_product_policy_merchant.py` | Multi-product merchant where each row carries its own compliance policy. One product hard-gates KYC + age + state; another is anonymous; a third uses `enforcement="soft"` (request KYC but don't block sale). Demonstrates `PolicyBlock`, `build_gate_from_policy`, `run_gate_with_enforcement`, `shipping_country_allowed`, `shipping_state_allowed`. |
|
|
51
|
-
| `signed_ucp_merchant.py` | Signed UCP profile (`/.well-known/ucp`) + JWKS endpoint (`/.well-known/jwks.json`). AgentScore's `agentscore-profile+jws` is a vendor extension on top of UCP for trust-mode verifiers (regulated-commerce, AP2-aware) that opt into auditable cryptographic provenance
|
|
51
|
+
| `signed_ucp_merchant.py` | Signed UCP profile (`/.well-known/ucp`) + JWKS endpoint (`/.well-known/jwks.json`). AgentScore's `agentscore-profile+jws` is a vendor extension on top of UCP for trust-mode verifiers (regulated-commerce, AP2-aware) that opt into auditable cryptographic provenance: UCP §6 itself does NOT mandate signing; production UCP merchants commonly ship unsigned. Wires ephemeral-for-dev / env-JWK-for-prod signing, kid rotation, and `Cache-Control` posture. Uses `generate_ucp_signing_key`, `sign_ucp_profile`, `build_jwks_response`, `UCPSigningKey.from_jwk`, `UCPVerificationError`. Demonstrates the payment-handler builders (`mpp_payment_handler`, `x402_payment_handler`, `stripe_spt_payment_handler`: see "Payment-handler builders" below). |
|
|
52
52
|
|
|
53
53
|
## Payment-handler builders
|
|
54
54
|
|
|
55
|
-
The SDK ships protocol-rooted builders for the AgentScore-published payment handlers
|
|
55
|
+
The SDK ships protocol-rooted builders for the AgentScore-published payment handlers: vendors compose UCP `payment_handlers` blocks by spreading these helpers instead of hand-writing the verbose binding wrapper:
|
|
56
56
|
|
|
57
57
|
```python
|
|
58
58
|
from agentscore_commerce import StripeRailSpec, TempoRailSpec, X402BaseRailSpec
|
|
@@ -73,7 +73,7 @@ build_ucp_profile(
|
|
|
73
73
|
)
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
Each helper returns `{ <reverse-DNS-key>: [binding] }` so spreading composes the parent map. The handler `version`, spec URL, and schema URL are owned by the helpers (`agentscore_commerce/identity/ucp.py`)
|
|
76
|
+
Each helper returns `{ <reverse-DNS-key>: [binding] }` so spreading composes the parent map. The handler `version`, spec URL, and schema URL are owned by the helpers (`agentscore_commerce/identity/ucp.py`): bumping a handler spec version is a one-line change there. `mpp` takes `TempoRailSpec` / `SolanaMppRailSpec` instances and `x402` takes `X402BaseRailSpec`; each carries a `recipient`: a static address string, or `""` / a factory callable to signal per-order minting (e.g. Stripe-derived deposit addresses), in which case the authoritative recipient ships in the 402 body rather than the static UCP profile.
|
|
77
77
|
|
|
78
78
|
## Identity model
|
|
79
79
|
|
|
@@ -81,11 +81,11 @@ Two identity types: wallet (`X-Wallet-Address`) and operator-token (`X-Operator-
|
|
|
81
81
|
|
|
82
82
|
`DenialReason` codes (`missing_identity`, `identity_verification_required`, `token_expired`, `invalid_credential`, `wallet_signer_mismatch`, `wallet_auth_requires_wallet_signing`, `wallet_not_trusted`, `api_error`, `payment_required`) each carry a structured `agent_instructions` JSON block describing concrete recovery actions. See `agentscore_commerce/identity/_response.py` for the canned action copy.
|
|
83
83
|
|
|
84
|
-
`create_session_on_missing` auto-mints a verification session when no identity is present AND when `wallet_not_trusted` carries fixable reasons (`kyc_required` / `kyc_pending` / `kyc_failed`)
|
|
84
|
+
`create_session_on_missing` auto-mints a verification session when no identity is present AND when `wallet_not_trusted` carries fixable reasons (`kyc_required` / `kyc_pending` / `kyc_failed`): both paths rewrite the denial to `identity_verification_required` before reaching `on_denied`. When the merchant omits `create_session_on_missing` from `CheckoutGateConfig`, `Checkout` auto-defaults it from `gate.api_key` + `gate.base_url` + `gate.context` + `gate.merchant_name`. Merchants that need `on_before_session` side effects (e.g. pre-minting an order_id) supply their own config to override.
|
|
85
85
|
|
|
86
86
|
**Getting a verify_url before paying.** On an identity-gated `Checkout`, the gate runs only on a settle leg, so a buyer with no identity reached the session 403 only by sending a payment credential first (an SPT buyer had to mint one). The discovery 402 carries an `identity_bootstrap` block (`build_identity_bootstrap()`) naming `X-Verification-Session: create` whenever the request has no identity header (`has_identity_header`), and a request carrying that header, no identity and no payment credential runs the gate, which answers with the same session-bearing 403. It is opt-in on purpose: scanners replay the Bazaar example body on a schedule, so minting on every identity-less 402 would create a session and (on goods stores) a pending order per probe. Gateless merchants neither advertise nor honor it.
|
|
87
87
|
|
|
88
|
-
`build_verification_required_body(reason, message=?, agent_instructions=?, extra=?)
|
|
88
|
+
`build_verification_required_body(reason, message=?, agent_instructions=?, extra=?)`: canonical body builder for the `identity_verification_required` denial. Spreads `verify_url` / `session_id` / `poll_secret` / `poll_url` / `agent_instructions` from the gate-minted reason into a 4xx envelope with merchant-specific message + optional extras. Saves the per-merchant mapping boilerplate.
|
|
89
89
|
|
|
90
90
|
`get_signer_verdict(request)` (per-adapter) returns the cached `signer_match` + `signer_sanctions` verdicts the gate composed on its primary `/v1/assess` call (single round trip; merchants build a 403 with `build_signer_mismatch_body(result=verdict.signer_match)` when `kind != "pass"`).
|
|
91
91
|
|
|
@@ -103,11 +103,11 @@ Anything that is not a well-formed `oph_` string reads as absent rather than bei
|
|
|
103
103
|
|
|
104
104
|
Captured wallets: `capture_wallet(...)` is fire-and-forget. Reads `operator_token` stashed during gating and POSTs to `/v1/credentials/wallets`. No-ops for wallet-authenticated requests.
|
|
105
105
|
|
|
106
|
-
Wallet-signer-match + signer-sanctions: the gate adapter calls `extract_payment_signer(x402_header)` pre-evaluate and passes `signer={address, network}` to the SDK's `assess`. The API returns both `signer_match` (wallet-binding) and `signer_sanctions` (OFAC SDN wallet-address) on the same response; commerce caches the raw body alongside the projected verdicts so `get_signer_verdict` is a pure cache read. **Wallet-OFAC SDN enforcement on the `signer` block is unconditional** whenever a signer is present
|
|
106
|
+
Wallet-signer-match + signer-sanctions: the gate adapter calls `extract_payment_signer(x402_header)` pre-evaluate and passes `signer={address, network}` to the SDK's `assess`. The API returns both `signer_match` (wallet-binding) and `signer_sanctions` (OFAC SDN wallet-address) on the same response; commerce caches the raw body alongside the projected verdicts so `get_signer_verdict` is a pure cache read. **Wallet-OFAC SDN enforcement on the `signer` block is unconditional** whenever a signer is present: no `policy.require_sanctions_clear` opt-in required. An SDN hit (or `sanctions_check_unavailable`) flips `decision -> deny` and the gate returns 403 before the handler runs.
|
|
107
107
|
|
|
108
108
|
### Always-on wallet OFAC default for gateless merchants
|
|
109
109
|
|
|
110
|
-
Merchants who do NOT configure a `gate` on `Checkout` (or `compute_first_checkout`) still get wallet OFAC SDN enforcement on every settle leg. The SDK resolves the API key from `AGENTSCORE_API_KEY` (or `gate.api_key` if a partial gate config is present), reads `AGENTSCORE_BASE_URL` for staging/dev overrides, extracts the payment signer, and calls `/v1/assess` with the signer block. SDN hit → 403 with `wallet_not_trusted` + `decision_reasons: ["sanctions_flagged"]`. No key → one-time `[checkout]` / `[<name>.compute_first]` warning + skip (dev/testnet pattern; see `agentscore_commerce/_warnings.py`). Stripe SPT (no extractable wallet signer) skips silently
|
|
110
|
+
Merchants who do NOT configure a `gate` on `Checkout` (or `compute_first_checkout`) still get wallet OFAC SDN enforcement on every settle leg. The SDK resolves the API key from `AGENTSCORE_API_KEY` (or `gate.api_key` if a partial gate config is present), reads `AGENTSCORE_BASE_URL` for staging/dev overrides, extracts the payment signer, and calls `/v1/assess` with the signer block. SDN hit → 403 with `wallet_not_trusted` + `decision_reasons: ["sanctions_flagged"]`. No key → one-time `[checkout]` / `[<name>.compute_first]` warning + skip (dev/testnet pattern; see `agentscore_commerce/_warnings.py`). Stripe SPT (no extractable wallet signer) skips silently: Stripe runs its own OFAC screen at customer creation. `_has_identity_gate()` detects identity-bearing flags (`require_kyc`, `require_sanctions_clear`, `min_age`, `allowed/blocked_jurisdictions`); when none are set, the 402 body omits `agent_memory` and per-rail commands strip the `-H 'X-Operator-Token: ...'` flag (gateless merchants have no operator to identify). Gate configured WITHOUT `api_key` falls through to the wallet-OFAC-only path so the merchant gets the strict-liability floor instead of silently allowing.
|
|
111
111
|
|
|
112
112
|
### Fail-open (opt-in)
|
|
113
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentscore-commerce
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Agentic 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 agentic commerce.
|
|
5
5
|
Project-URL: Homepage, https://www.agentscore.com
|
|
6
6
|
Project-URL: Repository, https://github.com/agentscore/python-commerce
|
|
@@ -23,7 +23,7 @@ Classifier: Programming Language :: Python :: 3.14
|
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.15
|
|
24
24
|
Classifier: Topic :: Software Development :: Libraries
|
|
25
25
|
Requires-Python: >=3.11
|
|
26
|
-
Requires-Dist: agentscore-py>=2.
|
|
26
|
+
Requires-Dist: agentscore-py>=2.8.0
|
|
27
27
|
Requires-Dist: httpx<1.0.0,>=0.25.0
|
|
28
28
|
Provides-Extra: aiohttp
|
|
29
29
|
Requires-Dist: aiohttp>=3.8.0; extra == 'aiohttp'
|
|
@@ -77,7 +77,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
|
|
|
77
77
|
|
|
78
78
|
| Submodule | What it provides |
|
|
79
79
|
|---|---|
|
|
80
|
-
| `agentscore_commerce` (top-level) | `Checkout` orchestrator + `CheckoutContext` + `CheckoutGateConfig` + `CheckoutValidationError` + `DiscoveryProbeConfig` + `SettleOutcome` + `MppxComposeOutcome` + `PricingResult` (
|
|
80
|
+
| `agentscore_commerce` (top-level) | `Checkout` orchestrator + `CheckoutContext` + `CheckoutGateConfig` + `CheckoutValidationError` + `DiscoveryProbeConfig` + `SettleOutcome` + `MppxComposeOutcome` + `PricingResult` (one config object, hooks for pre_validate/compute_pricing/on_settled/mint_recipients/compose_mppx, auto-derived x402+mppx servers, per-framework adapters `handle_fastapi`/`handle_flask`/`handle_django`/`handle_aiohttp`/`handle_sanic`, signed UCP routes via `mount_ucp_routes_{fastapi,flask,django,aiohttp,sanic}`); `create_result_cache` (neutral keyed JSON-value cache with a stable body-hash key builder; cache a probe-leg result, e.g. a paid upstream call made in `pre_validate`, and replay it on the settle leg) plus the compute-first-shaped `create_quote_cache` built on it; `pricing_result` (factory: cents-denominated → typed `PricingResult` with embedded `PricingBlock`); `validation_response_{fastapi,flask,django,aiohttp,sanic}` (per-framework 4xx envelope wrappers); `make_mppx_compose_hook` (canonical pympp compose adapter). |
|
|
81
81
|
| `agentscore_commerce.identity.{fastapi,flask,django,aiohttp,sanic,middleware}` | Trust gate middleware: KYC, sanctions (account name + signer wallet), age, jurisdiction. `AgentScoreGate(...)` (or `agentscore_gate(app, ...)` on Flask/Sanic), `get_agentscore_data(...)`, `capture_wallet(...)`, `get_signer_verdict(...)`. The gate extracts the payment signer pre-evaluate and passes it to `/v1/assess`, so the API composes both wallet-binding (`signer_match`) and OFAC SDN wallet-address (`signer_sanctions`) verdicts on one round trip. |
|
|
82
82
|
| `agentscore_commerce.identity` (package level) | Re-exports the denial helpers: `denial_reason_status`, `denial_reason_to_body`, `build_signer_mismatch_body`, `build_contact_support_next_steps`, `verification_agent_instructions`, `is_fixable_denial`, `FIXABLE_DENIAL_REASONS`. The per-framework adapter modules also expose `get_gate_quota_info(request)` for surfacing X-RateLimit info from gate state. Also re-exports the per-product policy helpers: `PolicyBlock`, `GateResult`, `EnforcementMode`, `IdentityStatus`, `build_gate_from_policy`, `run_gate_with_enforcement`, `shipping_country_allowed`, `shipping_state_allowed`, `validate_shipping_against_policy` (one-call country+state validator that raises `CheckoutValidationError` with the canonical envelope on miss), for multi-product merchants where each product carries its own compliance config: hard gate vs soft vs none, per-product shipping allowlists. Key + token helpers: `load_ucp_signing_key_from_env` (cached env-driven loader for the UCP signing key, reads `UCP_SIGNING_KEY_JWK_PRIVATE` JSON JWK, detects alg from shape, falls back to ephemeral when unset, sanitizes errors so key bytes never reach logs, concurrent-safe via `threading.Lock`; env-var names and `default_kid` / `default_alg` are overridable as kwargs); `hash_operator_token` (sha256 hex of plaintext `opc_...`, for merchants persisting `operator_token_id` to their own DB without ever storing the plaintext); `extract_owner_scope(headers) -> OwnerScope` (canonical owner-identity extractor for caller-scoped resource queries, reads `X-Wallet-Address` / `X-Operator-Token`, hashes the token so plaintext never leaves the request); `default_read_only_on_denied(reason)` (canonical `on_denied` for read-only resource gates: 401 + `Cache-Control: no-store` while still spreading `denial_reason_to_body`. Returns a `DefaultOnDeniedResult(body, status, headers)`; FastAPI / Flask / aiohttp / Sanic `on_denied` callbacks accept an optional 3-tuple `(body, status, headers)` to carry headers through; wrap with `lambda req, reason: (lambda r: (r.body, r.status, r.headers or {}))(default_read_only_on_denied(reason))`). |
|
|
83
83
|
| `agentscore_commerce.payment` | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_directive`, `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (opt-in v1↔v2 amount-field shim that adds both `amount` and `maxAmountRequired` to an entry. The 402 builders do NOT apply it by default, strict x402 v2 settlement matches the agent's echoed requirement by exact comparison, so an extra field the server's rebuilt requirement lacks breaks settle; use only when you know a client is hardcoded to read `maxAmountRequired`), `settlement_override_header`, `dispatch_settlement_by_network`, `extract_payment_signer` (accepts positional `x402_payment_header` AND/OR `authorization_header=` kwarg; recovers signer from x402 EIP-3009 `payload.authorization.from` OR MPP `Authorization: Payment <base64>` `did:pkh:eip155:<chain>:<addr>` / `did:pkh:solana:<genesis>:<addr>` source DID), `detect_rail_from_headers` (returns `"x402"` / `"mpp"` / `None` from inbound headers), `register_x402_schemes_v1_v2`; drop-in x402 helpers: `validate_x402_network_config` (boot-time guard), `verify_x402_request` (parse + validate inbound X-Payment), `process_x402_settle` (verify-then-settle with one call), `classify_x402_settle_result` (maps the tagged settle result to a recommended HTTP status / code / next_steps so merchants get a controlled envelope without coupling to facilitator-specific error text), `classify_orchestration_error` (same `ClassifiedX402Error` shape but for uncaught exceptions thrown elsewhere in the orchestration; returns `None` for unknown errors so merchants rethrow instead of swallowing); `zero_amount_carve_out` (skip CDP / pympp upstream verify+settle for $0 settles where the upstream rejects value=0 payloads, including credentials signed against a nonzero quote the merchant re-priced to $0 at settle; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape), `malformed_payment_credential` (wire-shape gate: payment headers that are not base64 JSON and not token-shaped are rejected before merchant hooks run; `Checkout` applies it by default, opt out with `credential_pre_check=False`); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP, for Tempo / Solana / Base USDC amount construction). |
|
|
@@ -146,7 +146,7 @@ async def purchase(request: Request, assess=Depends(get_agentscore_data)):
|
|
|
146
146
|
return {"ok": True}
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
## Checkout orchestrator
|
|
149
|
+
## Checkout orchestrator
|
|
150
150
|
|
|
151
151
|
`Checkout` is the canonical merchant surface: one config object, hooks for the merchant-specific pieces, and the SDK handles 402 emit, identity gating, x402 verify+settle, mppx compose, $0 carve-out, and the per-framework adapter. Most merchants reach for `Checkout` first and drop to lower-level helpers only when they need custom flows.
|
|
152
152
|
|
|
@@ -368,12 +368,12 @@ from agentscore_commerce.identity import (
|
|
|
368
368
|
ucp_a2a_extension,
|
|
369
369
|
)
|
|
370
370
|
|
|
371
|
-
#
|
|
371
|
+
# A2A v1.0 Agent Card. Publish at /.well-known/agent-card.json.
|
|
372
372
|
# Per UCP §A2A binding the card MUST declare the canonical UCP extension URI in
|
|
373
373
|
# `capabilities.extensions[]`; pass `ucp_a2a_extension()` with empty capabilities
|
|
374
374
|
# until you bind formal UCP capabilities (dev.ucp.shopping.checkout, etc.).
|
|
375
|
-
# Skills are top-level AgentSkill objects
|
|
376
|
-
#
|
|
375
|
+
# Skills are top-level AgentSkill objects. The card is returned unsigned: to sign it,
|
|
376
|
+
# compute RFC 7515 JWS entries over the card body and pass them as `signatures`.
|
|
377
377
|
card = build_a2a_agent_card(
|
|
378
378
|
name="My Service",
|
|
379
379
|
description="Buy products via agent payments.",
|
|
@@ -393,7 +393,7 @@ card = build_a2a_agent_card(
|
|
|
393
393
|
# Google Universal Commerce Protocol. Publish at /.well-known/ucp.
|
|
394
394
|
# Output shape: {"ucp": {"version", "services", "capabilities",
|
|
395
395
|
# "payment_handlers", "name?", "supported_versions?"}, "keys": [...]}
|
|
396
|
-
#
|
|
396
|
+
# services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
|
|
397
397
|
# service / capability / handler name (UCP spec §3 + §6).
|
|
398
398
|
profile = build_ucp_profile(
|
|
399
399
|
name="My Service",
|
|
@@ -461,7 +461,7 @@ jwks = build_jwks_response([key.public_jwk])
|
|
|
461
461
|
|
|
462
462
|
**Key rotation.** Mint a new key with a new `kid`, add the public JWK to your JWKS endpoint alongside the old one, then sign new profiles with the new key. Set `Cache-Control: public, max-age=300` on `/.well-known/jwks.json` and wait at least that long after publishing the new key before removing the old JWK.
|
|
463
463
|
|
|
464
|
-
**Inline JWK in the profile vs separate JWKS endpoint.** UCP §6 mandates the separate `/.well-known/jwks.json` endpoint as the canonical trust source. The profile's `
|
|
464
|
+
**Inline JWK in the profile vs separate JWKS endpoint.** UCP §6 mandates the separate `/.well-known/jwks.json` endpoint as the canonical trust source. The profile's `keys[]` is informational; verifiers MUST resolve the kid against the JWKS to prevent a swap-after-sign attack.
|
|
465
465
|
|
|
466
466
|
ACP (Stripe + OpenAI Agentic Commerce Protocol) is a transactional checkout protocol with no identity-publishing surface; ACP merchants integrate via the existing `build_402_body` + `build_payment_headers` + Stripe SPT rail.
|
|
467
467
|
|
|
@@ -24,7 +24,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
|
|
|
24
24
|
|
|
25
25
|
| Submodule | What it provides |
|
|
26
26
|
|---|---|
|
|
27
|
-
| `agentscore_commerce` (top-level) | `Checkout` orchestrator + `CheckoutContext` + `CheckoutGateConfig` + `CheckoutValidationError` + `DiscoveryProbeConfig` + `SettleOutcome` + `MppxComposeOutcome` + `PricingResult` (
|
|
27
|
+
| `agentscore_commerce` (top-level) | `Checkout` orchestrator + `CheckoutContext` + `CheckoutGateConfig` + `CheckoutValidationError` + `DiscoveryProbeConfig` + `SettleOutcome` + `MppxComposeOutcome` + `PricingResult` (one config object, hooks for pre_validate/compute_pricing/on_settled/mint_recipients/compose_mppx, auto-derived x402+mppx servers, per-framework adapters `handle_fastapi`/`handle_flask`/`handle_django`/`handle_aiohttp`/`handle_sanic`, signed UCP routes via `mount_ucp_routes_{fastapi,flask,django,aiohttp,sanic}`); `create_result_cache` (neutral keyed JSON-value cache with a stable body-hash key builder; cache a probe-leg result, e.g. a paid upstream call made in `pre_validate`, and replay it on the settle leg) plus the compute-first-shaped `create_quote_cache` built on it; `pricing_result` (factory: cents-denominated → typed `PricingResult` with embedded `PricingBlock`); `validation_response_{fastapi,flask,django,aiohttp,sanic}` (per-framework 4xx envelope wrappers); `make_mppx_compose_hook` (canonical pympp compose adapter). |
|
|
28
28
|
| `agentscore_commerce.identity.{fastapi,flask,django,aiohttp,sanic,middleware}` | Trust gate middleware: KYC, sanctions (account name + signer wallet), age, jurisdiction. `AgentScoreGate(...)` (or `agentscore_gate(app, ...)` on Flask/Sanic), `get_agentscore_data(...)`, `capture_wallet(...)`, `get_signer_verdict(...)`. The gate extracts the payment signer pre-evaluate and passes it to `/v1/assess`, so the API composes both wallet-binding (`signer_match`) and OFAC SDN wallet-address (`signer_sanctions`) verdicts on one round trip. |
|
|
29
29
|
| `agentscore_commerce.identity` (package level) | Re-exports the denial helpers: `denial_reason_status`, `denial_reason_to_body`, `build_signer_mismatch_body`, `build_contact_support_next_steps`, `verification_agent_instructions`, `is_fixable_denial`, `FIXABLE_DENIAL_REASONS`. The per-framework adapter modules also expose `get_gate_quota_info(request)` for surfacing X-RateLimit info from gate state. Also re-exports the per-product policy helpers: `PolicyBlock`, `GateResult`, `EnforcementMode`, `IdentityStatus`, `build_gate_from_policy`, `run_gate_with_enforcement`, `shipping_country_allowed`, `shipping_state_allowed`, `validate_shipping_against_policy` (one-call country+state validator that raises `CheckoutValidationError` with the canonical envelope on miss), for multi-product merchants where each product carries its own compliance config: hard gate vs soft vs none, per-product shipping allowlists. Key + token helpers: `load_ucp_signing_key_from_env` (cached env-driven loader for the UCP signing key, reads `UCP_SIGNING_KEY_JWK_PRIVATE` JSON JWK, detects alg from shape, falls back to ephemeral when unset, sanitizes errors so key bytes never reach logs, concurrent-safe via `threading.Lock`; env-var names and `default_kid` / `default_alg` are overridable as kwargs); `hash_operator_token` (sha256 hex of plaintext `opc_...`, for merchants persisting `operator_token_id` to their own DB without ever storing the plaintext); `extract_owner_scope(headers) -> OwnerScope` (canonical owner-identity extractor for caller-scoped resource queries, reads `X-Wallet-Address` / `X-Operator-Token`, hashes the token so plaintext never leaves the request); `default_read_only_on_denied(reason)` (canonical `on_denied` for read-only resource gates: 401 + `Cache-Control: no-store` while still spreading `denial_reason_to_body`. Returns a `DefaultOnDeniedResult(body, status, headers)`; FastAPI / Flask / aiohttp / Sanic `on_denied` callbacks accept an optional 3-tuple `(body, status, headers)` to carry headers through; wrap with `lambda req, reason: (lambda r: (r.body, r.status, r.headers or {}))(default_read_only_on_denied(reason))`). |
|
|
30
30
|
| `agentscore_commerce.payment` | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_directive`, `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (opt-in v1↔v2 amount-field shim that adds both `amount` and `maxAmountRequired` to an entry. The 402 builders do NOT apply it by default, strict x402 v2 settlement matches the agent's echoed requirement by exact comparison, so an extra field the server's rebuilt requirement lacks breaks settle; use only when you know a client is hardcoded to read `maxAmountRequired`), `settlement_override_header`, `dispatch_settlement_by_network`, `extract_payment_signer` (accepts positional `x402_payment_header` AND/OR `authorization_header=` kwarg; recovers signer from x402 EIP-3009 `payload.authorization.from` OR MPP `Authorization: Payment <base64>` `did:pkh:eip155:<chain>:<addr>` / `did:pkh:solana:<genesis>:<addr>` source DID), `detect_rail_from_headers` (returns `"x402"` / `"mpp"` / `None` from inbound headers), `register_x402_schemes_v1_v2`; drop-in x402 helpers: `validate_x402_network_config` (boot-time guard), `verify_x402_request` (parse + validate inbound X-Payment), `process_x402_settle` (verify-then-settle with one call), `classify_x402_settle_result` (maps the tagged settle result to a recommended HTTP status / code / next_steps so merchants get a controlled envelope without coupling to facilitator-specific error text), `classify_orchestration_error` (same `ClassifiedX402Error` shape but for uncaught exceptions thrown elsewhere in the orchestration; returns `None` for unknown errors so merchants rethrow instead of swallowing); `zero_amount_carve_out` (skip CDP / pympp upstream verify+settle for $0 settles where the upstream rejects value=0 payloads, including credentials signed against a nonzero quote the merchant re-priced to $0 at settle; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape), `malformed_payment_credential` (wire-shape gate: payment headers that are not base64 JSON and not token-shaped are rejected before merchant hooks run; `Checkout` applies it by default, opt out with `credential_pre_check=False`); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP, for Tempo / Solana / Base USDC amount construction). |
|
|
@@ -93,7 +93,7 @@ async def purchase(request: Request, assess=Depends(get_agentscore_data)):
|
|
|
93
93
|
return {"ok": True}
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
## Checkout orchestrator
|
|
96
|
+
## Checkout orchestrator
|
|
97
97
|
|
|
98
98
|
`Checkout` is the canonical merchant surface: one config object, hooks for the merchant-specific pieces, and the SDK handles 402 emit, identity gating, x402 verify+settle, mppx compose, $0 carve-out, and the per-framework adapter. Most merchants reach for `Checkout` first and drop to lower-level helpers only when they need custom flows.
|
|
99
99
|
|
|
@@ -315,12 +315,12 @@ from agentscore_commerce.identity import (
|
|
|
315
315
|
ucp_a2a_extension,
|
|
316
316
|
)
|
|
317
317
|
|
|
318
|
-
#
|
|
318
|
+
# A2A v1.0 Agent Card. Publish at /.well-known/agent-card.json.
|
|
319
319
|
# Per UCP §A2A binding the card MUST declare the canonical UCP extension URI in
|
|
320
320
|
# `capabilities.extensions[]`; pass `ucp_a2a_extension()` with empty capabilities
|
|
321
321
|
# until you bind formal UCP capabilities (dev.ucp.shopping.checkout, etc.).
|
|
322
|
-
# Skills are top-level AgentSkill objects
|
|
323
|
-
#
|
|
322
|
+
# Skills are top-level AgentSkill objects. The card is returned unsigned: to sign it,
|
|
323
|
+
# compute RFC 7515 JWS entries over the card body and pass them as `signatures`.
|
|
324
324
|
card = build_a2a_agent_card(
|
|
325
325
|
name="My Service",
|
|
326
326
|
description="Buy products via agent payments.",
|
|
@@ -340,7 +340,7 @@ card = build_a2a_agent_card(
|
|
|
340
340
|
# Google Universal Commerce Protocol. Publish at /.well-known/ucp.
|
|
341
341
|
# Output shape: {"ucp": {"version", "services", "capabilities",
|
|
342
342
|
# "payment_handlers", "name?", "supported_versions?"}, "keys": [...]}
|
|
343
|
-
#
|
|
343
|
+
# services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
|
|
344
344
|
# service / capability / handler name (UCP spec §3 + §6).
|
|
345
345
|
profile = build_ucp_profile(
|
|
346
346
|
name="My Service",
|
|
@@ -408,7 +408,7 @@ jwks = build_jwks_response([key.public_jwk])
|
|
|
408
408
|
|
|
409
409
|
**Key rotation.** Mint a new key with a new `kid`, add the public JWK to your JWKS endpoint alongside the old one, then sign new profiles with the new key. Set `Cache-Control: public, max-age=300` on `/.well-known/jwks.json` and wait at least that long after publishing the new key before removing the old JWK.
|
|
410
410
|
|
|
411
|
-
**Inline JWK in the profile vs separate JWKS endpoint.** UCP §6 mandates the separate `/.well-known/jwks.json` endpoint as the canonical trust source. The profile's `
|
|
411
|
+
**Inline JWK in the profile vs separate JWKS endpoint.** UCP §6 mandates the separate `/.well-known/jwks.json` endpoint as the canonical trust source. The profile's `keys[]` is informational; verifiers MUST resolve the kid against the JWKS to prevent a swap-after-sign attack.
|
|
412
412
|
|
|
413
413
|
ACP (Stripe + OpenAI Agentic Commerce Protocol) is a transactional checkout protocol with no identity-publishing surface; ACP merchants integrate via the existing `build_402_body` + `build_payment_headers` + Stripe SPT rail.
|
|
414
414
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Agentic commerce SDK
|
|
1
|
+
"""Agentic commerce SDK: identity middleware + payment helpers + 402 builders + discovery + Stripe multichain.
|
|
2
2
|
|
|
3
3
|
Submodules:
|
|
4
4
|
agentscore_commerce.identity - per-framework gate adapters
|
|
@@ -25,7 +25,6 @@ from agentscore_commerce.checkout import (
|
|
|
25
25
|
MppxComposeOutcome,
|
|
26
26
|
PricingResult,
|
|
27
27
|
SettleOutcome,
|
|
28
|
-
build_aip_trusted_issuers,
|
|
29
28
|
format_pydantic_errors,
|
|
30
29
|
get_identity_status,
|
|
31
30
|
pricing_result,
|
|
@@ -60,7 +59,6 @@ from agentscore_commerce.identity import (
|
|
|
60
59
|
A2A_DEFAULT_TRANSPORT,
|
|
61
60
|
A2A_PROTOCOL_VERSION,
|
|
62
61
|
AGENTSCORE_UCP_CAPABILITY,
|
|
63
|
-
AIP_A2A_EXTENSION_URI,
|
|
64
62
|
FIXABLE_DENIAL_REASONS,
|
|
65
63
|
UCP_A2A_EXTENSION_URI,
|
|
66
64
|
A2AAgentCard,
|
|
@@ -93,7 +91,6 @@ from agentscore_commerce.identity import (
|
|
|
93
91
|
UCPSigningKey,
|
|
94
92
|
UCPVerificationError,
|
|
95
93
|
VerifyWalletSignerResult,
|
|
96
|
-
aip_a2a_extension,
|
|
97
94
|
build_a2a_agent_card,
|
|
98
95
|
build_agent_memory_hint,
|
|
99
96
|
build_contact_support_next_steps,
|
|
@@ -117,6 +114,7 @@ from agentscore_commerce.identity import (
|
|
|
117
114
|
shipping_state_allowed,
|
|
118
115
|
sign_ucp_profile,
|
|
119
116
|
stripe_spt_payment_handler,
|
|
117
|
+
to_security_requirements,
|
|
120
118
|
ucp_a2a_extension,
|
|
121
119
|
validate_shipping_against_policy,
|
|
122
120
|
verification_agent_instructions,
|
|
@@ -162,7 +160,7 @@ from agentscore_commerce.quote_cache import (
|
|
|
162
160
|
try:
|
|
163
161
|
__version__ = _pkg_version("agentscore-commerce")
|
|
164
162
|
except PackageNotFoundError:
|
|
165
|
-
# Editable install or pre-build state
|
|
163
|
+
# Editable install or pre-build state: fall back to a sentinel so consumers
|
|
166
164
|
# don't crash on a missing dist-info dir. Real version always comes from
|
|
167
165
|
# pyproject.toml at install time.
|
|
168
166
|
__version__ = "0.0.0+local"
|
|
@@ -171,7 +169,6 @@ __all__ = [
|
|
|
171
169
|
"A2A_DEFAULT_TRANSPORT",
|
|
172
170
|
"A2A_PROTOCOL_VERSION",
|
|
173
171
|
"AGENTSCORE_UCP_CAPABILITY",
|
|
174
|
-
"AIP_A2A_EXTENSION_URI",
|
|
175
172
|
"FIXABLE_DENIAL_REASONS",
|
|
176
173
|
"UCP_A2A_EXTENSION_URI",
|
|
177
174
|
"VERIFICATION_SESSION_HEADER",
|
|
@@ -242,10 +239,8 @@ __all__ = [
|
|
|
242
239
|
"WorkOutcome",
|
|
243
240
|
"X402BaseRailSpec",
|
|
244
241
|
"__version__",
|
|
245
|
-
"aip_a2a_extension",
|
|
246
242
|
"build_a2a_agent_card",
|
|
247
243
|
"build_agent_memory_hint",
|
|
248
|
-
"build_aip_trusted_issuers",
|
|
249
244
|
"build_contact_support_next_steps",
|
|
250
245
|
"build_default_checkout_rails",
|
|
251
246
|
"build_gate_from_policy",
|
|
@@ -290,6 +285,7 @@ __all__ = [
|
|
|
290
285
|
"should_run_conditional_gate",
|
|
291
286
|
"sign_ucp_profile",
|
|
292
287
|
"stripe_spt_payment_handler",
|
|
288
|
+
"to_security_requirements",
|
|
293
289
|
"ucp_a2a_extension",
|
|
294
290
|
"validate_shipping_against_policy",
|
|
295
291
|
"validation_envelope",
|
{agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/_mppx_receipt.py
RENAMED
|
@@ -19,11 +19,11 @@ def extract_mppx_receipt_header_from_raw(raw: Any) -> str | None:
|
|
|
19
19
|
|
|
20
20
|
Covers three shapes hand-rolled hooks commonly return:
|
|
21
21
|
|
|
22
|
-
* ``raw.receipt_header
|
|
23
|
-
* ``raw.to_payment_receipt()
|
|
22
|
+
* ``raw.receipt_header``: pympp's current direct-attribute shape.
|
|
23
|
+
* ``raw.to_payment_receipt()``: pympp's older Receipt return-method shape
|
|
24
24
|
(also reached when ``raw`` is a ``(credential, receipt)`` tuple OR a
|
|
25
25
|
dict/object carrying ``.receipt``).
|
|
26
|
-
* ``raw.with_receipt(response) -> Response
|
|
26
|
+
* ``raw.with_receipt(response) -> Response``: a shape that wraps an
|
|
27
27
|
outgoing Response and attaches the header.
|
|
28
28
|
|
|
29
29
|
Returns ``None`` when none match or the underlying call raises.
|
|
@@ -4,7 +4,7 @@ Replaces the hand-rolled lazy-init pattern in ``quote_cache``,
|
|
|
4
4
|
``stripe_multichain.pi_cache``, and ``middleware._core`` so they don't drift
|
|
5
5
|
on logging posture, TLS handling, or connect-error semantics.
|
|
6
6
|
|
|
7
|
-
``redis`` is an optional peer dep
|
|
7
|
+
``redis`` is an optional peer dep: callers pass ``redis_url`` (or rely on
|
|
8
8
|
``REDIS_URL`` env); when unset or the lazy import fails, this returns ``None``
|
|
9
9
|
and the caller falls back to its in-process dict.
|
|
10
10
|
|
|
@@ -80,7 +80,7 @@ def memoized_redis(*, url: str | None, label: str) -> Callable[[], Awaitable[Any
|
|
|
80
80
|
First call constructs the client; later calls return the same client
|
|
81
81
|
(or the same ``None``).
|
|
82
82
|
|
|
83
|
-
Pairs with the per-caller ``redis_url`` opt
|
|
83
|
+
Pairs with the per-caller ``redis_url`` opt: when ``url`` is ``None`` AND
|
|
84
84
|
``REDIS_URL`` is unset, the getter resolves to ``None`` once and remains so
|
|
85
85
|
for the lifetime of the caller.
|
|
86
86
|
"""
|
|
@@ -28,7 +28,7 @@ def warn_missing_api_key_once(label: str) -> None:
|
|
|
28
28
|
return
|
|
29
29
|
_warned_no_api_key = True
|
|
30
30
|
logging.getLogger(__name__).warning(
|
|
31
|
-
f"[{label}] AGENTSCORE_API_KEY is not set
|
|
31
|
+
f"[{label}] AGENTSCORE_API_KEY is not set: wallet OFAC SDN sanctions are NOT being enforced. "
|
|
32
32
|
"Set the env var to enable strict-liability protection on settle."
|
|
33
33
|
)
|
|
34
34
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""AgentScore SDK re-export
|
|
1
|
+
"""AgentScore SDK re-export: single import path for the underlying agentscore-py.
|
|
2
2
|
|
|
3
3
|
Vendors install only ``agentscore-commerce`` and reach everything from the underlying
|
|
4
4
|
``agentscore-py`` here. Don't add ``agentscore-py`` as a separate dep; the two can
|
|
@@ -58,7 +58,7 @@ _RAIL_CLIENTS: dict[str, list[str]] = {
|
|
|
58
58
|
def compatible_clients_by_rails(rails: Iterable[str]) -> dict[str, list[str]] | None:
|
|
59
59
|
"""Smoke-verified client list for a set of rail keys.
|
|
60
60
|
|
|
61
|
-
The single source of truth for "which CLIs we've verified end-to-end on each rail"
|
|
61
|
+
The single source of truth for "which CLIs we've verified end-to-end on each rail":
|
|
62
62
|
consumed both by the 402-body builder (``build_agent_instructions``) and by discovery
|
|
63
63
|
surfaces (skill.md, llms.txt, etc.). Update here, every surface inherits.
|
|
64
64
|
"""
|
|
@@ -101,13 +101,13 @@ def build_agent_instructions(
|
|
|
101
101
|
extra_warnings: list[str] | None = None,
|
|
102
102
|
recommended: str | None = None,
|
|
103
103
|
# Per-rail list of client names the merchant has verified work end-to-end.
|
|
104
|
-
# Vendors set this from their own smoke matrix
|
|
104
|
+
# Vendors set this from their own smoke matrix: defaults to None, in which case
|
|
105
105
|
# the field is not emitted (avoids vouching for clients the merchant has not tested).
|
|
106
106
|
# Keys are rail identifiers (e.g. "x402_base", "tempo_mpp"); values are display labels.
|
|
107
107
|
compatible_clients: dict[str, list[str]] | None = None,
|
|
108
108
|
extra: dict[str, Any] | None = None,
|
|
109
109
|
) -> dict[str, Any]:
|
|
110
|
-
"""Build the agent_instructions block
|
|
110
|
+
"""Build the agent_instructions block: combines how_to_pay with tools, warnings, compat note, timeout.
|
|
111
111
|
|
|
112
112
|
Defaults adapt to the rails declared in ``how_to_pay``: only tempo-relevant warnings/tools
|
|
113
113
|
appear if ``how_to_pay["tempo"]`` is set, only x402-relevant ones if ``x402_base``/
|
|
@@ -10,7 +10,7 @@ Usage pattern:
|
|
|
10
10
|
- Merchant tracks per-operator (or per-IP / per-fingerprint) "have I seen this agent
|
|
11
11
|
before?" in their own DB
|
|
12
12
|
- On first encounter, include the hint so the agent saves the pattern
|
|
13
|
-
- On subsequent encounters, skip
|
|
13
|
+
- On subsequent encounters, skip: the agent already has it (or never will)
|
|
14
14
|
|
|
15
15
|
The hint contents come from :func:`build_agent_memory_hint` (re-exported here for
|
|
16
16
|
convenience). Keep it stateless: AgentScore's pattern doesn't depend on the merchant's
|
|
@@ -24,7 +24,6 @@ from agentscore_commerce.identity.types import AgentMemoryHint, build_agent_memo
|
|
|
24
24
|
|
|
25
25
|
def first_encounter_agent_memory(
|
|
26
26
|
first_encounter: bool,
|
|
27
|
-
aip_trusted_issuers: list[str] | None = None,
|
|
28
27
|
) -> AgentMemoryHint | None:
|
|
29
28
|
"""Return the ``agent_memory`` hint when this is a first encounter, otherwise ``None``.
|
|
30
29
|
|
|
@@ -39,17 +38,13 @@ def first_encounter_agent_memory(
|
|
|
39
38
|
),
|
|
40
39
|
)
|
|
41
40
|
|
|
42
|
-
When the merchant accepts AIP, pass ``aip_trusted_issuers`` (AgentScore's canonical
|
|
43
|
-
issuer plus any externals) so the hint advertises the ``agent_identity`` path. Omit for
|
|
44
|
-
non-AIP merchants.
|
|
45
|
-
|
|
46
41
|
Returning ``None`` means ``build_402_body`` cleanly skips the field instead of
|
|
47
42
|
emitting ``agent_memory: null`` (which would imply "I tried but failed" rather than
|
|
48
43
|
"didn't apply").
|
|
49
44
|
"""
|
|
50
45
|
if not first_encounter:
|
|
51
46
|
return None
|
|
52
|
-
return build_agent_memory_hint(
|
|
47
|
+
return build_agent_memory_hint()
|
|
53
48
|
|
|
54
49
|
|
|
55
50
|
__all__ = ["AgentMemoryHint", "build_agent_memory_hint", "first_encounter_agent_memory"]
|
{agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/how_to_pay.py
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""how_to_pay block builder
|
|
1
|
+
"""how_to_pay block builder: per-rail setup/command/what_it_does for 402 agent_instructions."""
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
4
|
from typing import Any
|
|
@@ -43,7 +43,7 @@ async def build_how_to_pay(
|
|
|
43
43
|
retry_body_json: str,
|
|
44
44
|
total_usd: float | str,
|
|
45
45
|
rails: dict[str, TempoRailSpec | X402BaseRailSpec | SolanaMppRailSpec | StripeRailSpec],
|
|
46
|
-
op_token_placeholder: str | None = "<your_opc_token>", # noqa: S107
|
|
46
|
+
op_token_placeholder: str | None = "<your_opc_token>", # noqa: S107 # literal placeholder, not a secret
|
|
47
47
|
max_spend: float | str | None = None,
|
|
48
48
|
decimals: int = 2,
|
|
49
49
|
) -> dict[str, Any]:
|
|
@@ -65,7 +65,7 @@ async def build_how_to_pay(
|
|
|
65
65
|
|
|
66
66
|
``op_token_placeholder`` defaults to ``"<your_opc_token>"``. Pass ``None``
|
|
67
67
|
(gateless merchants) to strip the ``-H 'X-Operator-Token: ...'`` snippet
|
|
68
|
-
from every rail command
|
|
68
|
+
from every rail command: appropriate when the merchant doesn't run an
|
|
69
69
|
identity gate. The always-on wallet OFAC SDN default does NOT need an
|
|
70
70
|
operator token, so gateless merchants emit cleaner commands.
|
|
71
71
|
"""
|
{agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/pricing.py
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Pricing block builder + canonical type.
|
|
2
2
|
|
|
3
3
|
Composes cents-denominated price components into the dollar-string shape that 402
|
|
4
|
-
challenge bodies advertise. Standardizes the pricing block so every merchant
|
|
5
|
-
current and future commerce-platform plugins (Commerce7, WooCommerce, Shopify)
|
|
4
|
+
challenge bodies advertise. Standardizes the pricing block so every merchant:
|
|
5
|
+
current and future commerce-platform plugins (Commerce7, WooCommerce, Shopify):
|
|
6
6
|
surfaces the same shape to agents.
|
|
7
7
|
|
|
8
8
|
Shipping is included by default because most physical-goods merchants carry it; pass
|
{agentscore_commerce-3.0.0 → agentscore_commerce-3.2.0}/agentscore_commerce/challenge/respond_402.py
RENAMED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"""``respond_402
|
|
1
|
+
"""``respond_402``: single-call 402 emit for merchants who use both pympp + x402.
|
|
2
2
|
|
|
3
3
|
Pympp handles tempo + stripe MPP rails; x402 handles Base + Solana.
|
|
4
4
|
|
|
5
5
|
The seam is fiddly enough to get wrong by hand:
|
|
6
6
|
|
|
7
7
|
- pympp's compose returns a 402 response with WWW-Authenticate directives whose ids
|
|
8
|
-
pympp's server-side validator REMEMBERS
|
|
8
|
+
pympp's server-side validator REMEMBERS: they round-trip in client credentials.
|
|
9
9
|
Overwriting that header (e.g. with a freshly-built directive) breaks the round-trip.
|
|
10
10
|
- x402 needs the binary-friendly ``PAYMENT-REQUIRED`` header (base64-encoded JSON of
|
|
11
|
-
``{x402Version, accepts, resource}``)
|
|
11
|
+
``{x402Version, accepts, resource}``): pympp doesn't emit it.
|
|
12
12
|
- Merchants want a richer JSON body (pricing, identity metadata, agent_instructions,
|
|
13
13
|
agent_memory, retry_body, accepted_methods cross-reference) than the bare pympp body.
|
|
14
14
|
|
|
@@ -35,7 +35,7 @@ from agentscore_commerce.payment.wwwauthenticate import payment_required_header
|
|
|
35
35
|
|
|
36
36
|
@dataclass
|
|
37
37
|
class Respond402Result:
|
|
38
|
-
"""Framework-neutral 402 response shape
|
|
38
|
+
"""Framework-neutral 402 response shape: body + headers + status."""
|
|
39
39
|
|
|
40
40
|
body: dict[str, object]
|
|
41
41
|
headers: dict[str, str]
|
|
@@ -55,7 +55,7 @@ def respond_402(
|
|
|
55
55
|
|
|
56
56
|
``body`` is the already-built dict from :func:`build_402_body`. ``x402``, when
|
|
57
57
|
set, carries the PAYMENT-REQUIRED header inputs (``x402_version``, ``accepts``,
|
|
58
|
-
``resource``); omit for merchants that don't accept x402 (Base / Solana)
|
|
58
|
+
``resource``); omit for merchants that don't accept x402 (Base / Solana): pympp-only
|
|
59
59
|
setups.
|
|
60
60
|
"""
|
|
61
61
|
headers = {k.lower(): v for k, v in mppx_challenge_headers.items()}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Pairs cleanly with the existing 402 / 403 builders. Every commerce merchant
|
|
4
4
|
returning helpful ``bad_request`` / ``not_found`` / ``out_of_stock`` errors
|
|
5
5
|
converges on the same shape: ``{error: {code, message}, ...optional_hints,
|
|
6
|
-
next_steps?}``. This builder doesn't choose the HTTP status
|
|
6
|
+
next_steps?}``. This builder doesn't choose the HTTP status: vendors wrap the
|
|
7
7
|
returned body in their framework's response (``JSONResponse(body, 400)`` in
|
|
8
8
|
FastAPI, etc.). Status stays the merchant's call because the same shape works
|
|
9
9
|
for 400/404/409/422.
|