> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentscore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AgentScore Commerce (Python)

> Identity middleware + payment helpers + 402 builders + discovery + Stripe multichain. The full merchant-side toolkit for agentic commerce.

`agentscore-commerce` is the full merchant-side SDK for agentic commerce in Python. One install bundles identity gating, payment-protocol helpers, 402 challenge builders, discovery doc generators, and Stripe multichain support. Submodule imports keep dependencies focused.

## Installation

```bash theme={"dark"}
pip install agentscore-commerce[fastapi]   # or [flask], [django], [aiohttp], [sanic], [starlette], [stripe]
```

Accept x402 payments through the Coinbase facilitator (mints per-endpoint CDP JWTs via `cdp-sdk`):

```bash theme={"dark"}
pip install 'agentscore-commerce[fastapi,x402,coinbase]'
# Set CDP_API_KEY_ID and CDP_API_KEY_SECRET in the environment.
```

The `[mppx]` extra adds Tempo MPP + Stripe SPT helpers.

## Build the x402 accepts entry for a 402 challenge

```python theme={"dark"}
from agentscore_commerce.payment import build_x402_accepts_for_402, create_x402_server

x402_server = await create_x402_server(facilitator="coinbase", rails=["x402-base-mainnet"])

x402_accepts = build_x402_accepts_for_402(
    x402_server,
    network="eip155:8453",
    price=f"${total_usd}",
    pay_to=os.environ["TREASURY_BASE_RECIPIENT"],
    max_timeout_seconds=300,
)
```

Returns a list of plain dicts ready for the 402 body's `accepts[]`. `extra.name` is derived from the registered scheme metadata so the EIP-712 domain matches the on-chain USDC contract.

## Submodules

