-
Notifications
You must be signed in to change notification settings - Fork 207
feat(cart): Cart capability for basket building #73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feat-context
Are you sure you want to change the base?
Conversation
Currency as client input is flawed - merchants control accepted
currencies, not buyers. Allowing "show me price in $X" implies forex
operations that break at payment time when the merchant's actual accepted
currency differs.
Correct model: buyer provides context signals, merchant determines currency
and other market-relevant attributes such as product availability,
shipping and delivery times, price, etc.
Changes:
- Add extensible Context type with country, region, postal_code for
progressive disclosure
- Add context to checkout (optional on create/update, omit on complete)
- Make currency output-only (omit on all operations)
This primitive will be relevant for catalog, cart, and checkout.
Doc generator previously required every $ref property to duplicate the referenced type's description. This violated DRY - same description copied everywhere, diverging over time. New behavior: - Embedder provides description → use it (custom/contextual) - Embedder omits description → inherit from ref'd type JSON Schema allows omitting description, so embedders can now choose: override with context-specific text, or inherit the canonical definition.
Checkout models buying: payment handlers, status lifecycle, order
finalization. Cart models shopping: browsing, comparing, saving items
for later, all without payment commitment.
Key differences from Checkout:
- No payment configuration required
- Binary state (exists/404) vs lifecycle status
- Estimated totals vs final pricing
- checkout_url for sharing/handoff vs continue_url
The typical flow: cart session → checkout session → order
Includes:
- Cart schema (dev.ucp.shopping.cart) with CRUD operations
- REST binding: POST/GET/PUT /carts, POST /carts/{id}/cancel
- MCP binding: create_cart, get_cart, update_cart, cancel_cart tools
- Cart-to-checkout conversion via cart_id parameter
- Full input/output schemas with request payload wrappers
16f071e to
c04ecb2
Compare
| order finalization, cart provides a lightweight CRUD interface for item | ||
| collection before purchase intent is established. | ||
|
|
||
| **When to use Cart vs Checkout:** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m having trouble understanding why we need Cart capability here. It seems more like a Checkout in draft status and make payment (it is actually already supporting empty object) optional during the Checkout creation?
raginpirate
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing how such a complex primitive like cart is suddenly "simple" because it inherits all of these build blocks from checkout 🔥
I added a few small nits, I'm really interested in having a dialogue about payments <-> cart, I'm thinking its worth keeping handlers around. Also interested in seeing fulfillment extend cart!
|
|
||
| * **MUST** provide `checkout_url` in all cart responses. | ||
| * **SHOULD** provide estimated totals when calculable. | ||
| * **MAY** omit shipping totals until checkout when address is unknown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| * **MAY** omit shipping totals until checkout when address is unknown. | |
| * **MAY** omit fulfillment totals until checkout when address is unknown. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has opened a series of questions for me:
- Looks like we need changes to the fulfillment capability to extend cart 🤔
- or... would we express this as an extension on the base fulfillment capability and cart capability simultaneously?
- And if so... how do we express the dependency graph? At the moment we allow one parent <-> child. Do we care to express this?
| * **SHOULD** follow [cart lifecycle requirements](#cart-to-checkout-conversion) | ||
| when checkout is initialized via `cart_id`. | ||
|
|
||
| ## Capability Schema Definition |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ## Capability Schema Definition | |
| ## Cart Schema Definition |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this title I was thinking I was going to be reading the config schema, so just adding some clarity to wording.
| | Aspect | Cart | Checkout | | ||
| |--------|------|----------| | ||
| | **Purpose** | Pre-purchase exploration | Purchase finalization | | ||
| | **Payment** | None | Required (handlers, instruments) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Challenge: if we're ok with fulfillment extending cart to build fulfillment estimates, shouldn't we be ok with payment handlers being negotiated and pruned down based on cart shape, to assist with accelerated checkouts from cart?
I'm fine without instruments though, we don't need store credit / vaulted cards surfacing through cart.
| ## Entities | ||
|
|
||
| Cart reuses the same entity schemas as [Checkout](checkout.md). This ensures | ||
| consistent data structures when converting a cart to a checkout session. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This description is good, but adding a table in this page for every existing checkout entity feels like a lot of repetition and makes me feel like maybe there is going to be a discrepancy in the descriptions or something.
My proposal: turn this into a simple list:
- line items
- buyer
- context
- total
- message
With links to the checkout page for each.
|
|
||
| ## Protocol Fundamentals | ||
|
|
||
| ### Base URL |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put up a PR this morning to add a Discovery section on the Checkout REST docs, matching the equivalent section in the MCP docs: https://github.com/Universal-Commerce-Protocol/ucp/pull/82/changes. I think the same change would be useful here, as the same problem (the ucp.dev schema URL for REST is not provided anywhere on the page) applies to this new Cart REST page.
| "description": "Optional merchant links (policies, FAQs).", | ||
| "ucp_request": "omit" | ||
| }, | ||
| "checkout_url": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider calling this continue_url for symmetry with Checkout? I personally find this name a little confusing, since the API endpoint is really the "checkout URL" for many UCP implementers.
| } | ||
| }, | ||
| "type": "object", | ||
| "required": ["ucp", "id", "line_items", "currency", "totals", "checkout_url"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
continue_url is optional on Checkout. Is there a reason checkout_url needs to be mandatory for Carts? I assumed it covered similar "human in the loop" flows, and would therefore have the same optionality in the schema as continue_url.
| * **During active checkout** — Business SHOULD maintain the cart and reflect | ||
| relevant checkout modifications (quantity changes, item removals) back to | ||
| the cart. This supports back-to-storefront flows when buyers transition | ||
| between checkout and storefront. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should a business do if multiple checkouts are created from the same cart?
| } | ||
| } | ||
| }, | ||
| "/carts/{id}/cancel": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider making this a DELETE instead, since there is no Checkout-style status=cancelled, and since we seem to be trying to express "it exists or it doesn’t" semantics for Carts? There are a few places where we have to say something like, "doesn’t exist, has expired, or was cancelled" to describe the 404 case, which I assume could also be simplified a bit if we treated this as a DELETE.
Summary
Cart capability (
dev.ucp.shopping.cart) enables pre-purchase exploration without checkout complexity. Checkout models buying — payment handlers, status lifecycle, order finalization. Cart models shopping — browsing, comparing, saving items for later, all without payment commitment.Motivation
Platforms need a lightweight way to build baskets before purchase intent is established — session persistence, sharing, and exploratory workflows without the checkout contract. Checkout operations are expensive for businesses (payment handler negotiations, 3P calls, tax calculations with full address resolution, etc), and often subject to strict rate limits. Carts address this via lower-fidelity policies that accommodate efficient pre-purchase basket building and ability to service much higher volumes.
Goals
cart_idparameterNon-Goals
Detailed Design
Key differences from Checkout:
checkout_urlcontinue_urlAPI Operations:
POST /cartscreate_cartGET /carts/{id}get_cartPUT /carts/{id}update_cartPOST /carts/{id}/cancelcancel_cartCart-to-Checkout Conversion:
When checkout is initialized via
cart_id, sessions SHOULD be linked for checkout duration. Business SHOULD maintain cart for back-to-storefront flows. After checkout completion, business MAY clear cart.Schema:
cart_idfield on checkout (via schema extension) for conversionresponse_cartadded to ucp.json for cart response metadataUpdate Semantics: Full replacement (mirrors checkout)
Cancel Behavior: Business MUST return cart state before deletion. Subsequent operations SHOULD return 404.
Tax Guidance: Taxes MAY be included where calculable. Platforms SHOULD assume cart totals are estimates when provided.
Risks and Mitigations
expires_at. Explicitcancel_cartfor privacy.$ref), ensuring consistency.Checklist: