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_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). 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), Receipt/ReceiptNextSteps/ProductInfo/ShippingAddress (canonical 200-receipt dataclasses, universal across goods + API merchants), 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 + LoadUCPSigningKeyOptions (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 (v1↔v2 amount field shim for v1-only x402 parser compat), 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.30feemakessub−50−centchargesunprofitable—sub−50−centAPIspass‘includestripe=False‘explicitly),‘isevmnetwork‘/‘issolananetwork‘(CAIP−2discriminators),‘haspaymentheader‘(settle−legdiscriminator);drop−inx402helpers:‘validatex402networkconfig‘(boot−timeguard),‘verifyx402request‘(parse+validateinboundX−Payment),‘processx402settle‘(verify−then−settleinonecall),‘classifyx402settleresult‘(mapsthetaggedsettleresulttoarecommendedHTTPstatus/code/nextstepssomerchantsgetacontrolledenvelopewithoutcouplingtofacilitator−specificerrortext),‘classifyorchestrationerror‘(same‘ClassifiedX402Error‘shapebutforuncaughtexceptionsthrownelsewhereintheorchestration;conservativesubstringfallbackreturns‘None‘forunknownerrorssomerchantsrethrowinsteadofswallowing);‘zeroamountcarveout‘(skipCDP/pymppupstreamverify+settlefor0 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, 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. |