| Import                                                                         | What it gives you                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agentscore_commerce` (top-level: the 2.0 high-level surface)                  | `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+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}`). Plus `compute_first_checkout`: variable-cost pay-per-result helper (compute-first + exact-x402) that works on every exact-mode rail without upto / Permit2 / Settlement-Overrides; `create_quote_cache`: content-hash quote cache used by the compute-first helper (in-memory default; pass `redis_url` for distributed deployments). `create_result_cache`: the neutral keyed JSON-value cache underneath it (same body-hash key builder); cache any probe-leg result a `Checkout` merchant replays on the settle leg, for example a paid upstream call made in `pre_validate`. `create_default_on_denied({merchant_name, support_email, support_context?, wallet_not_trusted_message?, payment_required_message?})`: canonical `on_denied(reason) -> DefaultOnDeniedResult` factory matching `Checkout`'s gate hook (handles `wallet_signer_mismatch`, `wallet_not_trusted` unfixable fallback, `payment_required`, `token_expired`/`invalid_credential`/`api_error`). `has_payment_header(request_or_headers)`: discriminator that splits discovery legs (no payment credential → 402) from settle legs (`payment-signature` / `x-payment` / `Authorization: Payment JWT-BYTES`); `has_x402_header` / `has_mppx_header`: granular dispatch helpers (x402 vs MPP credential present). \`malformed\_payment\_credential(request                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | headers)`: wire-shape gate for payment credentials (values that are not base64 JSON and not token-shaped are rejected); `Checkout`runs it before any merchant hook by default, opt out with`credential\_pre\_check=False`. `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`. Returns `DefaultOnDeniedResult(body, status, headers)`; FastAPI / Flask / aiohttp / Sanic `on\_denied`callbacks accept an optional 3-tuple`(body, status, headers)`to thread headers through.`extract\_owner\_scope(headers) -> OwnerScope`: pull canonical owner identity from `X-Wallet-Address`/`X-Operator-Token`with safe token hashing. Plus factories:`pricing\_result`(cents → typed`PricingResult`), `validation\_response\_{fastapi,flask,django,aiohttp,sanic}`(4xx envelope per framework),`make\_mppx\_compose\_hook\`. |
| `agentscore_commerce.identity.{fastapi,flask,django,aiohttp,sanic,middleware}` | Trust gate middleware: KYC, age, sanctions (account name + signer wallet), jurisdiction. `AgentScoreGate(...)` (or `agentscore_gate(app, ...)` on Flask/Sanic), `capture_wallet(...)`, `get_signer_verdict(...)`, `get_agentscore_data(...)`. Each adapter also 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 / Django expose `ConditionalAgentScoreGate`; Flask + Sanic expose `conditional_agentscore_gate(app, ...)`; aiohttp exposes `conditional_agentscore_gate_middleware(...)`. The existing `agentscore_gate(app, ...)` and `AgentScoreGate(...)` accept an optional `condition=` callable for inline gating.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `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 raising `CheckoutValidationError` with the canonical envelope on miss). All five are also re-exported at `agentscore_commerce` top-level.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `agentscore_commerce.identity` (publishers + key/token helpers)                | Cross-vendor identity-publishing helpers: `build_a2a_agent_card` (Google A2A v1.0 Signed Agent Card, served at `/.well-known/agent-card.json`), `build_ucp_profile` (Google Universal Commerce Protocol, served at `/.well-known/ucp`). Both return unsigned dataclasses with `to_dict()`: vendor signs + publishes. Key + token helpers: `load_ucp_signing_key_from_env` (cached env-driven loader: 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); `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).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `agentscore_commerce.payment`                                                  | `networks`, `USDC`, `rails` registries; `payment_directive`, `build_payment_headers` (one-call WWW-Authenticate + PAYMENT-REQUIRED bundle), `www_authenticate_header`, `payment_required_header`, `alias_amount_fields` (opt-in v1↔v2 amount-field shim; the 402 builders do NOT apply it by default: strict x402 v2 settlement matches the whole accepts object, so an extra field breaks settle; reach for it only when a specific 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), `parse_did_pkh_address` (parses raw `did:pkh:FAMILY:CHAIN:ADDR` DIDs into a typed `PaymentSigner`; used by `extract_payment_signer` and re-exported for callers receiving a typed source string from pympp), `detect_rail_from_headers` (returns `"x402"` / `"mpp"` / `None` from inbound headers), `build_idempotency_key`; `create_x402_server`, `create_mppx_server`; `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. Flipping `network` alone derives the right `token` / `chain_id`: Base Sepolia → Sepolia USDC + chain\_id 84532, Solana devnet → devnet USDC mint. Solana's `network` accepts CAIP-2 or 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 `stripe/charge` when `amount_usd < 0.50` since Stripe's fixed \~$0.30 fee makes sub-50-cent charges unprofitable; sub-50-cent APIs pass `include_stripe=False` explicitly), `is_evm_network` / `is_solana_network` (CAIP-2 discriminators), `has_payment_header` (settle-leg discriminator); 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 in 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; conservative substring fallback 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, rejects NaN / negative / infinite; for Tempo / Solana / Base USDC amount construction). |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `agentscore_commerce.discovery`                                                | `is_discovery_probe_request`, `build_discovery_probe_response` (with optional `x402_sample` for x402-aware crawlers), `sample_x402_accept_for_network`, `build_well_known_mpp`, `build_well_known_x402` (x402scan v1 `/.well-known/x402` shape), `build_llms_txt`, `build_skill_md` (Claude-Skill-compatible `/skill.md` agent-discovery manifest), `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` + `standard_endpoint_descriptions(kind=)` (canonical `/` discovery body for goods vs api merchants), `build_success_next_steps` (universal Passport-active success block), `build_agentscore_onboarding_steps`, `agentscore_openapi_snippets`, `siwx_security_scheme` + `x_payment_info_extension` + `x_guidance_extension`, `build_bazaar_discovery_payload`; `NoindexNonDiscoveryMiddleware` (ASGI) + `install_flask_noindex` (Flask) + `DjangoNoindexMiddleware` (Django): emit `X-Robots-Tag: noindex` on every path except the agent-discovery surfaces; pure helpers `is_discovery_path` + `DEFAULT_DISCOVERY_PATHS` for any framework not listed. 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}`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `agentscore_commerce.challenge`                                                | `build_accepted_methods`, `build_identity_metadata` (auto-attached by `Checkout` when wallet header present), `build_how_to_pay`, `build_agent_instructions` (auto-emits per-rail `compatible_clients`; pure helper `compatible_clients_by_rails(rails)` returns the same map for vendors building custom 402s), `build_402_body` (pass `x402=X402PaymentRequired(resource=X402ResourceInfo(...))` for x402 v2 resource metadata, `url` plus optional `service_name` / `tags` / `description` / `mime_type` / `icon_url` emitted with x402 camelCase keys, and `extensions` for declared extensions; both surface on the 402 body for spec-compliant crawlers, and `respond_402` mirrors `resource` into the PAYMENT-REQUIRED header), `build_pricing_block` (cents → dollar-string; optional `shipping_cents` + `discount_cents`; pass `discount_cents` for redemption codes / coupons so the 402 body surfaces `discount` alongside `subtotal` / `total` for agent-renderable savings; optional `decimals` for sub-cent precision so per-token / per-byte unit pricing advertises real amounts instead of rounding to two decimals), `first_encounter_agent_memory`, `Receipt`/`ReceiptNextSteps`/`ProductInfo`/`ShippingAddress` dataclasses (canonical 200-receipt shape); `respond_402`: 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `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).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `agentscore_commerce.stripe_multichain`                                        | `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=)`: 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` 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 and reuses the static recipient forever; pair with a one-time external USDC transfer to pre-create the recipient's USDC 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 `ata_creation_required` themselves to the charge factory). `simulate_crypto_deposit`, `create_mppx_stripe`; `create_pi_cache` (TTL'd PI / deposit-address cache, Redis-backed when `redis_url` set), `simulate_deposit_if_test_mode` (gates on `sk_test_` and looks up the PI for you), `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), `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), `STRIPE_TEST_TX_HASH_SUCCESS` / `STRIPE_TEST_TX_HASH_FAILED` constants.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |

## Identity model

Two identity types: wallet (`X-Wallet-Address`) and operator-token (`X-Operator-Token`). Default checks operator-token first, then wallet. Address normalization is network-aware: EVM lowercased, Solana base58 preserved verbatim.

`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.

