feat: commerce 2.0.0 — Checkout orchestrator + per-framework adapters + signed UCP routes - #49
Merged
Merged
Conversation
…1 lift B)
Collapses the universal goods-merchant boilerplate
if not shipping_country_allowed(country, policy):
raise CheckoutValidationError(code='unsupported_jurisdiction', ...)
if not shipping_state_allowed(state, country, policy):
raise CheckoutValidationError(code='unsupported_jurisdiction', ...)
into one helper call. Used by per_product_policy_merchant example +
core/store/purchase.py + martin-estate (each currently inlines the same
2-check pattern in preValidate).
Default messages are neutral (don't assume regulatory reason - could be
operational / commercial). country_message / state_message override verbatim.
error_code / error_action override the canonical denial codes.
Tests: 7 new cases (null policy, no-allowlist, country deny, state deny,
product_name in message, custom messages, custom code/action). Suite green
at 95.13 percent coverage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collapses the universal US-commerce boilerplate
block = build_pricing_block(subtotal_cents=..., tax_cents=..., ...)
return PricingResult(amount_usd=total_cents/100, block=block, ...)
into one helper call. Used by core/store, martin-estate, and the
multi_rail_merchant example (each currently inlines the same dance in
compute_pricing).
- subtotal_cents + tax_cents + shipping_cents derive amount_usd.
- tax_rate / tax_state attach to the block for the 402 body.
- Passthrough mode (amount_usd only) for API merchants with no tax.
- Explicit amount_usd overrides the cents-derived value when both passed.
Tests: 7 cases. pricing_result is also re-exported at the top level.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ft D) When a Checkout is constructed with discovery_probe=DiscoveryProbeConfig(...), any empty-body POST without a payment header short-circuits with a sample 402 advertising the merchant's payment shape. Saves vendors the is_discovery_probe_request + build_discovery_probe_response dance every crawler-friendly merchant repeats. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 wrappers (fastapi, flask, django, aiohttp, sanic) that convert the
framework-neutral SignedDiscoveryResponse / WellKnownPreflightResponse into
the framework's native Response. Saves merchants the 4-line
Response(content=..., media_type=..., headers=..., status_code=...) shim
they otherwise hand-roll on every /.well-known/{ucp,jwks.json} route.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds mount_ucp_routes_{fastapi,flask,django,aiohttp,sanic} on the Checkout
class. Each registers GET /.well-known/ucp + GET /.well-known/jwks.json + an
OPTIONS preflight for both, in one call. Saves merchants the ~40-line 3-route
registration block every UCP-publishing merchant otherwise hand-rolls.
FastAPI handlers patch __annotations__ post-hoc so the Request injection
survives `from __future__ import annotations` stringification.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- api_provider.py: Checkout(discovery_probe=...) replaces inline probe routing - per_product_policy_merchant.py: validate_shipping_against_policy replaces shipping_country_allowed + shipping_state_allowed pair - multi_rail_merchant.py: pricing_result() replaces build_pricing_block + body_extras dance - signed_ucp_merchant.py: Checkout.mount_ucp_routes_fastapi(...) replaces 3-route hand-mount Net: ~80 lines removed across examples; the underlying helpers carry the boilerplate now. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… universal copy - Kill handle_starlette = handle_fastapi alias (FastAPI inherits from Starlette; one adapter already covers both). - agent_instructions warnings + llms_txt: "order will not complete" → "request will not complete" (universal across goods + API merchants). - build_success_next_steps default: "Order complete." → "Payment complete." - build_contact_support_next_steps default: "with your order details" → "with the details of your request." No behavioural change. All helpers continue to accept caller overrides; only defaults changed so API-merchant consumers stop having to override every call. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Five-change wave focused on universalizing surface for both goods + API
merchants and on auto-attaching body-piece helpers from the orchestrator.
1. Rename order_receipt.py → receipt.py; OrderReceipt → Receipt;
OrderProductInfo → ProductInfo; OrderNextSteps → ReceiptNextSteps. Goods-only
slots (shipping, tracking_number, fulfillment_status, gift_note) stay
optional; docstrings explicitly tag goods-only vs universal fields. Zero
consumers used the old names so no consumer migration needed.
2. standard_endpoint_descriptions takes a kind: Literal["goods", "api"]
parameter; ships separate canonical bundles. Goods bundle stays current
(/catalog, /purchase, /orders/{id}); API bundle is /<endpoint> + /usage.
3. build_redemption_skill_md prose rewritten to be delivery-neutral (printed
mailers, emailed codes, in-app, API trial credits all covered). Adds
endpoint_path, delivery_intro, body_shape, body_rules, extra_recovery_rows
params so API merchants can pass non-goods shapes without rewriting the
whole template.
4. Wire build_identity_metadata into Checkout._emit_402 via
_resolve_identity_metadata: when X-Wallet-Address header is present, the
402 body now advertises identity_mode/required_signer/signer_constraint
(and linked_wallets when the gate populated request.assess). Agents
self-correct at discovery instead of at the 403 retry.
5. (No-op) Keep createMppxStripe rewrap as-is per design discussion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two routes to the API-merchant example so the new wave-2 surface is visible end-to-end: - GET /: discovery root using build_merchant_index_json + standard_endpoint_descriptions(kind="api"). Lists the per-call endpoint, supported rails, pricing, and a pointer to /redemption.md. - GET /redemption.md: build_redemption_skill_md with API-trial-credit body shape (query + redemption_code), delivery_intro framed for developer onboarding emails, body_rules dropped (no shipping for API merchants). Demonstrates how the same single-use code pattern that powers martin's printed mailers also covers API trial credits, promo codes, and any other out-of-band code distribution channel. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_rail
Two changes:
1. Cross-language parity fix: kill get_deposit_address (function +
__init__ export + tests + example call site). Wave 1 killed the node
equivalent but missed the python copy. Same rationale: trivial
`result.deposit_addresses.get(network)` dict lookup; no value-add as
a separate helper.
2. Demonstrate Receipt + ReceiptNextSteps + build_success_next_steps in
multi_rail_merchant._on_settled so the renamed canonical type has a
visible consumer. Also added `action` field to ReceiptNextSteps so
build_success_next_steps output spreads cleanly into the dataclass
(the helper emits {action: "done", user_message, order_status_url}
and ReceiptNextSteps now matches that shape verbatim).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three doc files refreshed to match the wave-1 + wave-2 SDK changes (parity with the node-commerce doc refresh): - README.md: add Checkout orchestrator quickstart (the 2.0 high-level surface that was missing); refresh helper tables (top-level adds Checkout + pricing_result + validation_response_* + Receipt types; identity adds validate_shipping_against_policy; discovery adds build_signed_ucp_response, build_signed_jwks_response, signed_response_*, build_merchant_index_json, standard_endpoint_descriptions, build_success_next_steps, build_agentscore_onboarding_steps, build_redemption_skill_md, well_known_preflight_response, default_a2a_services, bootstrap_ucp_signing_key; challenge: Receipt rename + identity_metadata auto-attach note; stripe_multichain: drop get_deposit_address); fix stale stripe code example to read deposit_addresses[network] directly; OrderReceipt → Receipt prose update. - CLAUDE.md: top-level row added covering Checkout + pricing_result + validation_response_* + Receipt types; same helper-table refresh as README; identity.policy row added with validate_shipping_against_policy. - examples/README.md: per-example "What it shows" descriptions updated to reflect the helpers each one was migrated to (Checkout, discovery_probe, build_merchant_index_json, standard_endpoint_descriptions(kind="api"), build_redemption_skill_md, pricing_result, Receipt + build_success_next_steps, mount_ucp_routes_fastapi, validate_shipping_against_policy). Add signed_ucp_merchant.py row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e __init__ Wave-1 added validate_shipping_against_policy to identity.policy module but the wave-2 audit caught that identity/__init__.py never re-exported it. Means \`from agentscore_commerce.identity import validate_shipping_against_policy\` fails at runtime even though the helper exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PricingBlock + build_pricing_block + pricing_result now accept discount_cents. When supplied, subtotal stays at list price, discount surfaces as a dollar- string, total = subtotal + tax + shipping - discount (floored at 0). Agents reading 402 challenges see the savings line instead of subtotal=0. Bumps (transitive via uv.lock): - agentscore-py 2.3.1 -> 2.3.2 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
asyncio is already module-level imported (line 16); the inner import in test_mount_ucp_routes_aiohttp_registers_three_routes shadowed it for no reason. Caught by github-code-quality bot on PR #49. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
multi-rail/stripe-multichain/variable-cost - identity_only.py: add create_session_on_missing + capture_wallet + public route to match node's teaching depth. - multi_rail_merchant.py: replace _create_multichain_payment_intent stub with the real create_multichain_payment_intent helper + pi_cache writes. - stripe_multichain_merchant.py: rename POST /buy -> POST /checkout, add the 3-network instructions block (matches node prose). - variable_cost_merchant.py: swap raw 402 dicts for build_402_body + build_accepted_methods + build_agent_instructions + build_how_to_pay + build_pricing_block; wire create_x402_server/create_mppx_server hooks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the linter-placation assignment with an explicit __all__ so _boot_x402_server + create_mppx_server are documented as reference imports for vendors wiring real servers. Caught by github-code-quality bot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Brings python's package-root surface in line with node's index.ts. Vendors
who switch SDKs now find the same A2A/UCP types at agentscore_commerce.*
without having to reach into agentscore_commerce.identity.
Re-exported: A2AAgentCard{,Capabilities,Extension,Signature},
A2AAgentInterface, A2AAgentProvider, A2AAgentSkill, UCPCapabilityBinding,
UCPPaymentHandlerBinding, UCPProfile, UCPProfileBody, UCPServiceBinding,
GeneratedUCPKey. Submodule imports continue to work for power users.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds build_gate_from_policy, run_gate_with_enforcement, shipping_country_allowed, shipping_state_allowed, validate_shipping_against_policy, EnforcementMode, GateResult, IdentityStatus, PolicyCheck, PolicyResult to agentscore_commerce.* Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…obals Stub env vars for the streaming SSE handler (501-stubbed today). Reference them in a comment instead. Caught by github-code-quality bot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use agentscore_commerce top-level for symbols that are now re-exported there. Submodule imports stay only for symbols that aren't top-level (networks, validate_x402_network_config, create_*_server, payment-protocol helpers, framework-specific gates / discovery / stripe_multichain). Matches node-commerce. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_profile Last submodule-import-with-top-level-equivalent. Matches node. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The identity package's _load_asgi_middleware fallback caused agentscore_commerce.CreateSessionOnMissing to resolve to None at runtime. Import directly from agentscore_commerce.identity.sessions (the canonical home) at the top-level __init__ so the symbol is always usable. Also flatten examples/identity_only.py to import CreateSessionOnMissing from the top level — now possible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Quick-start: RailSpec types + validate_shipping_against_policy via top-level - Payment helpers section: extract_payment_signer via top-level Remaining /payment + /identity.policy imports are submodule-only helpers (build_payment_directive, networks, build_x402_accepts_for_402, etc.). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The 2.0.0 release of `agentscore-commerce`. Adds the `Checkout` orchestrator (one config object + hooks), per-framework adapters (`handle_fastapi` / `handle_flask` / `handle_django` / `handle_aiohttp` / `handle_sanic`), signed UCP routes via `mount_ucp_routes_{fastapi,flask,django,aiohttp,sanic}`, optional `discovery_probe` config for x402-crawler auto-routing, and a canonical `Receipt` dataclass universal across goods + API merchants.
Wave-1 lifts: `pricing_result`, `validate_shipping_against_policy`, `DiscoveryProbeConfig`, `signed_response_`, `Checkout.mount_ucp_routes_`.
Wave-2: `OrderReceipt` → `Receipt` rename, `standard_endpoint_descriptions(kind='goods'|'api')`, `build_redemption_skill_md` universal prose, `build_identity_metadata` auto-attach in `Checkout._emit_402`.
Recent additions on this branch:
Test plan
🤖 Generated with Claude Code