agentscore-commerce 2.1.1__tar.gz → 2.3.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-2.1.1 → agentscore_commerce-2.3.0}/.github/workflows/security.yml +8 -2
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/CLAUDE.md +6 -2
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/PKG-INFO +2 -2
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/README.md +1 -1
- agentscore_commerce-2.3.0/agentscore_commerce/_warnings.py +39 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/how_to_pay.py +17 -6
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout.py +165 -7
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout_compute_first.py +93 -1
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/core.py +4 -4
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/middleware.py +4 -4
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/types.py +4 -4
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/rail_spec.py +24 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/__init__.py +4 -0
- agentscore_commerce-2.3.0/agentscore_commerce/stripe_multichain/pay_to_address.py +280 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/README.md +1 -1
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/multi_rail_merchant.py +26 -13
- agentscore_commerce-2.3.0/osv-scanner.toml +25 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/pyproject.toml +1 -1
- agentscore_commerce-2.3.0/tests/test_checkout_compute_first_ofac.py +207 -0
- agentscore_commerce-2.3.0/tests/test_checkout_wallet_ofac_default.py +259 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_pay_to_address.py +120 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/uv.lock +156 -179
- agentscore_commerce-2.1.1/agentscore_commerce/stripe_multichain/pay_to_address.py +0 -127
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/dependabot.yml +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/workflows/ci.yml +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.github/workflows/publish.yml +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/.gitignore +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/CODE_OF_CONDUCT.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/CONTRIBUTING.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/LICENSE +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/SECURITY.md +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/_headers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/_mppx_receipt.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/_redis.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/api/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/accepted_methods.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/agent_instructions.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/agent_memory.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/body.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/identity.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/pricing.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/receipt.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/respond_402.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/validation_error.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/checkout_hooks.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/agentscore_content.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/bazaar.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/llms_txt.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/openapi.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/probe.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/redemption_md.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/robots_tag.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/skill_md.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known_mpp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/discovery/well_known_x402.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/errors.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/_denial.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/_response.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/a2a.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/address.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/aiohttp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/cache.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/default_denied.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/django.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/fastapi.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/flask.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/policy.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/py.typed +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/sanic.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/sessions.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/signer.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/tokens.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/ucp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/ucp_jwks.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/_core.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/aiohttp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/asgi.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/django.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/fastapi.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/flask.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/middleware/sanic.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/amounts.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/compose_rails.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/constants.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/default_rails.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/directive.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/dispatch.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/headers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/idempotency.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/lazy.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/mppx_failures.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/mppx_server.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/network_kind.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/networks.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/payment_header.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/rails.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/settlement_override.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/signer.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/solana.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/usdc.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/wwwauthenticate.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_server.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_settle.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/x402_validation.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/payment/zero_settle.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/quote_cache.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/mppx_stripe.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/payment_intent.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/pi_cache.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/simulate_deposit.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/stripe_multichain/simulate_dispatch.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/api_provider.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/compliance_merchant.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/compute_first_merchant.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/identity_only.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/per_product_policy_merchant.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/signed_ucp_merchant.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/examples/stripe_multichain_merchant.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/lefthook.yml +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/ruff.toml +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/scripts/regenerate_cross_lang_fixtures.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/__init__.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/conftest.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-agentscore-gate-blocked.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-agentscore-gate-full.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-capability.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-emoji-keys.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-es256-rails.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-extras-int.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-int-boundary.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-minimal.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-multikey.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/node-unicode.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-agentscore-gate-blocked.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-agentscore-gate-full.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-capability.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-emoji-keys.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-es256-rails.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-extras-int.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-int-boundary.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-minimal.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-multikey.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/fixtures/cross-lang/py-unicode.json +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_a2a.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_a2a_jws_roundtrip.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_address.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_agent_memory_emitter.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_aiohttp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_amounts.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_api_reexport.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_cache.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_challenge.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_checkout.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first_adapters.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_checkout_compute_first_settle.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_checkout_coverage_gaps.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_classify_orchestration_error.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_compose_rails.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_core.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_coverage_fillers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_default_denied.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_default_rails.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_default_read_only_on_denied.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_denial.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_discovery.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_dispatch.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_django.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_extract_owner_scope.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_fastapi.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_flask.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_gate_quota_info.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_get_signer_verdict.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_idempotency_helper.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_integration.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_internal_helpers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_lifted_helpers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_load_ucp_signing_key_from_env.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_middleware.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_mppx_failures.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_mppx_receipt.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_network_kind.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_directive.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_dispatch.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_handlers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_header.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_headers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_misc.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_servers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_payment_signer.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_policy.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_pricing.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_public_surface.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_quote_cache.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_quote_cache_redis.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_rail_spec.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_rate_limit.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_redis_internal.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_response.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_robots_tag.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_sanic.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_seamless_helpers.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_sessions.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_signer_match.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_simulate_dispatch.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_skill_md.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_solana.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_solana_mpp_rail_spec_post_init.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_stripe_multichain.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_tokens.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_ucp.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_ucp_cross_lang.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_ucp_jwks.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_validation_error.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_x402_settle_extra.py +0 -0
- {agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/tests/test_zero_settle.py +0 -0
|
@@ -27,7 +27,10 @@ jobs:
|
|
|
27
27
|
chmod +x osv-scanner
|
|
28
28
|
|
|
29
29
|
- name: Scan dependencies
|
|
30
|
-
|
|
30
|
+
# `--config osv-scanner.toml` honors the in-repo allowlist (e.g. PYSEC-2025-183;
|
|
31
|
+
# see the toml for justifications). Keep the pip-audit `--ignore-vuln` flags below
|
|
32
|
+
# in sync with the same IDs.
|
|
33
|
+
run: ./osv-scanner scan source --lockfile=uv.lock --config=osv-scanner.toml --format=table
|
|
31
34
|
|
|
32
35
|
pip-audit:
|
|
33
36
|
name: Python Audit
|
|
@@ -44,6 +47,9 @@ jobs:
|
|
|
44
47
|
run: pip install pip-audit
|
|
45
48
|
|
|
46
49
|
- name: Audit dependencies
|
|
50
|
+
# `--ignore-vuln` allowlist mirrors `osv-scanner.toml` (e.g. PYSEC-2025-183;
|
|
51
|
+
# see that file for justifications). Keep both lists in sync.
|
|
47
52
|
run: |
|
|
48
53
|
uv export --format requirements-txt --no-hashes > requirements.txt
|
|
49
|
-
pip-audit -r requirements.txt --disable-pip --no-deps
|
|
54
|
+
pip-audit -r requirements.txt --disable-pip --no-deps \
|
|
55
|
+
--ignore-vuln PYSEC-2025-183
|
|
@@ -14,7 +14,7 @@ Every helper is extracted from a real consumer, not speculated.
|
|
|
14
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`), 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=, 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); on the discovery leg, mints a fresh PI
|
|
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. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only — solana method registration through `create_mppx_server` is a follow-up; merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). 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
18
|
| `agentscore_commerce.api` | Re-exports `AgentScore` from `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
|
|
|
@@ -88,7 +88,11 @@ Two identity types: wallet (`X-Wallet-Address`) and operator-token (`X-Operator-
|
|
|
88
88
|
|
|
89
89
|
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.
|
|
90
90
|
|
|
91
|
-
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.
|
|
91
|
+
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.
|
|
92
|
+
|
|
93
|
+
### Always-on wallet OFAC default for gateless merchants
|
|
94
|
+
|
|
95
|
+
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.
|
|
92
96
|
|
|
93
97
|
### Fail-open (opt-in)
|
|
94
98
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentscore-commerce
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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
|
|
@@ -78,7 +78,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
|
|
|
78
78
|
| `agentscore_commerce.payment` | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_directive`, `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (v1↔v2 amount field shim that emits both `amount` and `maxAmountRequired` so v1-only x402 parsers like Coinbase awal can read v2 bodies), `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; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP — for Tempo / Solana / Base USDC amount construction). |
|
|
79
79
|
| `agentscore_commerce.discovery` | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers like `awal x402 details`), `sample_x402_accept_for_network` (USDC sample-accept builder for known CAIP-2 networks), `build_well_known_mpp`, `build_llms_txt` + `llms_txt_identity_section` + `llms_txt_payment_section` (compact + verbose modes), `build_skill_md` (Claude-Skill-compatible `/skill.md` agent-discovery manifest; strictly agent-facing data only, no internal posture), `build_redemption_skill_md` (delivery-neutral redemption-code template — printed mailers, emailed codes, API trial credits all covered; `endpoint_path`/`delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides for non-goods shapes), `build_merchant_index_json` (canonical `/` discovery body), `standard_endpoint_descriptions(kind=)` (canonical method+path → description map for goods vs api merchants; optional `include_order_status_route` for goods), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps` (canonical skill.md onboarding for goods or API merchants), `agentscore_openapi_snippets`, `build_bazaar_discovery_payload`, `NoindexNonDiscoveryMiddleware` (ASGI middleware emitting `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for non-ASGI frameworks). 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}`. |
|
|
80
80
|
| `agentscore_commerce.challenge` | `build_402_body`, `build_accepted_methods`, `build_identity_metadata` (auto-attached by `Checkout` when an inbound `X-Wallet-Address` header is present), `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients`: smoke-verified CLIs the agent should use; vendor override supported; pure helper `compatible_clients_by_rails(rails)` returns the same map for vendors building custom 402s), `build_pricing_block` (cents to dollar-string with optional shipping/tax), `first_encounter_agent_memory` (cross-merchant hint, returns the canonical block or `None` based on a per-merchant first-seen flag), `Receipt` + `ReceiptNextSteps` + `ProductInfo` + `ShippingAddress` (canonical 200-receipt dataclasses — universal across goods + API merchants); `respond_402`, a drop-in 402 emit that preserves pympp's `WWW-Authenticate` and layers x402's `PAYMENT-REQUIRED`. `build_validation_error`: structured 4xx body builder (`{error: {code, message}, required_fields?, example_body?, next_steps?, ...extra}`) so vendors compose body shapes by name instead of inlining at every validation site. |
|
|
81
|
-
| `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check); on the discovery leg, mints a fresh PI
|
|
81
|
+
| `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; 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=)` — per-order payTo resolver: 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` so verify-leg lookups pass. `mint_multichain_recipients(...same kwargs)` — structured variant returning `MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)` for the full per-rail map (typical multi-rail merchant hook). Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where Solana per-call ATA rent (~$0.50 against MPP spec §13.6) dominates revenue — the SDK skips Stripe minting on that network, reuses the static recipient forever, and (with a one-time external pre-funding of the recipient's USDC ATA) lets every settle pay only the ~$0.001 per-tx fee. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only; solana method registration through `create_mppx_server` is a follow-up — merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). `simulate_crypto_deposit`; `create_pi_cache` (TTL'd PI / deposit-address cache, Redis-backed when `redis_url` set, in-memory otherwise), `simulate_deposit_if_test_mode` (gates on `sk_test_` and looks up the PI for you), `STRIPE_TEST_TX_HASH_SUCCESS` / `STRIPE_TEST_TX_HASH_FAILED` constants. Peer dep on `stripe`. |
|
|
82
82
|
| `agentscore_commerce.api` | Everything from `agentscore-py` re-exported in one place: `AgentScore` + `AgentScoreError`, `AGENTSCORE_TEST_ADDRESSES` + `is_agentscore_test_address`. **Don't add `agentscore-py` as a separate dep**: the two can drift versions and cause subtle type mismatches. |
|
|
83
83
|
| `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI: `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export. Flask: `rate_limit_flask(app, ...)` installer. Django: class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`. aiohttp: `rate_limit_aiohttp(...)` middleware factory. Sanic: `rate_limit_sanic(app, ...)` installer. `asgi.RateLimitMiddleware` 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). |
|
|
84
84
|
|
|
@@ -30,7 +30,7 @@ pip install 'agentscore-commerce[fastapi,x402,coinbase]'
|
|
|
30
30
|
| `agentscore_commerce.payment` | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_directive`, `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (v1↔v2 amount field shim that emits both `amount` and `maxAmountRequired` so v1-only x402 parsers like Coinbase awal can read v2 bodies), `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; parses the credential, lifts signer + network, returns a `ZeroSettleResult` shaped identically to the success path so callers branch on rail, not on result shape); `usd_to_atomic` (Decimal-based USD → atomic int, ROUND_HALF_UP — for Tempo / Solana / Base USDC amount construction). |
|
|
31
31
|
| `agentscore_commerce.discovery` | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers like `awal x402 details`), `sample_x402_accept_for_network` (USDC sample-accept builder for known CAIP-2 networks), `build_well_known_mpp`, `build_llms_txt` + `llms_txt_identity_section` + `llms_txt_payment_section` (compact + verbose modes), `build_skill_md` (Claude-Skill-compatible `/skill.md` agent-discovery manifest; strictly agent-facing data only, no internal posture), `build_redemption_skill_md` (delivery-neutral redemption-code template — printed mailers, emailed codes, API trial credits all covered; `endpoint_path`/`delivery_intro`/`body_shape`/`body_rules`/`extra_recovery_rows` overrides for non-goods shapes), `build_merchant_index_json` (canonical `/` discovery body), `standard_endpoint_descriptions(kind=)` (canonical method+path → description map for goods vs api merchants; optional `include_order_status_route` for goods), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps` (canonical skill.md onboarding for goods or API merchants), `agentscore_openapi_snippets`, `build_bazaar_discovery_payload`, `NoindexNonDiscoveryMiddleware` (ASGI middleware emitting `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for non-ASGI frameworks). 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}`. |
|
|
32
32
|
| `agentscore_commerce.challenge` | `build_402_body`, `build_accepted_methods`, `build_identity_metadata` (auto-attached by `Checkout` when an inbound `X-Wallet-Address` header is present), `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients`: smoke-verified CLIs the agent should use; vendor override supported; pure helper `compatible_clients_by_rails(rails)` returns the same map for vendors building custom 402s), `build_pricing_block` (cents to dollar-string with optional shipping/tax), `first_encounter_agent_memory` (cross-merchant hint, returns the canonical block or `None` based on a per-merchant first-seen flag), `Receipt` + `ReceiptNextSteps` + `ProductInfo` + `ShippingAddress` (canonical 200-receipt dataclasses — universal across goods + API merchants); `respond_402`, a drop-in 402 emit that preserves pympp's `WWW-Authenticate` and layers x402's `PAYMENT-REQUIRED`. `build_validation_error`: structured 4xx body builder (`{error: {code, message}, required_fields?, example_body?, next_steps?, ...extra}`) so vendors compose body shapes by name instead of inlining at every validation site. |
|
|
33
|
-
| `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; read `result.deposit_addresses[network]` directly), `create_pay_to_address_from_stripe_pi(authorization_header=, amount_cents=, stripe=, pi_cache=, networks=, metadata=, order_id=, preferred_network=)` — per-order payTo resolver: on the settle leg, reuses the buyer's signed-against payTo from the MPP credential (after `pi_cache.has_address` check); on the discovery leg, mints a fresh PI
|
|
33
|
+
| `agentscore_commerce.stripe_multichain` | `create_multichain_payment_intent` (returns `MultichainPaymentIntentResult(payment_intent_id, deposit_addresses)`; 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=)` — per-order payTo resolver: 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` so verify-leg lookups pass. `mint_multichain_recipients(...same kwargs)` — structured variant returning `MintMultichainRecipientsResult(recipients, payment_intent_id, reused_from_credential)` for the full per-rail map (typical multi-rail merchant hook). Use `static_recipients={"solana": "<wallet>"}` for low-margin endpoints where Solana per-call ATA rent (~$0.50 against MPP spec §13.6) dominates revenue — the SDK skips Stripe minting on that network, reuses the static recipient forever, and (with a one-time external pre-funding of the recipient's USDC ATA) lets every settle pay only the ~$0.001 per-tx fee. `SolanaMppRailSpec.ata_creation_required` defaults to `True` (data-only; solana method registration through `create_mppx_server` is a follow-up — merchants building the solana method directly via `pympp` should pass the flag themselves to the charge factory). `simulate_crypto_deposit`; `create_pi_cache` (TTL'd PI / deposit-address cache, Redis-backed when `redis_url` set, in-memory otherwise), `simulate_deposit_if_test_mode` (gates on `sk_test_` and looks up the PI for you), `STRIPE_TEST_TX_HASH_SUCCESS` / `STRIPE_TEST_TX_HASH_FAILED` constants. Peer dep on `stripe`. |
|
|
34
34
|
| `agentscore_commerce.api` | Everything from `agentscore-py` re-exported in one place: `AgentScore` + `AgentScoreError`, `AGENTSCORE_TEST_ADDRESSES` + `is_agentscore_test_address`. **Don't add `agentscore-py` as a separate dep**: the two can drift versions and cause subtle type mismatches. |
|
|
35
35
|
| `agentscore_commerce.middleware.{fastapi,flask,django,aiohttp,sanic,asgi}` | Framework-specific rate-limit middleware. FastAPI: `rate_limit_fastapi(...)` (FastAPI dependency) plus the ASGI `RateLimitMiddleware` re-export. Flask: `rate_limit_flask(app, ...)` installer. Django: class-based async `RateLimitMiddleware` configured via `settings.AGENTSCORE_RATE_LIMIT`. aiohttp: `rate_limit_aiohttp(...)` middleware factory. Sanic: `rate_limit_sanic(app, ...)` installer. `asgi.RateLimitMiddleware` 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). |
|
|
36
36
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Shared one-shot warning helpers for the SDK.
|
|
2
|
+
|
|
3
|
+
Module-level state ensures each warning fires at most once per process,
|
|
4
|
+
regardless of how many ``Checkout`` / ``compute_first_checkout`` instances
|
|
5
|
+
trigger it. Mirrors ``node-commerce/src/_warnings.ts``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
_warned_no_api_key = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def warn_missing_api_key_once(label: str) -> None:
|
|
16
|
+
"""Emit a one-time warning when AGENTSCORE_API_KEY is unset.
|
|
17
|
+
|
|
18
|
+
Triggered on a settle path that would otherwise enforce wallet OFAC SDN
|
|
19
|
+
sanctions. Both ``Checkout`` and ``compute_first_checkout`` route through
|
|
20
|
+
this so a single multi-surface app sees the warning ONCE, not once per
|
|
21
|
+
surface.
|
|
22
|
+
|
|
23
|
+
``label`` is the caller's identifier for the log message; e.g.
|
|
24
|
+
``"checkout"`` or the compute-first handler's ``name``.
|
|
25
|
+
"""
|
|
26
|
+
global _warned_no_api_key
|
|
27
|
+
if _warned_no_api_key:
|
|
28
|
+
return
|
|
29
|
+
_warned_no_api_key = True
|
|
30
|
+
logging.getLogger(__name__).warning(
|
|
31
|
+
f"[{label}] AGENTSCORE_API_KEY is not set — wallet OFAC SDN sanctions are NOT being enforced. "
|
|
32
|
+
"Set the env var to enable strict-liability protection on settle."
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _reset_warned_no_api_key() -> None:
|
|
37
|
+
"""Test-only: reset the warn-once flag."""
|
|
38
|
+
global _warned_no_api_key
|
|
39
|
+
_warned_no_api_key = False
|
{agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/challenge/how_to_pay.py
RENAMED
|
@@ -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 = "<your_opc_token>", # noqa: S107 — literal placeholder, not a secret
|
|
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]:
|
|
@@ -62,6 +62,12 @@ async def build_how_to_pay(
|
|
|
62
62
|
≥ $1 the default is still ``ceil(total) + 1`` formatted at ``decimals``; for
|
|
63
63
|
sub-dollar totals the default uses ``total.toFixed(decimals)`` so the cap
|
|
64
64
|
flag reflects the real amount instead of always being ``"1.00"``.
|
|
65
|
+
|
|
66
|
+
``op_token_placeholder`` defaults to ``"<your_opc_token>"``. Pass ``None``
|
|
67
|
+
(gateless merchants) to strip the ``-H 'X-Operator-Token: ...'`` snippet
|
|
68
|
+
from every rail command — appropriate when the merchant doesn't run an
|
|
69
|
+
identity gate. The wallet OFAC SDN default (TEC-311) does NOT need an
|
|
70
|
+
operator token, so gateless merchants emit cleaner commands.
|
|
65
71
|
"""
|
|
66
72
|
total_num = float(total_usd) if isinstance(total_usd, str) else total_usd
|
|
67
73
|
if max_spend is not None:
|
|
@@ -70,7 +76,9 @@ async def build_how_to_pay(
|
|
|
70
76
|
max_spend_str = f"{math.ceil(total_num) + 1:.{decimals}f}"
|
|
71
77
|
else:
|
|
72
78
|
max_spend_str = f"{total_num:.{decimals}f}"
|
|
73
|
-
|
|
79
|
+
gateless = op_token_placeholder is None
|
|
80
|
+
op_token = op_token_placeholder or "<your_opc_token>"
|
|
81
|
+
op_token_header_flag = "" if gateless else f"-H 'X-Operator-Token: {op_token}' "
|
|
74
82
|
block: dict[str, Any] = {}
|
|
75
83
|
|
|
76
84
|
tempo = rails.get("tempo")
|
|
@@ -79,11 +87,11 @@ async def build_how_to_pay(
|
|
|
79
87
|
chain_id = tempo.chain_id
|
|
80
88
|
recommend = tempo.recommend
|
|
81
89
|
tempo_command = (
|
|
82
|
-
f"tempo request -X POST
|
|
90
|
+
f"tempo request -X POST {op_token_header_flag}-H 'Content-Type: application/json' "
|
|
83
91
|
f"--json '{retry_body_json}' --max-spend {max_spend_str} {url}"
|
|
84
92
|
)
|
|
85
93
|
pay_command = (
|
|
86
|
-
f"agentscore-pay pay POST {url} --chain tempo
|
|
94
|
+
f"agentscore-pay pay POST {url} --chain tempo {op_token_header_flag}"
|
|
87
95
|
f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
|
|
88
96
|
)
|
|
89
97
|
entry: dict[str, Any] = {
|
|
@@ -115,7 +123,7 @@ async def build_how_to_pay(
|
|
|
115
123
|
f"least ${max_spend_str}. If the CLI is not installed, run the setup commands above first."
|
|
116
124
|
),
|
|
117
125
|
"command": (
|
|
118
|
-
f"agentscore-pay pay POST {url} --chain base
|
|
126
|
+
f"agentscore-pay pay POST {url} --chain base {op_token_header_flag}"
|
|
119
127
|
f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
|
|
120
128
|
),
|
|
121
129
|
"what_it_does": (
|
|
@@ -135,7 +143,7 @@ async def build_how_to_pay(
|
|
|
135
143
|
f"is at least ${max_spend_str}. If the CLI is not installed, run the setup commands above first."
|
|
136
144
|
),
|
|
137
145
|
"command": (
|
|
138
|
-
f"agentscore-pay pay POST {url} --chain solana
|
|
146
|
+
f"agentscore-pay pay POST {url} --chain solana {op_token_header_flag}"
|
|
139
147
|
f"-H 'Content-Type: application/json' -d '{retry_body_json}' --max-spend {max_spend_str}"
|
|
140
148
|
),
|
|
141
149
|
"what_it_does": (
|
|
@@ -182,6 +190,9 @@ async def build_how_to_pay(
|
|
|
182
190
|
),
|
|
183
191
|
(
|
|
184
192
|
f"link-cli mpp pay {url} --spend-request-id $SPEND_ID --method POST "
|
|
193
|
+
f"--data '{retry_body_json}' --output-json"
|
|
194
|
+
if gateless
|
|
195
|
+
else f"link-cli mpp pay {url} --spend-request-id $SPEND_ID --method POST "
|
|
185
196
|
f"--data '{retry_body_json}' --header 'X-Operator-Token: {op_token}' --output-json"
|
|
186
197
|
),
|
|
187
198
|
]
|
|
@@ -706,6 +706,30 @@ class Checkout:
|
|
|
706
706
|
body's ``extensions`` field so Bazaar crawlers and other spec-compliant
|
|
707
707
|
clients read the route's declared input/output schema."""
|
|
708
708
|
|
|
709
|
+
def _has_identity_gate(self) -> bool:
|
|
710
|
+
"""Return True when the merchant configured an identity-bearing flag.
|
|
711
|
+
|
|
712
|
+
The identity-bearing flags are ``require_kyc``, ``require_sanctions_clear``
|
|
713
|
+
(name screening on the KYC identity), ``min_age``, or jurisdiction
|
|
714
|
+
lists. Wallet OFAC SDN enforcement (the always-on default) does NOT
|
|
715
|
+
count as an identity gate; agents don't need an AgentScore credential
|
|
716
|
+
to satisfy it.
|
|
717
|
+
|
|
718
|
+
Used to conditionally emit AgentScore identity boilerplate in 402
|
|
719
|
+
bodies (``agent_memory``, ``X-Operator-Token`` references in per-rail
|
|
720
|
+
commands).
|
|
721
|
+
"""
|
|
722
|
+
g = self.gate
|
|
723
|
+
if g is None:
|
|
724
|
+
return False
|
|
725
|
+
return bool(
|
|
726
|
+
g.require_kyc
|
|
727
|
+
or g.require_sanctions_clear
|
|
728
|
+
or g.min_age is not None
|
|
729
|
+
or (g.allowed_jurisdictions and len(g.allowed_jurisdictions) > 0)
|
|
730
|
+
or (g.blocked_jurisdictions and len(g.blocked_jurisdictions) > 0)
|
|
731
|
+
)
|
|
732
|
+
|
|
709
733
|
async def _get_x402_server(self) -> Any:
|
|
710
734
|
"""Resolve the x402 server.
|
|
711
735
|
|
|
@@ -899,13 +923,22 @@ class Checkout:
|
|
|
899
923
|
|
|
900
924
|
ctx.pricing = await _maybe_await(self.compute_pricing(ctx))
|
|
901
925
|
|
|
902
|
-
# Per-request
|
|
903
|
-
# passes through to 402).
|
|
904
|
-
#
|
|
905
|
-
#
|
|
926
|
+
# Per-request compliance: runs on the settle leg only (anonymous
|
|
927
|
+
# discovery passes through to 402).
|
|
928
|
+
#
|
|
929
|
+
# Two paths converge here:
|
|
930
|
+
# - Merchants with an explicit ``gate`` config run the full identity
|
|
931
|
+
# policy (KYC / age / sanctions / jurisdiction) via ``_run_gate``.
|
|
932
|
+
# - Merchants WITHOUT a ``gate`` config still get wallet OFAC SDN
|
|
933
|
+
# enforcement via ``_run_wallet_sanctions_only`` — the always-on
|
|
934
|
+
# strict-liability default. Falls back to AGENTSCORE_API_KEY env
|
|
935
|
+
# var when set; logs a warning and skips when no key is set
|
|
936
|
+
# (dev/testnet pattern).
|
|
906
937
|
has_payment_header = has_x402_header(request.headers) or has_mppx_header(request.headers)
|
|
907
|
-
if
|
|
908
|
-
gate_result =
|
|
938
|
+
if has_payment_header:
|
|
939
|
+
gate_result = (
|
|
940
|
+
await self._run_gate(ctx) if self.gate is not None else await self._run_wallet_sanctions_only(ctx)
|
|
941
|
+
)
|
|
909
942
|
if gate_result is not None:
|
|
910
943
|
return gate_result
|
|
911
944
|
|
|
@@ -1402,6 +1435,13 @@ class Checkout:
|
|
|
1402
1435
|
result = await _maybe_await(gate.run_gate(ctx))
|
|
1403
1436
|
return self._coerce_run_gate_result(ctx, result)
|
|
1404
1437
|
|
|
1438
|
+
# Gate configured without an API key — full policy enforcement requires
|
|
1439
|
+
# /v1/assess access, which we can't reach. Fall through to wallet OFAC
|
|
1440
|
+
# SDN enforcement (the strict-liability default) so the merchant still
|
|
1441
|
+
# gets the basic protection layer instead of silently allowing.
|
|
1442
|
+
if not gate.api_key:
|
|
1443
|
+
return await self._run_wallet_sanctions_only(ctx)
|
|
1444
|
+
|
|
1405
1445
|
# 2. per_request_policy resolves per-product compliance (e.g. wine vs
|
|
1406
1446
|
# generic merch). Return None to skip the gate entirely for this request.
|
|
1407
1447
|
policy: Any = None
|
|
@@ -1500,6 +1540,117 @@ class Checkout:
|
|
|
1500
1540
|
)
|
|
1501
1541
|
return None
|
|
1502
1542
|
|
|
1543
|
+
async def _run_wallet_sanctions_only(self, ctx: CheckoutContext) -> CheckoutResult | None:
|
|
1544
|
+
"""Wallet OFAC SDN enforcement.
|
|
1545
|
+
|
|
1546
|
+
Runs on settle (payment header present) when either ``self.gate`` is
|
|
1547
|
+
None OR a gate is configured but has no ``api_key`` to reach
|
|
1548
|
+
``/v1/assess`` for full policy enforcement (fallback to the
|
|
1549
|
+
strict-liability default).
|
|
1550
|
+
|
|
1551
|
+
Env knobs:
|
|
1552
|
+
- ``AGENTSCORE_API_KEY`` — required. No key → one-time warning + skip
|
|
1553
|
+
(dev/testnet pattern; production should always configure a key).
|
|
1554
|
+
- ``AGENTSCORE_BASE_URL`` — optional override for staging/dev API
|
|
1555
|
+
(e.g. ``https://api-dev.agentscore.sh`` or ``http://localhost:3002``).
|
|
1556
|
+
|
|
1557
|
+
Stripe SPT (no extractable wallet signer) → skip silently; Stripe runs
|
|
1558
|
+
its own OFAC screen on the buyer's Stripe account at customer creation.
|
|
1559
|
+
|
|
1560
|
+
Calls ``/v1/assess`` with the signer wallet as both the primary address
|
|
1561
|
+
and the signer block. The API enforces signer-sanctions unconditionally
|
|
1562
|
+
when a signer is present (no policy flag needed). Denies on OFAC SDN
|
|
1563
|
+
hit; fail-closed on unavailable lookup (strict liability — falsely
|
|
1564
|
+
allowing a sanctioned settle is an OFAC violation, falsely denying a
|
|
1565
|
+
clean buyer is just bad UX).
|
|
1566
|
+
"""
|
|
1567
|
+
import os
|
|
1568
|
+
|
|
1569
|
+
from agentscore_commerce._warnings import warn_missing_api_key_once
|
|
1570
|
+
|
|
1571
|
+
api_key = (self.gate.api_key if self.gate is not None else None) or os.environ.get("AGENTSCORE_API_KEY")
|
|
1572
|
+
if not api_key:
|
|
1573
|
+
warn_missing_api_key_once("checkout")
|
|
1574
|
+
return None
|
|
1575
|
+
|
|
1576
|
+
from agentscore_commerce.payment.signer import extract_payment_signer, read_x402_payment_header
|
|
1577
|
+
|
|
1578
|
+
x402_header = read_x402_payment_header(ctx.request.headers)
|
|
1579
|
+
authorization_header: str | None = None
|
|
1580
|
+
for header_key, header_value in ctx.request.headers.items():
|
|
1581
|
+
if header_key.lower() == "authorization":
|
|
1582
|
+
authorization_header = header_value
|
|
1583
|
+
break
|
|
1584
|
+
signer = extract_payment_signer(x402_header, authorization_header=authorization_header)
|
|
1585
|
+
if signer is None:
|
|
1586
|
+
# Stripe SPT path — no wallet signer, no OFAC check possible. Stripe
|
|
1587
|
+
# screens its own customer accounts; we have nothing to add here.
|
|
1588
|
+
return None
|
|
1589
|
+
|
|
1590
|
+
from agentscore_commerce.api import AgentScore
|
|
1591
|
+
from agentscore_commerce.identity._denial import denial_reason_status
|
|
1592
|
+
from agentscore_commerce.identity._response import denial_reason_to_body
|
|
1593
|
+
from agentscore_commerce.identity.types import DenialReason
|
|
1594
|
+
|
|
1595
|
+
base_url = os.environ.get("AGENTSCORE_BASE_URL")
|
|
1596
|
+
client_kwargs: dict[str, Any] = {"api_key": api_key}
|
|
1597
|
+
if base_url:
|
|
1598
|
+
client_kwargs["base_url"] = base_url
|
|
1599
|
+
client = AgentScore(**client_kwargs)
|
|
1600
|
+
from agentscore.errors import (
|
|
1601
|
+
AgentScoreError,
|
|
1602
|
+
InvalidCredentialError,
|
|
1603
|
+
TokenExpiredError,
|
|
1604
|
+
)
|
|
1605
|
+
|
|
1606
|
+
try:
|
|
1607
|
+
result = await client.aassess(
|
|
1608
|
+
address=signer.address,
|
|
1609
|
+
signer={"address": signer.address, "network": signer.network},
|
|
1610
|
+
)
|
|
1611
|
+
except (TokenExpiredError, InvalidCredentialError) as err:
|
|
1612
|
+
# 401 — credential issues map to invalid_credential. These are
|
|
1613
|
+
# unusual on the wallet-OFAC-only path (no operator_token) but
|
|
1614
|
+
# carried for parity with node's mapping.
|
|
1615
|
+
reason = DenialReason(
|
|
1616
|
+
code="invalid_credential" if isinstance(err, InvalidCredentialError) else "token_expired",
|
|
1617
|
+
message=str(err),
|
|
1618
|
+
)
|
|
1619
|
+
return CheckoutResult(
|
|
1620
|
+
status=denial_reason_status(reason),
|
|
1621
|
+
body=denial_reason_to_body(reason),
|
|
1622
|
+
headers={},
|
|
1623
|
+
reference_id=ctx.reference_id,
|
|
1624
|
+
settled=False,
|
|
1625
|
+
)
|
|
1626
|
+
except (AgentScoreError, Exception) as err:
|
|
1627
|
+
# 503 — API outage or network failure. Fail-closed: strict-liability.
|
|
1628
|
+
reason = DenialReason(code="api_error", message=str(err))
|
|
1629
|
+
return CheckoutResult(
|
|
1630
|
+
status=denial_reason_status(reason),
|
|
1631
|
+
body=denial_reason_to_body(reason),
|
|
1632
|
+
headers={},
|
|
1633
|
+
reference_id=ctx.reference_id,
|
|
1634
|
+
settled=False,
|
|
1635
|
+
)
|
|
1636
|
+
|
|
1637
|
+
decision = result.get("decision") if isinstance(result, dict) else None
|
|
1638
|
+
if decision == "deny":
|
|
1639
|
+
decision_reasons = result.get("decision_reasons") or [] if isinstance(result, dict) else []
|
|
1640
|
+
reason = DenialReason(
|
|
1641
|
+
code="wallet_not_trusted",
|
|
1642
|
+
reasons=list(decision_reasons),
|
|
1643
|
+
decision=decision,
|
|
1644
|
+
)
|
|
1645
|
+
return CheckoutResult(
|
|
1646
|
+
status=denial_reason_status(reason),
|
|
1647
|
+
body=denial_reason_to_body(reason),
|
|
1648
|
+
headers={},
|
|
1649
|
+
reference_id=ctx.reference_id,
|
|
1650
|
+
settled=False,
|
|
1651
|
+
)
|
|
1652
|
+
return None
|
|
1653
|
+
|
|
1503
1654
|
def _coerce_run_gate_result(
|
|
1504
1655
|
self,
|
|
1505
1656
|
ctx: CheckoutContext,
|
|
@@ -1849,6 +2000,10 @@ class Checkout:
|
|
|
1849
2000
|
total_usd=f"{ctx.pricing.amount_usd:.{pricing_decimals}f}",
|
|
1850
2001
|
rails=how_to_pay_rails,
|
|
1851
2002
|
decimals=pricing_decimals,
|
|
2003
|
+
# Merchants without an identity-bearing policy flag get clean commands
|
|
2004
|
+
# without an X-Operator-Token header — agents don't need one to satisfy
|
|
2005
|
+
# wallet OFAC enforcement (the always-on default per TEC-311).
|
|
2006
|
+
op_token_placeholder=None if not self._has_identity_gate() else "<your_opc_token>",
|
|
1852
2007
|
)
|
|
1853
2008
|
pricing_block = ctx.pricing.block or build_pricing_block(
|
|
1854
2009
|
subtotal_cents=ctx.pricing.amount_usd * 100,
|
|
@@ -1898,7 +2053,10 @@ class Checkout:
|
|
|
1898
2053
|
pricing=pricing_block,
|
|
1899
2054
|
amount_usd=f"{ctx.pricing.amount_usd:.{pricing_decimals}f}",
|
|
1900
2055
|
retry_body=ctx.request.body,
|
|
1901
|
-
|
|
2056
|
+
# Merchants without an identity-bearing gate get a clean 402: no
|
|
2057
|
+
# AgentScore-identity bootstrap describing a verification flow they
|
|
2058
|
+
# don't run. Wallet OFAC (the always-on default) doesn't need it.
|
|
2059
|
+
agent_memory=first_encounter_agent_memory(first_encounter=self._has_identity_gate()),
|
|
1902
2060
|
product=ctx.pricing.product,
|
|
1903
2061
|
extra=ctx.pricing.body_extras,
|
|
1904
2062
|
x402=X402PaymentRequired(
|
|
@@ -34,7 +34,7 @@ from __future__ import annotations
|
|
|
34
34
|
import logging
|
|
35
35
|
import uuid
|
|
36
36
|
from dataclasses import dataclass, field
|
|
37
|
-
from datetime import datetime, timezone
|
|
37
|
+
from datetime import UTC, datetime, timezone
|
|
38
38
|
from decimal import Decimal
|
|
39
39
|
from typing import TYPE_CHECKING, Any
|
|
40
40
|
|
|
@@ -434,6 +434,91 @@ class ComputeFirstCheckout:
|
|
|
434
434
|
)
|
|
435
435
|
return 402, body_402, headers
|
|
436
436
|
|
|
437
|
+
async def _enforce_wallet_sanctions(
|
|
438
|
+
self,
|
|
439
|
+
request: ComputeFirstRequest,
|
|
440
|
+
reference_id: str,
|
|
441
|
+
) -> tuple[int, dict[str, Any], dict[str, str]] | None:
|
|
442
|
+
"""Always-on wallet OFAC SDN enforcement for compute-first merchants.
|
|
443
|
+
|
|
444
|
+
Mirrors :meth:`Checkout._run_wallet_sanctions_only`. Resolves API key
|
|
445
|
+
from ``AGENTSCORE_API_KEY``, optional base URL from
|
|
446
|
+
``AGENTSCORE_BASE_URL``, extracts the signer from the payment header,
|
|
447
|
+
calls ``/v1/assess`` with the signer block (no policy). Denies on SDN
|
|
448
|
+
hit or unavailable lookup. Skips silently for Stripe SPT (no wallet
|
|
449
|
+
signer) and when no API key is set (log-once warning).
|
|
450
|
+
"""
|
|
451
|
+
import os
|
|
452
|
+
|
|
453
|
+
from agentscore_commerce._warnings import warn_missing_api_key_once
|
|
454
|
+
|
|
455
|
+
api_key = os.environ.get("AGENTSCORE_API_KEY")
|
|
456
|
+
if not api_key:
|
|
457
|
+
warn_missing_api_key_once(f"{self.name}.compute_first")
|
|
458
|
+
return None
|
|
459
|
+
|
|
460
|
+
from agentscore_commerce.payment.signer import extract_payment_signer, read_x402_payment_header
|
|
461
|
+
|
|
462
|
+
x402_header = read_x402_payment_header(request.headers)
|
|
463
|
+
authorization_header: str | None = None
|
|
464
|
+
for header_key, header_value in request.headers.items():
|
|
465
|
+
if header_key.lower() == "authorization":
|
|
466
|
+
authorization_header = header_value
|
|
467
|
+
break
|
|
468
|
+
signer = extract_payment_signer(x402_header, authorization_header=authorization_header)
|
|
469
|
+
if signer is None:
|
|
470
|
+
return None # Stripe SPT — no wallet signer to screen
|
|
471
|
+
|
|
472
|
+
from agentscore_commerce.api import AgentScore
|
|
473
|
+
|
|
474
|
+
base_url = os.environ.get("AGENTSCORE_BASE_URL")
|
|
475
|
+
client_kwargs: dict[str, Any] = {"api_key": api_key}
|
|
476
|
+
if base_url:
|
|
477
|
+
client_kwargs["base_url"] = base_url
|
|
478
|
+
client = AgentScore(**client_kwargs)
|
|
479
|
+
|
|
480
|
+
from agentscore_commerce.identity._denial import denial_reason_status
|
|
481
|
+
from agentscore_commerce.identity._response import denial_reason_to_body
|
|
482
|
+
from agentscore_commerce.identity.types import DenialReason
|
|
483
|
+
|
|
484
|
+
denial_reason: DenialReason | None = None
|
|
485
|
+
try:
|
|
486
|
+
result = await client.aassess(
|
|
487
|
+
address=signer.address,
|
|
488
|
+
signer={"address": signer.address, "network": signer.network},
|
|
489
|
+
)
|
|
490
|
+
except Exception as err:
|
|
491
|
+
denial_reason = DenialReason(code="api_error", message=str(err))
|
|
492
|
+
result = None
|
|
493
|
+
|
|
494
|
+
if denial_reason is None:
|
|
495
|
+
decision = result.get("decision") if isinstance(result, dict) else None
|
|
496
|
+
if decision == "deny":
|
|
497
|
+
decision_reasons = list(result.get("decision_reasons") or []) if isinstance(result, dict) else []
|
|
498
|
+
denial_reason = DenialReason(
|
|
499
|
+
code="wallet_not_trusted",
|
|
500
|
+
reasons=decision_reasons,
|
|
501
|
+
decision=decision,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
if denial_reason is not None:
|
|
505
|
+
# Spread denial_reason_to_body so agent_instructions / verify_url
|
|
506
|
+
# ride through (parity with node's enforceWalletSanctions which
|
|
507
|
+
# spreads denialReasonToBody into the compute-first envelope).
|
|
508
|
+
denial_body = denial_reason_to_body(denial_reason)
|
|
509
|
+
return (
|
|
510
|
+
denial_reason_status(denial_reason),
|
|
511
|
+
{
|
|
512
|
+
"id": reference_id,
|
|
513
|
+
"endpoint": self.name,
|
|
514
|
+
"created_at": datetime.now(UTC).isoformat(),
|
|
515
|
+
"payment_status": "failed",
|
|
516
|
+
**denial_body,
|
|
517
|
+
},
|
|
518
|
+
{"Content-Type": "application/json"},
|
|
519
|
+
)
|
|
520
|
+
return None
|
|
521
|
+
|
|
437
522
|
async def _handle_x402_settle(
|
|
438
523
|
self,
|
|
439
524
|
request: ComputeFirstRequest,
|
|
@@ -693,6 +778,13 @@ class ComputeFirstCheckout:
|
|
|
693
778
|
{"Content-Type": "application/json"},
|
|
694
779
|
)
|
|
695
780
|
recipients = quote.recipients if hasattr(quote, "recipients") else {}
|
|
781
|
+
# Wallet OFAC SDN enforcement (always-on default — mirrors
|
|
782
|
+
# Checkout._run_wallet_sanctions_only). Strict-liability check
|
|
783
|
+
# before the rail-specific settle so funds don't move (x402) or
|
|
784
|
+
# order doesn't fulfill (MPP) for a sanctioned wallet.
|
|
785
|
+
ofac_denial = await self._enforce_wallet_sanctions(request, reference_id)
|
|
786
|
+
if ofac_denial is not None:
|
|
787
|
+
return ofac_denial
|
|
696
788
|
if has_x402_header(request.headers):
|
|
697
789
|
return await self._handle_x402_settle(
|
|
698
790
|
request, reference_id, quote.body, int(quote.price_cents), recipients
|
{agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/core.py
RENAMED
|
@@ -415,10 +415,10 @@ class AgentScoreCore:
|
|
|
415
415
|
request — single round trip. Returns ``None`` when the gate didn't run with
|
|
416
416
|
a signer (operator-token-only paths, discovery legs).
|
|
417
417
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
verdict.
|
|
418
|
+
Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the
|
|
419
|
+
request — SDN wallet-address hits are already enforced by the gate
|
|
420
|
+
(decision -> deny before the handler runs); merchant code typically only
|
|
421
|
+
needs this for the signer_match wallet-binding verdict.
|
|
422
422
|
"""
|
|
423
423
|
claimed_norm = normalize_address(claimed_address)
|
|
424
424
|
key = self._cache_key(address=claimed_norm)
|
{agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/middleware.py
RENAMED
|
@@ -322,10 +322,10 @@ def get_signer_verdict(request: Request) -> SignerVerdict | None:
|
|
|
322
322
|
Returns ``None`` when the gate didn't run with a signer: operator-token-only paths,
|
|
323
323
|
discovery legs that arrive without a payment credential, and fail-open pass-throughs.
|
|
324
324
|
|
|
325
|
-
|
|
326
|
-
gate to ``decision=deny`` before the handler
|
|
327
|
-
``signer_match`` for the wallet-binding
|
|
328
|
-
:func:`build_signer_mismatch_body`).
|
|
325
|
+
Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the request —
|
|
326
|
+
SDN wallet-address hits already flip the gate to ``decision=deny`` before the handler
|
|
327
|
+
runs. Merchant code typically only reads ``signer_match`` for the wallet-binding
|
|
328
|
+
verdict (e.g. via :func:`build_signer_mismatch_body`).
|
|
329
329
|
"""
|
|
330
330
|
state = request.scope.get("state", {}).get(GATE_STATE_KEY)
|
|
331
331
|
if not state or not state.get("wallet_address"):
|
{agentscore_commerce-2.1.1 → agentscore_commerce-2.3.0}/agentscore_commerce/identity/types.py
RENAMED
|
@@ -128,10 +128,10 @@ class SignerVerdict:
|
|
|
128
128
|
``/v1/assess`` call (single round trip). ``signer_match`` describes the wallet-
|
|
129
129
|
binding; ``signer_sanctions`` describes the OFAC SDN wallet-address check.
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
for the wallet-binding verdict.
|
|
131
|
+
Wallet-OFAC SDN enforcement is unconditional whenever a signer is in the request —
|
|
132
|
+
a ``signer_sanctions`` hit OR an unavailable lookup already flips
|
|
133
|
+
``decision -> deny`` inside the gate before the handler runs. Merchant code
|
|
134
|
+
typically only needs to read ``signer_match`` here for the wallet-binding verdict.
|
|
135
135
|
"""
|
|
136
136
|
|
|
137
137
|
signer_match: VerifyWalletSignerResult | None = None
|