Skip to content

Commit 3f6c0e1

Browse files
committed
Add Java (Spring) demo application.
1 parent 8bf5313 commit 3f6c0e1

129 files changed

Lines changed: 24721 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
.ruff_cache
44
uv.lock
55
.venv
6+
rest/java-spring/target

rest/java-spring/README.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
<!--
2+
Copyright 2026 UCP Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
# UCP Merchant Server (Java/Spring Boot)
18+
19+
This directory contains a reference implementation of a Universal Commerce
20+
Protocol (UCP) server built with Java 25 and Spring Boot 3.5.9. It demonstrates
21+
how to implement the UCP specifications for checkout sessions, including modular
22+
support for fulfillment, discounts, and order extensions.
23+
24+
## Project Structure
25+
26+
* `src/main/java/com/dev/ucp/actions`: Contains the business logic for
27+
checkout operations (Create, Update, Complete, etc.).
28+
* `src/main/java/com/dev/ucp/entities`: JPA entities with nested repository
29+
interfaces for persistence.
30+
* `src/main/java/com/dev/ucp/service`: REST controllers and exception
31+
handlers.
32+
* `src/main/resources/openapi.extensions.json`: Defines modular UCP schema
33+
extensions (e.g., fulfillment, discounts) used for model generation.
34+
* `generated/`: Source root for models and interfaces generated from OpenAPI
35+
specs.
36+
* `pom.xml`: Main Maven configuration.
37+
* `codegen.pom.xml`: Specialized Maven configuration for OpenAPI code
38+
generation.
39+
40+
## Prerequisites
41+
42+
* Java 25 JDK
43+
* Apache Maven 3.9+
44+
* (Optional) Official `ucp` repository for
45+
[model generation](#generating-models).
46+
47+
## Setup
48+
49+
```shell
50+
# Clone the samples repository if you haven't already
51+
git clone https://github.com/Universal-Commerce-Protocol/samples.git
52+
cd samples/rest/java-spring
53+
54+
# Build the project (pre-generated models are included in the repository)
55+
mvn clean install
56+
```
57+
58+
## Run the Server
59+
60+
Start the server on port 8182. Using the `conformance` profile enables
61+
simulation endpoints required for testing.
62+
63+
```bash
64+
# Start the server in the background, redirecting output to server.log
65+
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8182 -Pconformance > server.log 2>&1 &
66+
SERVER_PID=$!
67+
```
68+
69+
Note: You can monitor the logs with `tail -f server.log`.
70+
71+
## Discovery & Health Check
72+
73+
To verify the operational status and discover the capabilities of your local
74+
server, you can query the UCP discovery endpoint. This serves as a primary
75+
validation that the service is correctly configured and accessible.
76+
77+
```bash
78+
curl -s http://localhost:8182/.well-known/ucp
79+
```
80+
81+
### Example Discovery Response:
82+
83+
```json
84+
{
85+
"ucp": {
86+
"version": "2026-01-11",
87+
"services": {
88+
"dev.ucp.shopping": {
89+
"version": "2026-01-11",
90+
"spec": "https://ucp.dev/specs/shopping",
91+
"rest": {
92+
"schema": "https://ucp.dev/services/shopping/openapi.json",
93+
"endpoint": "http://localhost:8182/ucp"
94+
}
95+
}
96+
},
97+
"capabilities": [
98+
{
99+
"name": "dev.ucp.shopping.checkout",
100+
"version": "2026-01-11",
101+
"spec": "https://ucp.dev/specs/shopping/checkout",
102+
"schema": "https://ucp.dev/schemas/shopping/checkout.json"
103+
},
104+
{
105+
"name": "dev.ucp.shopping.discount",
106+
"version": "2026-01-11",
107+
"spec": "https://ucp.dev/specs/shopping/discount",
108+
"schema": "https://ucp.dev/schemas/shopping/discount.json",
109+
"extends": "dev.ucp.shopping.checkout"
110+
},
111+
{
112+
"name": "dev.ucp.shopping.fulfillment",
113+
"version": "2026-01-11",
114+
"spec": "https://ucp.dev/specs/shopping/fulfillment",
115+
"schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
116+
"extends": "dev.ucp.shopping.checkout"
117+
},
118+
{
119+
"name": "dev.ucp.shopping.order",
120+
"version": "2026-01-11",
121+
"spec": "https://ucp.dev/specs/shopping/order",
122+
"schema": "https://ucp.dev/schemas/shopping/order.json"
123+
}
124+
]
125+
},
126+
"payment": {
127+
"handlers": [
128+
{
129+
"id": "mock_payment_handler",
130+
"name": "dev.ucp.mock_payment",
131+
"version": "2026-01-11",
132+
"spec": "https://ucp.dev/specs/mock",
133+
"config_schema": "https://ucp.dev/schemas/mock.json",
134+
"instrument_schemas": [
135+
"https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
136+
],
137+
"config": {
138+
"supported_tokens": [
139+
"success_token",
140+
"fail_token"
141+
]
142+
}
143+
}
144+
]
145+
}
146+
}
147+
```
148+
149+
## Running Conformance Tests
150+
151+
To verify that this server implementation complies with the UCP specifications,
152+
please use the official
153+
[UCP Conformance Test Suite](https://github.com/Universal-Commerce-Protocol/conformance).
154+
Refer to its documentation for prerequisites, installation, and detailed
155+
execution instructions.
156+
157+
Assuming the conformance suite is configured to target port **8182**, you can
158+
run the fulfillment tests with a command similar to:
159+
160+
```bash
161+
.venv/bin/python3 fulfillment_test.py \
162+
--server_url=http://localhost:8182 \
163+
--simulation_secret=super-secret-sim-key \
164+
--conformance_input=test_data/flower_shop/conformance_input.json
165+
```
166+
167+
## Testing Endpoints
168+
169+
The server exposes additional endpoints for simulation and testing purposes.
170+
These are only available when the server is started with the `conformance`
171+
profile.
172+
173+
**Note:** If you are relying on hot-swapping, ensure that you also include the
174+
`-Pconformance` flag during your build or compilation steps to keep these
175+
test-only classes active.
176+
177+
* `POST /testing/simulate-shipping/{id}`: Triggers a simulated "order shipped"
178+
event for the specified order ID. This updates the order status and sends a
179+
webhook notification. This endpoint requires the `Simulation-Secret` header.
180+
181+
## Database & Test Data
182+
183+
The server uses an in-memory H2 database for the demo. The schema and initial
184+
flower shop data are automatically loaded on startup.
185+
186+
If you wish to add custom products, discounts, or inventory for testing, you can
187+
modify `src/main/resources/data.sql`
188+
189+
## Generating Models
190+
191+
This project uses OpenAPI Generator to maintain strict adherence to the UCP
192+
schemas. Pre-generated models are provided in the `generated/` directory.
193+
194+
### Workspace Structure
195+
196+
The generation process assumes that the main `ucp` repository (containing the
197+
official specifications) is checked out in a directory sibling to the `samples`
198+
repository:
199+
200+
```text
201+
/work/
202+
├── ucp/ <-- Official specifications
203+
└── samples/ <-- This repository
204+
```
205+
206+
### Regeneration Steps
207+
208+
If the specifications change, you can regenerate the models:
209+
210+
1. **Run the preparation and generation script**:
211+
212+
```bash
213+
./generate_ucp_models.sh [optional_path_to_ucp_root]
214+
```
215+
216+
By default, it looks for UCP at `../../../ucp`.
217+
218+
This script copies the official specs to `target/spec`, applies necessary
219+
compatibility transformations, and runs the code generator.
220+
221+
The generated code will be placed in the `generated/` directory and is
222+
automatically included in the main build's classpath.
223+
224+
## Terminate the server
225+
226+
Terminate the server process when finished:
227+
228+
```bash
229+
kill -9 $(lsof -t -i :8182)
230+
```
231+
232+
## Developer Guide: Core Framework Components
233+
234+
The following files provide essential infrastructure for UCP compliance and are designed to be reused or extended as the protocol evolves:
235+
236+
* **`CheckoutSessionsApiController.java`**: The primary REST entry point for the UCP Shopping API. It handles protocol-level concerns like `UCP-Agent` header validation before delegating business logic to specialized action classes.
237+
* **`DiscoveryController.java`**: Implements the mandatory UCP discovery endpoint (`.well-known/ucp`), advertising the server's supported protocol versions, services, and capabilities to the platform.
238+
* **`ExtensionsHelper.java` & `src/main/resources/openapi.extensions.json`**: These work in tandem to manage UCP extension fields. `openapi.extensions.json` defines the schema extensions for model generation, while `ExtensionsHelper.java` provides typed access to these fields. This infrastructure only requires updates when a new extension module is added to or removed from the protocol.
239+
* **`JacksonConfig.java`**: Handles the specialized JSON processing required by the UCP specification, including `JsonNullable` support and custom polymorphic deserialization for union types like `PaymentCredential` and `FulfillmentDestination`.
240+
* **`UCPExceptionControllerAdvice.java`**: Ensures that all service-layer exceptions are automatically transformed into structured, UCP-compliant error responses with appropriate protocol-standard error codes.
241+
* **`PlatformProfileResolver.java`**: Orchestrates the discovery and resolution of platform UCP profiles, enabling the server to dynamically determine capability endpoints and webhook configurations.

rest/java-spring/codegen.pom.xml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>org.springframework.boot</groupId>
8+
<artifactId>spring-boot-starter-parent</artifactId>
9+
<version>3.5.9</version>
10+
<relativePath/> <!-- lookup parent from repository -->
11+
</parent>
12+
<groupId>com.dev.ucp</groupId>
13+
<artifactId>api-generator</artifactId>
14+
<version>2026.1.11-1-SNAPSHOT</version>
15+
<name>ucp-api-generator</name>
16+
<description>UCP API Generator</description>
17+
<url/>
18+
<licenses>
19+
<license>
20+
<name>Apache License, Version 2.0</name>
21+
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
22+
<distribution>repo</distribution>
23+
</license>
24+
</licenses>
25+
<developers>
26+
<developer/>
27+
</developers>
28+
<scm>
29+
<connection/>
30+
<developerConnection/>
31+
<tag/>
32+
<url/>
33+
</scm>
34+
<properties>
35+
<java.version>25</java.version>
36+
<spec.dir>${project.basedir}/spec</spec.dir>
37+
</properties>
38+
<build>
39+
<plugins>
40+
<!-- OpenAPI Generator Maven Plugin -->
41+
<plugin>
42+
<groupId>org.openapitools</groupId>
43+
<artifactId>openapi-generator-maven-plugin</artifactId>
44+
<version>7.17.0</version>
45+
<configuration>
46+
<generatorName>spring</generatorName>
47+
<apiPackage>com.dev.ucp.service.shopping</apiPackage>
48+
<modelPackage>com.dev.ucp.service.shopping.model</modelPackage>
49+
<output>${project.basedir}/generated</output>
50+
<modelNameMappings>
51+
<nameMapping>Postal_Address_1=PostalAddress</nameMapping>
52+
<nameMapping>Item_Response_1=ItemResponse</nameMapping>
53+
</modelNameMappings>
54+
<typeMappings>
55+
<typeMapping>AnyType=JsonNode</typeMapping>
56+
<typeMapping>object=JsonNode</typeMapping>
57+
</typeMappings>
58+
<importMappings>
59+
<importMapping>JsonNode=com.fasterxml.jackson.databind.JsonNode</importMapping>
60+
</importMappings>
61+
<configOptions>
62+
<generateBuilders>true</generateBuilders>
63+
<annotationLibrary>swagger2</annotationLibrary>
64+
<dateLibrary>java8</dateLibrary>
65+
<useSpringBoot3>true</useSpringBoot3>
66+
<disallowAdditionalPropertiesIfNotPresent>false</disallowAdditionalPropertiesIfNotPresent>
67+
</configOptions>
68+
<generateSupportingFiles>false</generateSupportingFiles>
69+
<generateApis>false</generateApis>
70+
</configuration>
71+
<executions>
72+
<execution>
73+
<id>generate-core-models</id>
74+
<goals>
75+
<goal>generate</goal>
76+
</goals>
77+
<configuration>
78+
<inputSpec>${spec.dir}/services/shopping/rest.openapi.json</inputSpec>
79+
<generateApis>true</generateApis>
80+
<cleanupOutput>true</cleanupOutput>
81+
<configOptions combine.children="append">
82+
<interfaceOnly>true</interfaceOnly>
83+
</configOptions>
84+
</configuration>
85+
</execution>
86+
<execution>
87+
<id>generate-extensions</id>
88+
<goals>
89+
<goal>generate</goal>
90+
</goals>
91+
<configuration>
92+
<inputSpec>${basedir}/src/main/resources/openapi.extensions.json</inputSpec>
93+
</configuration>
94+
</execution>
95+
</executions>
96+
</plugin>
97+
<!-- License Maven Plugin to add license headers to generated files -->
98+
<plugin>
99+
<groupId>com.mycila</groupId>
100+
<artifactId>license-maven-plugin</artifactId>
101+
<version>4.6</version>
102+
<configuration>
103+
<licenseName>apache_v2</licenseName>
104+
<header>${project.basedir}/license_template.txt</header>
105+
<mapping>
106+
<java>SLASHSTAR_STYLE</java>
107+
</mapping>
108+
<includes>
109+
<include>**/*.java</include>
110+
</includes>
111+
<strictCheck>true</strictCheck>
112+
</configuration>
113+
<executions>
114+
<execution>
115+
<id>add-license-headers</id>
116+
<phase>generate-sources</phase>
117+
<goals>
118+
<goal>format</goal>
119+
</goals>
120+
<configuration>
121+
<basedir>${project.basedir}/generated</basedir>
122+
</configuration>
123+
</execution>
124+
</executions>
125+
</plugin>
126+
</plugins>
127+
</build>
128+
</project>

0 commit comments

Comments
 (0)