`create_session_on_missing` auto-mints a verification session for two paths: cold-start (no identity headers) AND `wallet_not_trusted` with fixable reasons (`kyc_required` / `kyc_pending` / `kyc_failed`). Both paths rewrite the denial to `identity_verification_required` before reaching `on_denied`, so merchants only handle one code. **When the merchant omits `create_session_on_missing` from the gate config, `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. `build_verification_required_body(reason, message=?, agent_instructions=?, extra=?)` collapses the per-merchant body-mapping boilerplate. The gate middleware extracts the payment signer pre-evaluate (`extract_payment_signer(x402_header)`) and passes `signer={address, network}` to `/v1/assess`; one round trip now carries the gate verdict AND the wallet-signer-match outcome AND the `signer_sanctions` OFAC SDN wallet-address verdict. Merchants read both back synchronously via `get_signer_verdict(request)` off the gate's cache (no extra HTTP call). **Wallet-OFAC SDN enforcement is unconditional** whenever the signer is supplied; an SDN hit OR an unavailable wallet-sanctions lookup flips the gate decision to deny before the handler runs (fail-closed; OFAC strict-liability), without requiring `policy.require_sanctions_clear` to opt in. `policy.require_sanctions_clear` is the separate NAME-based screen on the operator's KYC identity. `capture_wallet` is fire-and-forget; POSTs the signer to `/v1/credentials/wallets` so the operator's cross-merchant credential↔wallet profile builds up over time.

## Identity publishing (cross-vendor standards)

Two helpers compose AgentScore identity into the payload formats published to other agent-commerce ecosystems. Each returns an unsigned dataclass; your service signs + serves it however its key infrastructure works.

```python theme={"dark"}
from agentscore_commerce import (
    A2AAgentSkill,
    AgentScoreGatePolicy,
    StripeRailSpec,
    UCPPaymentHandlerBinding,
    UCPServiceBinding,
    UCPSigningKey,
    build_a2a_agent_card,
    build_ucp_profile,
    ucp_a2a_extension,
)

