Skip to content

Commit a1590df

Browse files
authored
feat(checkout): add Checkout orchestrator for one-call agent-commerce flow (#45)
## Summary High-level orchestrator that collapses 402-emit + verify+settle into a single `await checkout.handle()` call. Services every merchant shape via the same primitive: | Axis | Options | |---|---| | Rail mix | x402-only / MPP-only / both | | Custody | self-custody (chain) / Stripe (custodial) / mixed | | Identity | gated (AgentScoreCore upstream) / ungated | | Use case | goods seller (per-order) / API seller (per-call) | ## Common usage **Goods seller** (full agent-commerce): ```python checkout = Checkout( rails={ "tempo": TempoRailSpec(recipient=...), "x402_base": X402BaseRailSpec(recipient=...), "stripe": StripeRailSpec(profile_id=...), }, url=APP_URL, compute_pricing=lambda ctx: PricingResult(amount_usd=cart_total(ctx.body)), mint_recipients=lambda ctx: stripe_multichain_addresses_for(ctx), on_settled=lambda ctx, outcome: persist_order(ctx.reference_id, ctx.body, outcome), compose_mppx=lambda ctx: mppx_compose(mppx, ctx.request), x402_server=x402, ) ``` **API seller** (per-call x402): ```python checkout = Checkout( rails={"x402_base": X402BaseRailSpec(recipient=TREASURY)}, url=APP_URL, compute_pricing=lambda ctx: PricingResult(amount_usd=0.01), on_settled=lambda ctx, _: {"data": await run_api(ctx.body)}, x402_server=x402, ) ``` ## Design notes - **Framework-neutral**: `handle()` takes `CheckoutRequest`, returns `CheckoutResult` (body + headers + status + reference_id + settled). Merchant wraps in their framework's response. - **Domain-neutral**: `reference_id` is a UUID; goods merchants persist as order id, API merchants treat as request id. - **x402 base network is auto-derived** from `rails["x402_base"].network` — no duplicate kwarg. - **`CheckoutRequest.raw`** is an escape hatch for `compose_mppx` hooks that need the framework's native request object. - **Flattening rule respected**: `__init__` is flat-kwargs with `*` keyword-only marker; all dataclasses are value types (data shapes for inputs/outputs), not parameter-bundle wrappers. ## Test plan - [x] `pytest` green (1076 tests pass; coverage 95.17%) - [x] `ty check` clean - [x] `ruff check` clean - [x] `vulture` clean - [x] 12-test matrix in `tests/test_checkout.py` covers every flexibility axis
1 parent 7ec9ef6 commit a1590df

3 files changed

Lines changed: 967 additions & 0 deletions

File tree

agentscore_commerce/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,40 @@
66
agentscore_commerce.discovery - probe + .well-known/mpp.json + llms.txt + OpenAPI snippets
77
agentscore_commerce.challenge - 402-body builders
88
agentscore_commerce.stripe_multichain - multichain PaymentIntent helpers
9+
agentscore_commerce.checkout - high-level Checkout orchestrator
910
agentscore_commerce.api - AgentScore SDK re-export
1011
"""
1112

1213
from importlib.metadata import PackageNotFoundError
1314
from importlib.metadata import version as _pkg_version
1415

16+
from agentscore_commerce.checkout import (
17+
Checkout,
18+
CheckoutContext,
19+
CheckoutRailSpec,
20+
CheckoutRequest,
21+
CheckoutResult,
22+
MppxComposeOutcome,
23+
PricingResult,
24+
SettleOutcome,
25+
)
26+
1527
try:
1628
__version__ = _pkg_version("agentscore-commerce")
1729
except PackageNotFoundError:
1830
# Editable install or pre-build state — fall back to a sentinel so consumers
1931
# don't crash on a missing dist-info dir. Real version always comes from
2032
# pyproject.toml at install time.
2133
__version__ = "0.0.0+local"
34+
35+
__all__ = [
36+
"Checkout",
37+
"CheckoutContext",
38+
"CheckoutRailSpec",
39+
"CheckoutRequest",
40+
"CheckoutResult",
41+
"MppxComposeOutcome",
42+
"PricingResult",
43+
"SettleOutcome",
44+
"__version__",
45+
]

0 commit comments

Comments
 (0)