Skip to main content

Testing and Validation

9.1 UCP Profile Validation

The Profile is the entry point for every AI agent connection and must pass rigorous validation.

Basic Checks

# 1. Verify HTTPS and no redirects
curl -sI "https://mystore.com/.well-known/ucp" | head -5
# Expected: HTTP/2 200 (must not be 3xx)

# 2. Check Cache-Control
curl -sI "https://mystore.com/.well-known/ucp" | grep -i cache-control
# Expected: cache-control: public, max-age=N (N >= 60)

# 3. Check Content-Type
curl -sI "https://mystore.com/.well-known/ucp" | grep -i content-type
# Expected: content-type: application/json

# 4. Fetch and validate JSON
curl -s "https://mystore.com/.well-known/ucp" | jq .

Profile Field Validation Checklist

FieldValidation RuleRequired
supported_versionsNon-empty array containing valid date-formatted version stringsYes
servicesAt least one service; base_url must be HTTPSYes
capabilitiesAt least one capability; namespace format must be correctYes
capabilities.*.versionValid date format; within the supported_versions rangeYes
payment_handlersArray with reverse-DNS naming formatNo
signing_keysJWK array; each key must have kid, kty, crv, x, y, use, algNo

Automated Profile Validation Script

#!/bin/bash
URL="https://mystore.com/.well-known/ucp"

echo "=== UCP Profile Validation ==="

# Check HTTP status code (do not follow redirects)
STATUS=$(curl -so /dev/null -w "%{http_code}" --max-redirs 0 "$URL")
if [ "$STATUS" = "200" ]; then
  echo "PASS: HTTP 200 (no redirect)"
else
  echo "FAIL: HTTP $STATUS (expected 200, no redirects allowed)"
fi