# 1. Google A2A v1.0 Signed Agent Card, published at /.well-known/agent-card.json.
#    Per UCP §A2A binding (https://ucp.dev/specification/checkout-a2a/), agents that
#    serve UCP via the A2A transport MUST declare the canonical UCP extension URI in
#    capabilities.extensions[] so platforms arriving via A2A detect UCP support
#    without re-fetching the profile. Pass an empty capabilities map when you serve
#    UCP at the discovery layer but have no formal capability bindings yet.
#    Skills declare what the agent can do; they are AgentSkill objects (top-level on
#    the card). Identity claims live in a separate AgentCardSignature (RFC 7515 JWS)
#    wrapping the serialized card, NOT in the card body itself.
card = build_a2a_agent_card(
    name="My Agent Service",
    description="Buy products via agent payments.",
    url="https://agents.example.com",
    version="1.0.0",
    skills=[
        A2AAgentSkill(
            id="purchase",
            name="Purchase",
            description="Buy products via agent payments.",
            tags=["commerce", "payment"],
        ),
    ],
    extensions=[ucp_a2a_extension()],
)

# 2. Google Universal Commerce Protocol profile, published at /.well-known/ucp
#    Output shape: {"ucp": {"version", "services", "capabilities",
#    "payment_handlers", "name?", "supported_versions?"}, "signing_keys": [...]}
#    services / capabilities / payment_handlers are MAPS keyed by reverse-DNS
#    service / capability / handler name (UCP spec §3 + §6).
profile = build_ucp_profile(
    name="My Agent Service",
    services={
        "dev.ucp.shopping": [
            UCPServiceBinding(
                version="2026-04-08",
                spec="https://ucp.dev/2026-04-08/specification/overview",
                transport="mcp",
                endpoint="https://agents.example.com/api/ucp/mcp",
                schema="https://ucp.dev/services/shopping/mcp.openrpc.json",
            ),
        ],
    },
    payment_handlers={
        **mpp_payment_handler(networks=[{"network": "tempo-mainnet", "chain_id": 4217, "recipient": TEMPO_ADDR}]),
        **x402_payment_handler(networks=[{"network": "base-8453", "recipient": BASE_ADDR}]),
        **stripe_spt_payment_handler(spec=StripeRailSpec(profile_id="profile_5xKvNqM9BaH")),
    },
    signing_keys=[UCPSigningKey(kid="me", kty="EC", alg="ES256", crv="P-256", extras={"x": "...", "y": "..."})],
    # Optional: declare merchant gate policy as a `com.agentscore.identity` capability
    # binding inside the public profile. Static policy declaration only; no per-operator
    # claims. Per-operator identity attestation flows through the AP2 risk-signal endpoint.
    agentscore_gate=AgentScoreGatePolicy(
        require_kyc=True, min_age=21, allowed_jurisdictions=["US"],
    ),
)
```

Note: ACP (Stripe + OpenAI Agentic Commerce Protocol) is a transactional checkout protocol. Not an identity-publishing surface. ACP merchants integrate through the existing `build_402_body` + `build_payment_headers` + Stripe SPT rail.

### Signing UCP profiles (vendor extension; opt-in for trust-mode verifiers)

UCP §6 doesn't mandate profile-body JWS signing; production UCP merchants commonly ship unsigned. AgentScore's `agentscore-profile+jws` is a vendor extension layered on top of the unsigned UCP profile for trust-mode verifiers (regulated-commerce, AP2-aware) that opt into auditable cryptographic provenance. Vanilla UCP agents read the canonical body and ignore the `signature` field. `agentscore-commerce` ships four helpers that ride on the `joserfc` optional dep (`pip install agentscore-commerce[ucp]`):

```python theme={"dark"}
from agentscore_commerce import (
    UCPSigningKey,
    UCPVerificationError,
    build_jwks_response,
    generate_ucp_signing_key,
    sign_ucp_profile,
    verify_ucp_profile,
)

