Commit a1590df
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 axis1 parent 7ec9ef6 commit a1590df
3 files changed
Lines changed: 967 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
15 | 27 | | |
16 | 28 | | |
17 | 29 | | |
18 | 30 | | |
19 | 31 | | |
20 | 32 | | |
21 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
0 commit comments