All Guides
intermediate

How to Implement UCP Checkout: A Developer's Guide

A protocol-oriented guide to UCP discovery, schema validation, service bindings, checkout transport, and conformance testing.

Published 2026-02-10Updated 2026-07-23

How to Implement UCP Checkout: A Developer's Guide

This guide outlines UCP checkout from the agent side. UCP service URLs and transports are advertised by each merchant profile, so production code must bind to the schemas and service definitions in that profile rather than assume one universal client API.

> Tooling note: the official JavaScript SDK currently provides generated types and Zod schemas. It does not export a UCPClient, payment handler, webhook verifier, or transport implementation. TypeScript blocks below that call client are transport-neutral pseudocode and must be adapted to the merchant's advertised REST, MCP, or embedded service.

Prerequisites

  • Node.js 20+ and TypeScript 5+
  • A UCP-compatible merchant endpoint or the official sample server
  • A payment handler supported by the merchant profile

Step 1: Install the SDK

pnpm add @ucp-js/sdk

The @ucp-js/sdk package provides generated TypeScript types and Zod schemas. Use it to validate data crossing your transport boundary.

Step 2: Configure the Client

import { CheckoutResponseSchema } from '@ucp-js/sdk';

// Network calls are implementation-specific. Validate their payloads at the boundary.
const parsed = CheckoutResponseSchema.safeParse(checkoutData);
if (!parsed.success) throw parsed.error;
const checkout = parsed.data;

Authentication is service-specific. Read the merchant profile and advertised service schema before selecting credentials or a transport.

Step 3: Discover the Merchant Endpoint

Before initiating checkout, your agent needs to fetch the merchant's business profile from /.well-known/ucp and inspect its advertised services:

const discovery = await client.discover('https://example-store.com');

console.log(discovery);
// {
//   endpoint: 'https://example-store.com/api/ucp/v1',
//   capabilities: ['checkout', 'order-management'],
//   supportedCurrencies: ['USD', 'EUR', 'GBP'],
//   protocolVersion: '1.2',
// }

The discover method fetches and validates the merchant's UCP manifest. Always check capabilities to confirm the merchant supports checkout before proceeding.

Step 4: Create a Checkout Session

// Transport-neutral pseudocode; exact shapes come from the advertised schemas.
const items = [
  {
    merchantProductId: 'SKU-12345',
    quantity: 2,
    // Price is informational here — the merchant calculates the authoritative total
    expectedUnitPrice: { amount: 2999, currency: 'USD' },
  },
  {
    merchantProductId: 'SKU-67890',
    quantity: 1,
    expectedUnitPrice: { amount: 4999, currency: 'USD' },
  },
];

const shippingAddress = {
  name: 'Jane Doe',
  line1: '123 Main St',
  city: 'San Francisco',
  region: 'CA',
  postalCode: '94102',
  country: 'US',
};

const session = await client.checkout.createSession({
  merchantEndpoint: discovery.endpoint,
  lineItems: items,
  shippingAddress,
  currency: 'USD',
  // Optional: link to user's existing merchant account
  identityToken: userMerchantToken,
});

The returned session object contains everything your agent needs to present the order summary to the user:

console.log(session);
// {
//   sessionId: 'cs_abc123...',
//   status: 'pending_confirmation',
//   lineItems: [...],          // Merchant-confirmed items with final prices
//   subtotal: { amount: 10997, currency: 'USD' },
//   tax: { amount: 962, currency: 'USD' },
//   shippingOptions: [
//     { id: 'standard', label: 'Standard (5-7 days)', cost: { amount: 599, currency: 'USD' } },
//     { id: 'express', label: 'Express (2-3 days)', cost: { amount: 1299, currency: 'USD' } },
//   ],
//   expiresAt: '2026-02-10T15:30:00Z',
// }

Note that all monetary amounts are in the smallest currency unit (cents for USD). The expectedUnitPrice you sent is advisory — the merchant returns the authoritative price. If the merchant's price differs significantly from what you expected, flag that to the user before confirming.

Step 5: Confirm the Session

Once the user approves the order summary and selects a shipping method:

const confirmation = await client.checkout.confirmSession({
  sessionId: session.sessionId,
  selectedShippingOption: 'standard',
});

// confirmation contains the payment token
console.log(confirmation);
// {
//   sessionId: 'cs_abc123...',
//   status: 'awaiting_payment',
//   paymentToken: 'pt_xyz789...',
//   total: { amount: 12558, currency: 'USD' },
//   tokenExpiresAt: '2026-02-10T15:35:00Z',
// }

The paymentToken is a single-use, time-limited credential. You must submit it to a payment handler before tokenExpiresAt.

Step 6: Process Payment

Forward the payment token to your configured payment handler. Here's an example using Stripe's UCP integration:

// Provider-specific pseudocode: there is no universal UCP payment-handler SDK.
const paymentHandler = configuredPaymentHandler;

const payment = await paymentHandler.processToken({
  paymentToken: confirmation.paymentToken,
  paymentMethodId: userSavedPaymentMethod.id,
});

console.log(payment);
// {
//   status: 'succeeded',
//   transactionId: 'txn_abc123',
//   settledAmount: { amount: 12558, currency: 'USD' },
// }

The payment handler verifies the token with the merchant, charges the user's payment method, and returns a settlement confirmation. The merchant is notified asynchronously and will begin fulfillment.

Step 7: Handle Errors

UCP defines structured error codes. Always handle these cases:

// Illustrative control flow: adapt these cases to the advertised error schema.

try {
  const session = await client.checkout.createSession({ ... });
} catch (error) {
  if (error instanceof UCPError) {
    switch (error.code) {
      case UCPErrorCode.ITEM_UNAVAILABLE:
        // One or more items are out of stock
        console.error('Unavailable items:', error.details.unavailableItems);
        break;
      case UCPErrorCode.SHIPPING_RESTRICTED:
        // Merchant can't ship to the provided address
        console.error('Shipping restriction:', error.message);
        break;
      case UCPErrorCode.SESSION_EXPIRED:
        // Session timed out — create a new one
        console.error('Session expired, retrying...');
        break;
      case UCPErrorCode.PRICE_CHANGED:
        // Price changed between discovery and session creation
        console.error('Price delta:', error.details.priceDelta);
        break;
      case UCPErrorCode.RATE_LIMITED:
        // Back off and retry
        const retryAfter = error.headers?.['retry-after'] ?? 5;
        console.error(`Rate limited. Retry after ${retryAfter}s`);
        break;
      default:
        console.error('UCP error:', error.code, error.message);
    }
  } else {
    // Network error, timeout, etc.
    console.error('Unexpected error:', error);
  }
}

Step 8: Listen for Order Updates

Register a webhook handler to receive post-purchase notifications:

// Signature verification and event shapes are defined by your chosen service/provider.

// In your API route handler (e.g., Next.js route handler)
export async function POST(request: Request) {
  const body = await request.text();
  const signature = request.headers.get('ucp-signature')!;

  const isValid = verifyWebhookSignature(body, signature, process.env.UCP_WEBHOOK_SECRET!);
  if (!isValid) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event: OrderEvent = JSON.parse(body);

  switch (event.type) {
    case 'order.shipped':
      // Update your UI — tracking number is in event.data.tracking
      break;
    case 'order.delivered':
      break;
    case 'order.refunded':
      break;
  }

  return new Response('OK', { status: 200 });
}

Testing

Use the official sample merchant server and conformance fixtures instead of relying on undocumented global sandbox credentials or test SKUs. Run the conformance tests against your implementation before going live:

git clone https://github.com/Universal-Commerce-Protocol/conformance
cd conformance
uv sync
# Configure conformance_input.json and test_fixtures.json, then follow the README:
SERVER_URL=https://merchant.example SIMULATION_SECRET=... uv run pytest \
  --conformance_input=path/to/conformance_input.json \
  --fixture_config=path/to/test_fixtures.json

The official suite validates a running merchant server against configured UCP scenarios. Its README is the source of truth for required fixtures and command-line options.

Explore the UCP Ecosystem

Browse our directory of UCP tools, merchants, and platforms.

Browse Directory