# One-time per merchant: generate or load a key. Persist private_key in your secrets manager.
key = generate_ucp_signing_key(kid="merchant-2026-05")

# On every /.well-known/ucp request:
profile = build_ucp_profile(
    name="My Agent Service",
    services={...},  # keyed by service name, see above
    payment_handlers={...},  # keyed by handler reverse-DNS name, see above
    signing_keys=[UCPSigningKey.from_jwk(key.public_jwk)],
)
signed = sign_ucp_profile(
    profile.to_dict(),
    signing_key=key.private_key,
    kid=key.public_jwk["kid"],
    alg="EdDSA",
)
return signed

# On /.well-known/jwks.json:
return build_jwks_response([key.public_jwk])
```

`verify_ucp_profile` enforces the JWS protected header `typ='agentscore-profile+jws'` (vendor-namespaced; UCP §6 doesn't define a profile-as-JWS typ), restricts `alg` to `EdDSA`/`ES256`, requires a `kid`, rejects duplicate kids, and compares canonical body bytes against the JWS payload. Failures raise `UCPVerificationError` with a discriminated `code` attribute.

`sign_ucp_profile` rejects profiles containing `float` values: cross-language float canonicalization is not stable. Use decimal strings (e.g. `"9.99"`) for monetary or fractional fields in `extras`.

**Persisting the private JWK.** Mint once via `generate_ucp_signing_key()`, serialize via `key.private_key.as_dict(private=True)`, store in your secret manager. On each container start, read the secret, `OKPKey.import_key(jwk_dict)` (or `ECKey.import_key` for ES256) to re-hydrate. KMS-backed flows require subclassing the joserfc Key to delegate the sign hook.

**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. Drop the old JWK after your verifier-side cache TTL has elapsed.

**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 `signing_keys[]` is informational; verifiers MUST resolve the kid against the JWKS to prevent a swap-after-sign attack.

Verifiers reconstruct the canonical body (everything except `signature`, keys sorted at every level), look up the kid in JWKS, and check the JWS. `verify_ucp_profile(signed, jwks)` does this for you. Both EdDSA (Ed25519) and ES256 are supported. EdDSA is the default and recommended. Profiles signed by Node verify in Python and vice versa: cross-language byte-identical canonicalization.

## Quick start: full merchant via the `Checkout` orchestrator

`Checkout` is the canonical merchant surface in 2.0; 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, identity\_metadata auto-attach, and the per-framework adapter. Most merchants reach for `Checkout` first and only drop to lower-level helpers when they need custom flows (variable-cost streaming, multi-protocol composition).

```python theme={"dark"}
import os
from fastapi import FastAPI, Request
from agentscore_commerce import (
    Checkout, CheckoutGateConfig, DiscoveryProbeConfig, PricingResult, pricing_result,
    SolanaMppRailSpec, StripeRailSpec, TempoRailSpec, X402BaseRailSpec,
    validate_shipping_against_policy,
)
from agentscore_commerce.discovery import default_a2a_services

app = FastAPI()


async def _pre_validate(ctx):
    body = ctx.request.body or {}
    product = await lookup_product(body.get("product_slug"))
    validate_shipping_against_policy(
        country=body.get("shipping", {}).get("country", ""),
        state=body.get("shipping", {}).get("state", ""),
        policy=product,
        product_name=product["name"],
    )
    return {"product": product}


async def _compute_pricing(ctx) -> PricingResult:
    return pricing_result(
        subtotal_cents=ctx.state["product"]["price_cents"],
        tax_cents=ctx.state["product"]["tax_cents"],
        tax_rate=ctx.state["product"]["tax_rate"],
        tax_state=ctx.state["product"]["tax_state"],
    )


async def _on_settled(ctx, outcome):
    return {"ok": True, "order_id": ctx.reference_id, "tx_hash": outcome.tx_hash}


checkout = Checkout(
    rails={
        "tempo":     TempoRailSpec(recipient=os.environ["TEMPO_RECIPIENT"]),
        "x402_base": X402BaseRailSpec(recipient=os.environ["X402_BASE_RECIPIENT"], network="eip155:8453"),
        "solana_mpp":SolanaMppRailSpec(recipient=os.environ["SOLANA_RECIPIENT"], network="solana:mainnet"),
        "stripe":    StripeRailSpec(profile_id=os.environ["STRIPE_PROFILE_ID"]),
    },
    url="https://merchant.example/purchase",
    pre_validate=_pre_validate,
    compute_pricing=_compute_pricing,
    on_settled=_on_settled,
    cdp_api_key_id=os.environ.get("CDP_API_KEY_ID"),
    cdp_api_key_secret=os.environ.get("CDP_API_KEY_SECRET"),
    mppx_secret_key=os.environ.get("MPP_SECRET_KEY"),
    gate=CheckoutGateConfig(
        api_key=os.environ["AGENTSCORE_API_KEY"],
        merchant_name="Merchant",
        require_kyc=True, require_sanctions_clear=True, min_age=21, allowed_jurisdictions=["US"],
    ),
    # Optional: empty-body POSTs without a payment header auto-route to a sample 402
    # so x402 crawlers (awal x402 details, x402-proxy, ...) can discover the surface.
    discovery_probe=DiscoveryProbeConfig(
        realm="merchant.example",
        sample_rail="tempo-mainnet",
        sample_amount_usd=1.0,
        sample_recipient=os.environ["TEMPO_RECIPIENT"],
    ),
)
```

<Warning>
  **Solana MPP requires a static recipient with a pre-funded token account.** Point the Solana rail at a fixed wallet you control (as with `SOLANA_RECIPIENT` above) and pre-fund that wallet's USDC associated token account (ATA) once. Per-order rotating Solana deposit addresses (for example Stripe-multichain minted addresses) do not settle on Solana MPP, because the settlement transaction does not create the recipient's token account. Tempo and x402 (Base) settle fine to per-order recipients; only Solana needs the static, pre-funded wallet. When you mint per-order recipients via `agentscore_commerce.stripe_multichain`, pass `static_recipients={"solana": "<wallet>"}` so Solana is served from your fixed wallet, or leave Solana out of the rail set.
</Warning>

```python theme={"dark"}
# One-call mount: GET /.well-known/ucp + GET /.well-known/jwks.json + OPTIONS preflights.
# Flask / Django / aiohttp / Sanic each have a sibling helper
# (`mount_ucp_routes_flask` etc.) with the same kwargs.
checkout.mount_ucp_routes_fastapi(
    app,
    name="Merchant",
    well_known_ucp_url="https://merchant.example/.well-known/ucp",
    services=default_a2a_services(agent_card_url="https://merchant.example/.well-known/agent-card.json"),
    signing_kid="merchant-2026-05",
)


@app.post("/purchase")
async def purchase(request: Request):
    return await checkout.handle_fastapi(request)