# Check HTTPS
if [[ "$URL" == https://* ]]; then
  echo "PASS: HTTPS"
else
  echo "FAIL: Must be HTTPS"
fi

# Check Cache-Control
CACHE=$(curl -sI "$URL" | grep -i "cache-control" | tr -d '\r')
if echo "$CACHE" | grep -qi "public"; then
  echo "PASS: Cache-Control includes public"
else
  echo "FAIL: Cache-Control must include 'public'"
fi

# Check JSON validity
BODY=$(curl -s "$URL")
if echo "$BODY" | jq . > /dev/null 2>&1; then
  echo "PASS: Valid JSON"
else
  echo "FAIL: Invalid JSON"
fi

# Check required fields
if echo "$BODY" | jq -e '.supported_versions | length > 0' > /dev/null 2>&1; then
  echo "PASS: supported_versions present"
else
  echo "FAIL: supported_versions missing or empty"
fi

if echo "$BODY" | jq -e '.services | keys | length > 0' > /dev/null 2>&1; then
  echo "PASS: services present"
else
  echo "FAIL: services missing or empty"
fi

if echo "$BODY" | jq -e '.capabilities | keys | length > 0' > /dev/null 2>&1; then
  echo "PASS: capabilities present"
else
  echo "FAIL: capabilities missing or empty"
fi

# Check signing key format
KEYS=$(echo "$BODY" | jq -r '.signing_keys | length')
if [ "$KEYS" -gt 0 ]; then
  echo "INFO: $KEYS signing key(s) found"
  echo "$BODY" | jq -r '.signing_keys[] | "  kid=\(.kid) alg=\(.alg) crv=\(.crv)"'
else
  echo "INFO: No signing keys (Webhook signing not configured)"
fi

echo "=== Validation Complete ==="

9.2 Checkout Flow Testing

Complete Checkout Flow

BASE="https://mystore.com/ucp"
TOKEN="Bearer ucp_pk_test_xxxx"

# Step 1: Create a checkout session
SESSION=$(curl -s -X POST "$BASE/checkout/sessions" \
  -H "Content-Type: application/json" \
  -H "Authorization: $TOKEN" \
  -d '{
    "line_items": [{"product_id": "prod_001", "quantity": 1}],
    "buyer": {"email": "test@example.com"}
  }')
echo "Create: $(echo $SESSION | jq -r '.checkout_session.status')"
# Expected: incomplete

SESSION_ID=$(echo $SESSION | jq -r '.checkout_session.id')

# Step 2: Update buyer information and shipping
curl -s -X PATCH "$BASE/checkout/sessions/$SESSION_ID" \
  -H "Content-Type: application/json" \
  -H "Authorization: $TOKEN" \
  -d '{
    "buyer": {
      "name": "Test User",
      "shipping_address": {
        "line1": "123 Main St",
        "city": "San Francisco",
        "state": "CA",
        "postal_code": "94102",
        "country_code": "US"
      }
    },
    "fulfillment": {"method": "standard_shipping"}
  }' | jq '.checkout_session.status'

# Step 3: Query session status
STATUS=$(curl -s "$BASE/checkout/sessions/$SESSION_ID" \
  -H "Authorization: $TOKEN" | jq -r '.checkout_session.status')
echo "After update: $STATUS"
# Expected: incomplete or ready_for_complete

# Step 4: Cancel the session (recommended in test environments instead of completing)
curl -s -X POST "$BASE/checkout/sessions/$SESSION_ID/cancel" \
  -H "Content-Type: application/json" \
  -H "Authorization: $TOKEN" \
  -d '{"reason": "test_cancellation"}' | jq '.checkout_session.status'
# Expected: canceled

State Machine Validation

Test all six status transition paths for correctness:
Test ScenarioInitial StatusOperationExpected Result
Create empty session-Createincomplete
Provide complete informationincompleteUpdateready_for_complete
Submit for completionready_for_completeCompletecomplete_in_progress
Cancel an in-progress sessionincompleteCancelcanceled
Cancel a completed sessioncompletedCancel400 error
Update a canceled sessioncanceledUpdate400 error
Trigger human escalationincompleteSpecific conditionrequires_escalation

9.3 Product Data Validation

Every product record must pass the following validation:
Required field checks:
  [x] id: Non-empty and unique
  [x] name: Non-empty string
  [x] price.amount: Positive integer (minor currency units)
  [x] price.currency_code: Valid ISO 4217 code (e.g., USD, EUR, JPY)
  [x] availability: One of in_stock / out_of_stock / preorder
  [x] images: At least one item; URL must be accessible via HTTPS

Optional field format checks:
  [x] variants[].sku: If present, must be unique
  [x] variants[].price.amount: Positive integer
  [x] gtin: If present, must conform to GTIN-13 or GTIN-14 format
  [x] compare_at_price.amount: If present, must be greater than price.amount

Amount consistency:
  [x] All price objects use the same currency_code
  [x] Amount values are consistent with the currency's minor unit (USD uses cents, JPY uses yen)

9.4 Signature Verification Testing

If Webhook signing is configured, test both signature creation and verification:
# Simulate a merchant sending a signed Webhook
# 1. Create a test request body
BODY='{"event_type":"order.created","order_id":"ord_test_001"}'

# 2. Compute Content-Digest
DIGEST=$(echo -n "$BODY" | openssl dgst -sha256 -binary | base64)
echo "Content-Digest: sha-256=:$DIGEST:"

# 3. Verify signing keys in the merchant Profile
curl -s "https://mystore.com/.well-known/ucp" | jq '.signing_keys'
# Confirm kid, alg (ES256), crv (P-256) and other fields are correct

9.5 OAuth Flow Testing

# 1. Verify authorization server discovery
curl -s "https://mystore.com/.well-known/oauth-authorization-server" | jq .
# Check: authorization_endpoint, token_endpoint, revocation_endpoint

# 2. Verify PKCE support
# Check that code_challenge_methods_supported includes "S256"

# 3. Verify scope support
# Check that scopes_supported includes "ucp:scopes:checkout_session"

# 4. Test the token revocation endpoint (RFC 7009)
curl -s -X POST "https://mystore.com/oauth/revoke" \
  -d "token=expired_test_token&token_type_hint=access_token&client_id=test" \
  -w "\nHTTP Status: %{http_code}\n"
# Expected: 200 (should return 200 even for invalid tokens)

9.6 Performance Benchmarks

UCP endpoints should respond within reasonable timeframes:
Endpoint TypeResponse Time TargetConcurrency Requirement
/.well-known/ucp ProfileUnder 100ms100+ QPS
Product searchUnder 500ms50+ QPS
Product detailsUnder 200ms100+ QPS
Checkout operationsUnder 1s20+ QPS
OAuth token endpointUnder 300ms50+ QPS
# Simple performance test
# Profile endpoint latency
for i in $(seq 1 10); do
  curl -so /dev/null -w "%{time_total}\n" "https://mystore.com/.well-known/ucp"
done

# Product search concurrency test (requires ab or wrk)
ab -n 100 -c 10 "https://mystore.com/ucp/catalog/products?query=test&limit=10"

9.7 Common Issues and Troubleshooting

IssueCauseSolution
AI agent cannot discover Profile3xx redirect or non-HTTPSEnsure a direct 200 response with no redirects
Cache-Control validation failsmax-age below 60 secondsSet public, max-age=3600
Prices display incorrectlyNot using minor currency unitsConfirm that amount is an integer (cents), not a decimal
Signature verification failskid mismatch or expired keyCheck that the kid in signing_keys matches the Signature-Input
OAuth flow breaksMissing PKCE supportImplement S256 code_challenge verification
Capability negotiation returns emptyVersion mismatchEnsure both parties have overlapping supported_versions
Webhooks not receivedSignature verification rejectedVerify Content-Digest and Signature are correct

Next chapter: Case Studies and Extensions — Buyer Consent extension, AP2 Mandate, and real-world integration paths