Skip to content

feat(rest/python): implement cart capability and discount extension - #159

Open
damaz91 wants to merge 11 commits into
Universal-Commerce-Protocol:mainfrom
damaz91:feat/cart-implementation
Open

feat(rest/python): implement cart capability and discount extension#159
damaz91 wants to merge 11 commits into
Universal-Commerce-Protocol:mainfrom
damaz91:feat/cart-implementation

Conversation

@damaz91

@damaz91 damaz91 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Description

Implement the Cart capability (dev.ucp.shopping.cart) and the Cart Discount extension (dev.ucp.shopping.discount) in the Python REST sample server.

Key changes:

  • Cart Capability & Cart Discount Support:
    • Created CartService for managing cart lifecycle (create, get, update, cancel) and calculating estimated totals.
    • Updated models.py with UnifiedCart, UnifiedCartCreateRequest, and UnifiedCartUpdateRequest to support the discount extension fields on cart.
    • Added comprehensive integration tests in cart_test.py covering cart CRUD operations and discount application.
  • Cart-to-Checkout Conversion:
    • Integrated conversion flow in CheckoutService: inherits all cart data (line items, buyer, context, signals, attribution, and discounts) and deletes the cart session upon successful checkout completion.
    • Enforced idempotency: returns existing incomplete checkout if one already exists for the given cart_id.
    • Simplified client payload: updated UnifiedCheckoutCreateRequest (models.py) to make line_items optional, and added a model validator enforcing that either cart_id or line_items is provided. This allows clients to convert by sending only the cart_id as per the UCP specification.
    • Refactored variable initialization in CheckoutService.create_checkout to handle missing optional fields safely.
  • Client Simulator & Walkthrough:
    • Refactored simple_happy_path_client.py to execute the full Cart-to-Checkout flow, verifying the conversion with a minimal payload.
    • Fixed the client to omit the id field for new line items during updates, ensuring it only sends known IDs or leaves them unset, conforming to UCP specifications.
  • Discovery Profile & Payment Handlers:
    • Updated discovery_profile.json to expose the cart capability and advertise that the dev.ucp.shopping.discount capability extends both checkout and cart.
    • Populated ucp.payment_handlers in the checkout response (services/checkout_service.py) with default supported handlers loaded from the discovery profile via a new cached helper in config.py. This ensures full compliance with the required fields in the response schema.
  • Refactoring:
    • Renamed _recalculate_totals to _enrich_and_recalculate in both cart_service.py and checkout_service.py to clarify that the method performs product details enrichment from the database (like updating titles and prices) before computing totals.
  • Database:
    • Fixed SQLite JSON query matching in db.py by using .as_string() on the JSON index to avoid quote mismatch during cart lookup.

Category (Required)

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Governance/Contributing: Updates to GOVERNANCE.md, CONTRIBUTING.md, or CODEOWNERS. (Requires Governance Council approval)
  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)
  • Infrastructure: CI/CD, Linters, or build scripts. (Requires DevOps Maintainer approval)
  • Maintenance: Version bumps, lockfile updates, or minor bug fixes. (Requires DevOps Maintainer approval)
  • SDK: Language-specific SDK updates and releases. (Requires DevOps Maintainer approval)
  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)
  • UCP Schema: Changes to the ucp-schema tool (resolver, linter, validator). (Requires Maintainer approval)
  • Community Health (.github): Updates to templates, workflows, or org-level configs. (Requires DevOps Maintainer approval)

Related Issues

Fixes #134

Checklist

  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk.

@damaz91 damaz91 changed the title feat(samples): implement cart capability and discount extension feat(rest/python): implement cart capability and discount extension Aug 3, 2026

@vishkaty vishkaty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested this end-to-end — it holds up very well. Since this is the reference implementation for a released capability, I ran it rather than just reading it:

  • Full suite on the PR head: 165 passed (131 at the merge-base — the delta is the new cart test file; no regressions in the checkout/webhook/signature tests).
  • On the wire (server seeded with the flower-shop data): full cart lifecycle works — create/get/update/cancel, 10OFF applied as totals: [subtotal 3500, discount −350, total 3150] with a correct allocations entry, and conversion via cart_id only correctly inherits the cart's line items and discounts, populates payment_handlers, is idempotent (a second create with the same cart_id returns the same checkout), and clears the cart after /complete. The happy-path client runs the whole flow green.
  • Schema-validated every response against the official v2026-04-08 schemas (pinned at a2d8bf0): cart responses vs shopping/cart.json + the discount extension, conversion checkouts vs checkout.json + cart.json#/$defs/checkout, and /.well-known/ucp vs ucp.json#/$defs/business_schema — all pass. Nice touch getting the discount total negative, which total.json's conditional (exclusiveMaximum: 0) requires and which is easy to miss.
  • Edge behavior is right too: 409 on idempotency-key reuse with a different body, 400 with the UCP error envelope for unknown products, 422 when neither cart_id nor line_items is provided.

One logistical note from actually performing the rebase: the conflict with main is in create_checkout, where #167's server-assigned checkout id collides with the checkout_id = getattr(checkout_req, "id", ...) line inside the new cart-initialization block. Resolving in favor of the server-assigned id is mechanical, but the two cart-conversion tests then need a matching update — they assert client-chosen checkout ids, which #167 now (correctly) forbids. The cart-clearing in complete_checkout merges cleanly alongside #169's webhook dispatch.

This also closes #134 as scoped there (lifecycle routes, discovery declaration, conversion semantics per cart.md, tests in the existing style — plus the discount extension beyond it), and it gives cart conformance coverage something real to run against, which the ecosystem has been missing. Great to see this land.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat]: implement the Cart capability in the Python sample server

3 participants