```

The 402 body Checkout emits auto-attaches `identity_mode` + `required_signer` + `signer_constraint` (and `linked_wallets` when the gate populated them) when an inbound `X-Wallet-Address` header is present; so agents self-correct at discovery instead of at the 403 retry.

## Fail-open (opt-in)

By default AgentScore Gate fails closed on AgentScore-side infra failure (429 / 5xx / network timeout); buyer gets 503. Pass `fail_open=True` to opt in to graceful degradation, then read the per-request degraded state via `get_gate_degraded_state(request)`:

```python theme={"dark"}
from fastapi import Depends, FastAPI, Request
from agentscore_commerce.identity.fastapi import AgentScoreGate, get_gate_degraded_state

app = FastAPI()
gate = AgentScoreGate(api_key=os.environ["AGENTSCORE_API_KEY"], fail_open=True)

@app.post("/purchase", dependencies=[Depends(gate)])
async def purchase(request: Request):
    state = get_gate_degraded_state(request)
    if state["degraded"]:
        logger.warning("gate degraded: %s", state["infra_reason"])
    # ...
```

`get_gate_degraded_state` is exported by every Python adapter (FastAPI, Flask, Django, AIOHTTP, Sanic, ASGI middleware) and reads the framework-appropriate request state. Signatures take a request argument on every adapter except Flask, which reads from `g` and takes no arguments (`get_gate_degraded_state()`). Compliance denials (sanctions, age, jurisdiction, signer-mismatch) still deny regardless of `fail_open`; see [compliance-gating › Fail-open behavior](/compliance-gating#fail-open-behavior-opt-in).

## Examples

The [examples/](https://github.com/agentscore/python-commerce/tree/main/examples) directory has runnable single-file FastAPI apps for each common merchant scenario:

| Example                                                                                                                             | Scenario                                                                                                                                                                                                                                                                                                                               |
| ----------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`api_provider.py`](https://github.com/agentscore/python-commerce/blob/main/examples/api_provider.py)                               | Per-call API billing on multiple rails: Tempo MPP + x402 (Base) + Solana MPP; no compliance gate                                                                                                                                                                                                                                       |
| [`identity_only.py`](https://github.com/agentscore/python-commerce/blob/main/examples/identity_only.py)                             | Compliance gate without payment (vendor handles their own)                                                                                                                                                                                                                                                                             |
| [`multi_rail_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/multi_rail_merchant.py)                 | Full agent-commerce: identity + Tempo MPP + x402 + Stripe SPT                                                                                                                                                                                                                                                                          |
| [`stripe_multichain_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/stripe_multichain_merchant.py)   | Stripe-anchored multichain (PaymentIntent → tempo/base/solana deposit addresses)                                                                                                                                                                                                                                                       |
| [`variable_cost_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/variable_cost_merchant.py)           | Pay-per-actual-usage on **two protocols**: x402 upto + MPP tempo session                                                                                                                                                                                                                                                               |
| [`compliance_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/compliance_merchant.py)                 | Regulated-goods merchant: full compliance gate + custom `on_denied` composing the denial helpers (`verification_agent_instructions`, `is_fixable_denial`, `build_signer_mismatch_body`, `build_contact_support_next_steps`, `denial_reason_status` / `denial_reason_to_body` to assemble the (status, body) pair for any DenialReason) |
| [`per_product_policy_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/per_product_policy_merchant.py) | Multi-product merchant where each row carries its own compliance policy (hard gate, soft, or none). Uses `PolicyBlock`, `build_gate_from_policy`, `run_gate_with_enforcement`, and the one-call `validate_shipping_against_policy`.                                                                                                    |
| [`signed_ucp_merchant.py`](https://github.com/agentscore/python-commerce/blob/main/examples/signed_ucp_merchant.py)                 | Signed UCP profile + JWKS endpoint via the one-call `checkout.mount_ucp_routes_fastapi(app, ...)`. Demonstrates ephemeral-for-dev / env-JWK-for-prod and `bootstrap_ucp_signing_key` lifespan-hook usage.                                                                                                                              |

## Links

* [PyPI](https://pypi.org/project/agentscore-commerce/)
* [GitHub](https://github.com/agentscore/python-commerce)
