diff --git a/docs/specification/getting-started.md b/docs/specification/getting-started.md new file mode 100644 index 000000000..8b7caaeaa --- /dev/null +++ b/docs/specification/getting-started.md @@ -0,0 +1,921 @@ +--- +description: Getting started guide for building UCP servers in Python and Node.js. +--- + + + +# Getting Started with UCP + +This guide will walk you through building a basic Universal Commerce Protocol (UCP) server. We provide examples for both **Python (FastAPI)** and **Node.js (Express)** using the official [Python SDK](https://github.com/Universal-Commerce-Protocol/python-sdk) and [TypeScript SDK](https://github.com/Universal-Commerce-Protocol/js-sdk). + +We will implement a simple checkout server that allows a platform to initiate a checkout session and retrieve it. + +## Prerequisites + +=== "Python" + + * Python 3.10 or higher installed. + * Basic familiarity with FastAPI and Pydantic. + * We recommend using [`uv`](https://docs.astral.sh/uv/) for package management. + +=== "Node.js" + + * Node.js 18 or higher installed. + * Basic familiarity with Express and Zod. + +--- + +## Project Setup + +=== "Python" + + Create a new directory and initialize the project: + + ```bash + mkdir ucp-quickstart-python + cd ucp-quickstart-python + uv init + ``` + + Add the required dependencies. [`ucp-sdk`](https://github.com/Universal-Commerce-Protocol/python-sdk) contains the Pydantic models generated from UCP schemas: + + !!! note + The Python SDK package is registered as `ucp-sdk` on PyPI, but is imported as `ucp_sdk` (with an underscore) in your Python code. + + ```bash + uv add fastapi uvicorn ucp-sdk + ``` + +=== "Node.js" + + Create a new directory and initialize the project: + + ```bash + mkdir ucp-quickstart-nodejs + cd ucp-quickstart-nodejs + npm init -y + ``` + + Configure your `package.json` to use ES Modules by adding `"type": "module"`. + + Add the required dependencies. [`@ucp-js/sdk`](https://github.com/Universal-Commerce-Protocol/js-sdk) contains the TypeScript types and Zod schemas: + + ```bash + npm install express uuid @ucp-js/sdk + npm install --save-dev typescript @types/express @types/node tsx + ``` + + Initialize TypeScript configuration: + + ```bash + npx tsc --init + ``` + +--- + +## Implementing the Server + +We will implement the server step-by-step. The server needs to handle: + +1. **Create Checkout (`POST /checkout-sessions`)**: Receives desired items and returns a checkout session with totals and available payment handlers. +2. **Get Checkout (`GET /checkout-sessions/{id}`)**: Allows the platform to poll the session status. + +### High-Level Flow + +Here is how the components interact during the checkout process: + +```mermaid +%%{init: {'sequence': {'actorMargin': 90, 'messageMargin': 45}}}%% +sequenceDiagram + autonumber + participant Platform as "Platform" + participant Server as "Checkout Server (Your App)" + participant DB as "In-Memory DB" + + Note over Platform,Server: Create Checkout Session + Platform->>+Server: POST /checkout-sessions (with Items and Headers) + Note over Server: Validate, Calculate Totals, and Advertise Payment Handlers + Server->>DB: Store Session + Server-->>-Platform: 201 Created (Checkout Response) + + Note over Platform,Server: Retrieve Checkout Session (Polling) + Platform->>+Server: GET /checkout-sessions/{id} + Server->>DB: Fetch Session + Server-->>-Platform: 200 OK (Checkout Response) +``` + +### How it fits into the E2E Flow + +In a complete UCP integration, the flow follows these phases: + +1. **Discovery:** The Platform discovers your UCP endpoints (like `/checkout-sessions`) by fetching your UCP Discovery Profile at `/.well-known/ucp`. +2. **Create Checkout (Implemented in this guide):** The Platform initiates the checkout session with the items the user wants to buy. +3. **Update Checkout:** The Platform updates the checkout session with buyer details (e.g., shipping address, email) to calculate final taxes and shipping options. +4. **Complete Checkout:** The Platform submits the payment credentials to finalize the order. + +This guide focuses on step 2 (Create Checkout) and the subsequent retrieval of the checkout session state. + +### 1. Imports and Setup + +Initialize the application and define an in-memory database to store sessions. + +=== "Python" + + ```python + # main.py + import uuid + from typing import Annotated + from fastapi import FastAPI, Header, HTTPException, status + + # Import UCP SDK models + from ucp_sdk.models.schemas.ucp import ResponseCheckoutSchema + from ucp_sdk.models.schemas.shopping.checkout import Checkout + from ucp_sdk.models.schemas.shopping.checkout_create_request import CheckoutCreateRequest + from ucp_sdk.models.schemas.shopping.types.line_item import LineItem + from ucp_sdk.models.schemas.shopping.types.item import Item + from ucp_sdk.models.schemas.common.types.totals import Total + from ucp_sdk.models.schemas.common.types.link import Link + from ucp_sdk.models.schemas.common.types.available_payment_instrument import AvailablePaymentInstrument + from ucp_sdk.models.schemas.payment_handler import ResponseSchema as PaymentHandlerResponse + + # Initialize FastAPI app + app = FastAPI(title="UCP Quickstart Server") + + # Simple in-memory database + checkout_sessions = {} + ``` + +=== "Node.js" + + ```typescript + // server.ts + import express from 'express'; + import { v4 as uuidv4 } from 'uuid'; + + // Import validation schemas from JS SDK + import { + CheckoutCreateRequestSchema, + CheckoutResponseSchema, + type CheckoutResponse + } from '@ucp-js/sdk'; + + // Initialize Express app + const app = express(); + app.use(express.json()); + + // Simple in-memory database + const checkoutSessions: Record = {}; + ``` + +### 2. Create Checkout Endpoint (Route & Header Validation) + +Define the endpoint to create a checkout session. UCP requires `Idempotency-Key` and `UCP-Agent` headers. + +=== "Python" + + ```python + # main.py + @app.post( + "/checkout-sessions", + response_model=Checkout, + status_code=status.HTTP_201_CREATED, + response_model_exclude_none=True + ) + async def create_checkout( + body: CheckoutCreateRequest, + idempotency_key: Annotated[str, Header(alias="Idempotency-Key")], + ucp_agent: Annotated[str, Header(alias="UCP-Agent")] + ): + """Create a new UCP checkout session.""" + # Note: In a production environment, you must use the Idempotency-Key + # to prevent duplicate processing of the same request. + + # Generate a unique checkout session ID + session_id = f"chk_{uuid.uuid4().hex[:10]}" + ``` + +=== "Node.js" + + ```typescript + // server.ts + app.post('/checkout-sessions', (req, res) => { + // 1. Validate required UCP headers + const idempotencyKey = req.header('Idempotency-Key'); + const ucpAgent = req.header('UCP-Agent'); + + if (!idempotencyKey || !ucpAgent) { + return res.status(400).json({ + error: 'Missing required headers (Idempotency-Key, UCP-Agent)' + }); + } + + // 2. Validate request body against UCP schema using Zod + const validation = CheckoutCreateRequestSchema.safeParse(req.body); + if (!validation.success) { + return res.status(400).json({ errors: validation.error.errors }); + } + + const body = validation.data; + + // Generate a unique checkout session ID + const sessionId = `chk_${uuidv4().substring(0, 10)}`; + ``` + +!!! note + If the required UCP headers are missing, FastAPI (Python) will automatically return an **HTTP 422 Unprocessable Entity** error due to its built-in validation. In our Express (Node.js) implementation, we manually return an **HTTP 400 Bad Request** error. + +### 3. Business Logic (Process Items & Calculate Totals) + +Process the incoming line items, resolve their prices, and calculate the subtotal, tax, and total. Prices are always in **minor units** (e.g., cents for USD). + +=== "Python" + + ```python + # main.py + # Map input line items to output line items with pricing + output_line_items = [] + subtotal = 0 + tax = 0 + + for index, item_req in enumerate(body.line_items): + # Mock product database lookup + # In production, perform real-time inventory verification and stock + # reservation here (catalog availability signals are advisory and can drift). + price = 2500 # $25.00 in minor units (cents) + title = f"Flower Bouquet {item_req.item.id}" + item_subtotal = price * item_req.quantity + item_tax = int(item_subtotal * 0.08) # 8% tax + item_total = item_subtotal + item_tax + + subtotal += item_subtotal + tax += item_tax + + output_line_items.append( + LineItem( + id=f"li_{index}", + item=Item(id=item_req.item.id, title=title, price=price), + quantity=item_req.quantity, + totals=[ + Total(type="subtotal", amount=item_subtotal), + Total(type="tax", amount=item_tax), + Total(type="total", amount=item_total) + ] + ) + ) + + total = subtotal + tax + ``` + +=== "Node.js" + + ```typescript + // server.ts + // Map input line items to output line items with pricing + const outputLineItems = body.line_items.map((item, index) => { + // Mock product database lookup + // In production, perform real-time inventory verification and stock + // reservation here (catalog availability signals are advisory and can drift). + const price = 2500; // $25.00 in minor units (cents) + const title = `Flower Bouquet ${item.item.id}`; + const itemSubtotal = price * item.quantity; + const itemTax = Math.floor(itemSubtotal * 0.08); // 8% tax + const itemTotal = itemSubtotal + itemTax; + + return { + id: `li_${index}`, + item: { id: item.item.id, title, price }, + quantity: item.quantity, + totals: [ + { type: 'subtotal', amount: itemSubtotal }, + { type: 'tax', amount: itemTax }, + { type: 'total', amount: itemTotal } + ] + }; + }); + + // Calculate order totals from line items + const subtotal = outputLineItems.reduce((acc, item) => { + const subtotalEntry = item.totals.find(t => t.type === 'subtotal'); + return acc + (subtotalEntry ? subtotalEntry.amount : 0); + }, 0); + const tax = outputLineItems.reduce((acc, item) => { + const taxEntry = item.totals.find(t => t.type === 'tax'); + return acc + (taxEntry ? taxEntry.amount : 0); + }, 0); + const total = subtotal + tax; + ``` + +### 4. UCP Response Construction + +Construct the UCP metadata block, advertising supported payment handlers, and assemble the final checkout response. + +!!! note "Capability Negotiation" + In production, a business uses the `UCP-Agent` header to resolve the platform's profile, calculates the intersection of supported capabilities, and returns the active negotiated set in `capabilities`. For simplicity in this quickstart, full capability negotiation is omitted. See [Discovery & Capability Negotiation](/documentation/core-concepts/#discovery-capability-negotiation) for details. + +=== "Python" + + ```python + # main.py + # Configure available payment handlers. + # We advertise support for a generic mock payment handler. + payment_handlers = { + "com.example.mock_pay": [ + PaymentHandlerResponse( + id="mock_pay_handler_1", + version="2026-08-25", + available_instruments=[ + AvailablePaymentInstrument(type="mock_instrument") + ] + ) + ] + } + + # Construct UCP protocol metadata + # (In production, populate active capabilities from UCP-Agent negotiation) + ucp_metadata = ResponseCheckoutSchema( + version="2026-08-25", + status="success", + payment_handlers=payment_handlers + ) + + # Assemble the final Checkout payload + checkout = Checkout( + ucp=ucp_metadata, + id=session_id, + status="incomplete", + currency="USD", + line_items=output_line_items, + totals=[ + Total(type="subtotal", amount=subtotal), + Total(type="tax", amount=tax), + Total(type="total", amount=total) + ], + links=[ + Link(type="terms_of_service", url="https://example.com/terms"), + Link(type="privacy_policy", url="https://example.com/privacy") + ] + ) + + # Save to database and return + checkout_sessions[session_id] = checkout + return checkout + ``` + +=== "Node.js" + + ```typescript + // server.ts + // Configure available payment handlers. + // We advertise support for a generic mock payment handler. + const ucpMetadata = { + version: '2026-08-25', + status: 'success' as const, + capabilities: {}, // In production, populate via capability negotiation from UCP-Agent + payment_handlers: { + 'com.example.mock_pay': [ + { + id: 'mock_pay_handler_1', + version: '2026-08-25', + available_instruments: [ + { type: 'mock_instrument' } + ] + } + ] + } + }; + + // Assemble the final Checkout payload + const checkout: CheckoutResponse = { + ucp: ucpMetadata, + id: sessionId, + status: 'incomplete', + currency: 'USD', + line_items: outputLineItems, + totals: [ + { type: 'subtotal', amount: subtotal }, + { type: 'tax', amount: tax }, + { type: 'total', amount: total } + ], + links: [ + { type: 'terms_of_service', url: 'https://example.com/terms' }, + { type: 'privacy_policy', url: 'https://example.com/privacy' } + ] + }; + + // Validate output matches CheckoutResponse schema before sending + const outputValidation = CheckoutResponseSchema.safeParse(checkout); + if (!outputValidation.success) { + console.error('Output validation failed:', outputValidation.error); + return res.status(500).json({ error: 'Internal server error' }); + } + + // Save to database and return + checkoutSessions[sessionId] = checkout; + res.status(201).json(checkout); + }); + ``` + +### 5. Get Checkout Endpoint + +Implement the retrieval route so the platform can fetch the checkout state. + +=== "Python" + + ```python + # main.py + @app.get( + "/checkout-sessions/{id}", + response_model=Checkout, + response_model_exclude_none=True + ) + async def get_checkout(id: str): + """Retrieve an existing checkout session.""" + if id not in checkout_sessions: + raise HTTPException(status_code=404, detail="Checkout session not found") + return checkout_sessions[id] + ``` + +=== "Node.js" + + ```typescript + // server.ts + app.get('/checkout-sessions/:id', (req, res) => { + const session = checkoutSessions[req.params.id]; + if (!session) { + return res.status(404).json({ error: 'Checkout session not found' }); + } + res.json(session); + }); + + const PORT = 3000; + app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); + }); + ``` + +--- + +## Running the Server + +=== "Python" + + Start the server using Uvicorn: + + ```bash + uv run uvicorn main:app --port 8080 --reload + ``` + +=== "Node.js" + + Add a start script to your `package.json`: + + + ```json + "scripts": { + "start": "tsx server.ts" + } + ``` + + Start the server: + + ```bash + npm start + ``` + +Your server is now running at `http://127.0.0.1:8080` (Python) or `http://127.0.0.1:3000` (Node.js). + +--- + +## Testing the Server + +You can test your server using `curl`. + +### 1. Create a Checkout Session + +Send a `POST` request to create a checkout session with one item: + +=== "Python" + + ```bash + curl -X POST http://127.0.0.1:8080/checkout-sessions \ + -H "Content-Type: application/json" \ + -H "Idempotency-Key: test-key-123" \ + -H "UCP-Agent: profile=\"https://platform.example/profile\"" \ + -d '{ + "line_items": [ + { + "item": { + "id": "prod_roses" + }, + "quantity": 2 + } + ] + }' + ``` + +=== "Node.js" + + ```bash + curl -X POST http://127.0.0.1:3000/checkout-sessions \ + -H "Content-Type: application/json" \ + -H "Idempotency-Key: test-key-123" \ + -H "UCP-Agent: profile=\"https://platform.example/profile\"" \ + -d '{ + "line_items": [ + { + "item": { + "id": "prod_roses" + }, + "quantity": 2 + } + ] + }' + ``` + +You should receive a response containing the UCP metadata, calculated totals, and the configured payment handler: + + +```json +{ + "ucp": { + "version": "2026-08-25", + "status": "success", + "payment_handlers": { + "com.example.mock_pay": [ + { + "version": "2026-08-25", + "id": "mock_pay_handler_1", + "available_instruments": [ + { + "type": "mock_instrument" + } + ] + } + ] + } + }, + "id": "chk_...", + "line_items": [ + { + "id": "li_0", + "item": { + "id": "prod_roses", + "title": "Flower Bouquet prod_roses", + "price": 2500 + }, + "quantity": 2, + "totals": [ + { + "type": "subtotal", + "amount": 5000 + }, + { + "type": "tax", + "amount": 400 + }, + { + "type": "total", + "amount": 5400 + } + ] + } + ], + "status": "incomplete", + "currency": "USD", + "totals": [ + { + "type": "subtotal", + "amount": 5000 + }, + { + "type": "tax", + "amount": 400 + }, + { + "type": "total", + "amount": 5400 + } + ], + "links": [ + { + "type": "terms_of_service", + "url": "https://example.com/terms" + }, + { + "type": "privacy_policy", + "url": "https://example.com/privacy" + } + ] +} +``` + +### 2. Retrieve the Checkout Session + +Retrieve the session using the `id` returned from the previous step: + +=== "Python" + + ```bash + curl http://127.0.0.1:8080/checkout-sessions/ + ``` + +=== "Node.js" + + ```bash + curl http://127.0.0.1:3000/checkout-sessions/ + ``` + +--- + +## Full File Reference + +If you want to verify your code, expand the section below to see the complete files. + +??? "Complete Code Reference" + + === "Python" + + `main.py` + ```python + # main.py + import uuid + from typing import Annotated + from fastapi import FastAPI, Header, HTTPException, status + + # Import UCP SDK models + from ucp_sdk.models.schemas.ucp import ResponseCheckoutSchema + from ucp_sdk.models.schemas.shopping.checkout import Checkout + from ucp_sdk.models.schemas.shopping.checkout_create_request import CheckoutCreateRequest + from ucp_sdk.models.schemas.shopping.types.line_item import LineItem + from ucp_sdk.models.schemas.shopping.types.item import Item + from ucp_sdk.models.schemas.common.types.totals import Total + from ucp_sdk.models.schemas.common.types.link import Link + from ucp_sdk.models.schemas.common.types.available_payment_instrument import AvailablePaymentInstrument + from ucp_sdk.models.schemas.payment_handler import ResponseSchema as PaymentHandlerResponse + + # Initialize FastAPI app + app = FastAPI(title="UCP Quickstart Server") + + # Simple in-memory database + checkout_sessions = {} + + @app.post( + "/checkout-sessions", + response_model=Checkout, + status_code=status.HTTP_201_CREATED, + response_model_exclude_none=True + ) + async def create_checkout( + body: CheckoutCreateRequest, + idempotency_key: Annotated[str, Header(alias="Idempotency-Key")], + ucp_agent: Annotated[str, Header(alias="UCP-Agent")] + ): + """Create a new UCP checkout session.""" + # Note: In a production environment, you must use the Idempotency-Key + # to prevent duplicate processing of the same request. + + # Generate a unique checkout session ID + session_id = f"chk_{uuid.uuid4().hex[:10]}" + + # Map input line items to output line items with pricing + output_line_items = [] + subtotal = 0 + tax = 0 + + for index, item_req in enumerate(body.line_items): + # Mock product database lookup + # In production, perform real-time inventory verification and stock + # reservation here (catalog availability signals are advisory and can drift). + price = 2500 # $25.00 in minor units (cents) + title = f"Flower Bouquet {item_req.item.id}" + item_subtotal = price * item_req.quantity + item_tax = int(item_subtotal * 0.08) # 8% tax + item_total = item_subtotal + item_tax + + subtotal += item_subtotal + tax += item_tax + + output_line_items.append( + LineItem( + id=f"li_{index}", + item=Item(id=item_req.item.id, title=title, price=price), + quantity=item_req.quantity, + totals=[ + Total(type="subtotal", amount=item_subtotal), + Total(type="tax", amount=item_tax), + Total(type="total", amount=item_total) + ] + ) + ) + + total = subtotal + tax + + # Configure available payment handlers. + # We advertise support for a generic mock payment handler. + payment_handlers = { + "com.example.mock_pay": [ + PaymentHandlerResponse( + id="mock_pay_handler_1", + version="2026-08-25", + available_instruments=[ + AvailablePaymentInstrument(type="mock_instrument") + ] + ) + ] + } + + # Construct UCP protocol metadata + # (In production, populate active capabilities from UCP-Agent negotiation) + ucp_metadata = ResponseCheckoutSchema( + version="2026-08-25", + status="success", + payment_handlers=payment_handlers + ) + + # Assemble the final Checkout payload + checkout = Checkout( + ucp=ucp_metadata, + id=session_id, + status="incomplete", + currency="USD", + line_items=output_line_items, + totals=[ + Total(type="subtotal", amount=subtotal), + Total(type="tax", amount=tax), + Total(type="total", amount=total) + ], + links=[ + Link(type="terms_of_service", url="https://example.com/terms"), + Link(type="privacy_policy", url="https://example.com/privacy") + ] + ) + + # Save to database and return + checkout_sessions[session_id] = checkout + return checkout + + @app.get( + "/checkout-sessions/{id}", + response_model=Checkout, + response_model_exclude_none=True + ) + async def get_checkout(id: str): + """Retrieve an existing checkout session.""" + if id not in checkout_sessions: + raise HTTPException(status_code=404, detail="Checkout session not found") + return checkout_sessions[id] + ``` + + === "Node.js" + + `server.ts` + ```typescript + // server.ts + import express from 'express'; + import { v4 as uuidv4 } from 'uuid'; + + // Import validation schemas from JS SDK + import { + CheckoutCreateRequestSchema, + CheckoutResponseSchema, + type CheckoutResponse + } from '@ucp-js/sdk'; + + // Initialize Express app + const app = express(); + app.use(express.json()); + + // Simple in-memory database + const checkoutSessions: Record = {}; + + app.post('/checkout-sessions', (req, res) => { + // 1. Validate required UCP headers + const idempotencyKey = req.header('Idempotency-Key'); + const ucpAgent = req.header('UCP-Agent'); + + if (!idempotencyKey || !ucpAgent) { + return res.status(400).json({ + error: 'Missing required headers (Idempotency-Key, UCP-Agent)' + }); + } + + // 2. Validate request body against UCP schema using Zod + const validation = CheckoutCreateRequestSchema.safeParse(req.body); + if (!validation.success) { + return res.status(400).json({ errors: validation.error.errors }); + } + + const body = validation.data; + + // Generate a unique checkout session ID + const sessionId = `chk_${uuidv4().substring(0, 10)}`; + + // Map input line items to output line items with pricing + const outputLineItems = body.line_items.map((item, index) => { + // Mock product database lookup + // In production, perform real-time inventory verification and stock + // reservation here (catalog availability signals are advisory and can drift). + const price = 2500; // $25.00 in minor units (cents) + const title = `Flower Bouquet ${item.item.id}`; + const itemSubtotal = price * item.quantity; + const itemTax = Math.floor(itemSubtotal * 0.08); // 8% tax + const itemTotal = itemSubtotal + itemTax; + + return { + id: `li_${index}`, + item: { id: item.item.id, title, price }, + quantity: item.quantity, + totals: [ + { type: 'subtotal', amount: itemSubtotal }, + { type: 'tax', amount: itemTax }, + { type: 'total', amount: itemTotal } + ] + }; + }); + + // Calculate order totals from line items + const subtotal = outputLineItems.reduce((acc, item) => { + const subtotalEntry = item.totals.find(t => t.type === 'subtotal'); + return acc + (subtotalEntry ? subtotalEntry.amount : 0); + }, 0); + const tax = outputLineItems.reduce((acc, item) => { + const taxEntry = item.totals.find(t => t.type === 'tax'); + return acc + (taxEntry ? taxEntry.amount : 0); + }, 0); + const total = subtotal + tax; + + // Configure available payment handlers. + // We advertise support for a generic mock payment handler. + const ucpMetadata = { + version: '2026-08-25', + status: 'success' as const, + capabilities: {}, // In production, populate via capability negotiation from UCP-Agent + payment_handlers: { + 'com.example.mock_pay': [ + { + id: 'mock_pay_handler_1', + version: '2026-08-25', + available_instruments: [ + { type: 'mock_instrument' } + ] + } + ] + } + }; + + // Assemble the final Checkout payload + const checkout: CheckoutResponse = { + ucp: ucpMetadata, + id: sessionId, + status: 'incomplete', + currency: 'USD', + line_items: outputLineItems, + totals: [ + { type: 'subtotal', amount: subtotal }, + { type: 'tax', amount: tax }, + { type: 'total', amount: total } + ], + links: [ + { type: 'terms_of_service', url: 'https://example.com/terms' }, + { type: 'privacy_policy', url: 'https://example.com/privacy' } + ] + }; + + // Validate output matches CheckoutResponse schema before sending + const outputValidation = CheckoutResponseSchema.safeParse(checkout); + if (!outputValidation.success) { + console.error('Output validation failed:', outputValidation.error); + return res.status(500).json({ error: 'Internal server error' }); + } + + // Save to database and return + checkoutSessions[sessionId] = checkout; + res.status(201).json(checkout); + }); + + app.get('/checkout-sessions/:id', (req, res) => { + const session = checkoutSessions[req.params.id]; + if (!session) { + return res.status(404).json({ error: 'Checkout session not found' }); + } + res.json(session); + }); + + const PORT = 3000; + app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); + }); + ``` + +--- + +## Next Steps + +To build a fully compliant UCP server, you will also need to: + +* Implement the checkout update endpoint (`PUT /checkout-sessions/{id}`) to handle buyer information updates (like shipping address). +* Implement the checkout completion endpoint (`POST /checkout-sessions/{id}/complete`) to process the payment instrument provided by the platform. +* Advertise your service using a [UCP Discovery Profile](/documentation/core-concepts/#discovery-capability-negotiation) at `/.well-known/ucp`. +* Run the conformance suite from the [UCP Conformance repository](https://github.com/Universal-Commerce-Protocol/conformance) against your server to verify protocol compliance. + +For a complete reference implementation, check out the [UCP Samples repository](https://github.com/Universal-Commerce-Protocol/samples). diff --git a/mkdocs.yml b/mkdocs.yml index f3372f3bb..767514d70 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -42,6 +42,7 @@ nav: - Announcements: documentation/announcements.md - Specification: - Overview: specification/overview/index.md + - Getting Started: specification/getting-started.md - Common: - Identity Linking: specification/common/identity-linking/index.md - Location Capability: @@ -389,6 +390,9 @@ plugins: - specification/overview/index.md: >- Master technical specification of UCP, discovery mechanisms, secure transports, capability negotiation, and error taxonomies. + - specification/getting-started.md: >- + Step-by-step developer guide for building a UCP checkout server + in Python (FastAPI) or Node.js (Express). - specification/glossary.md: >- Definitions of the protocol, commerce, payment, regulatory, and standards terms and acronyms used throughout the specification,