From 3f6c0e16735ca0588e9caeead7964ec617a90bea Mon Sep 17 00:00:00 2001 From: Maksim Semenov Date: Mon, 26 Jan 2026 17:50:05 +0000 Subject: [PATCH] Add Java (Spring) demo application. --- .gitignore | 1 + rest/java-spring/README.md | 241 ++++++ rest/java-spring/codegen.pom.xml | 128 ++++ .../ucp/conformance/SimulationController.java | 66 ++ rest/java-spring/generate_ucp_models.sh | 51 ++ .../service/shopping/CheckoutSessionsApi.java | 379 ++++++++++ .../service/shopping/model/Adjustment.java | 406 +++++++++++ .../model/AdjustmentLineItemsInner.java | 202 ++++++ .../dev/ucp/service/shopping/model/Buyer.java | 328 +++++++++ .../shopping/model/CapabilityResponse.java | 361 +++++++++ .../shopping/model/CardCredential.java | 456 ++++++++++++ .../shopping/model/CardPaymentInstrument.java | 562 ++++++++++++++ .../shopping/model/CheckoutCreateRequest.java | 328 +++++++++ .../shopping/model/CheckoutResponse.java | 683 ++++++++++++++++++ .../shopping/model/CheckoutUpdateRequest.java | 359 +++++++++ .../model/CompleteCheckoutRequest.java | 251 +++++++ .../shopping/model/DiscountRequest.java | 212 ++++++ .../model/DiscountRequestAppliedInner.java | 403 +++++++++++ ...ntRequestAppliedInnerAllocationsInner.java | 203 ++++++ .../shopping/model/DiscountResponse.java | 212 ++++++ .../service/shopping/model/Expectation.java | 374 ++++++++++ .../model/ExpectationLineItemsInner.java | 202 ++++++ .../FulfillmentAvailableMethodResponse.java | 378 ++++++++++ .../model/FulfillmentDestinationRequest.java | 47 ++ .../model/FulfillmentDestinationResponse.java | 47 ++ .../shopping/model/FulfillmentEvent.java | 401 ++++++++++ .../model/FulfillmentEventLineItemsInner.java | 202 ++++++ .../model/FulfillmentGroupCreateRequest.java | 229 ++++++ .../model/FulfillmentGroupResponse.java | 352 +++++++++ .../model/FulfillmentMethodCreateRequest.java | 427 +++++++++++ .../model/FulfillmentMethodResponse.java | 459 ++++++++++++ .../model/FulfillmentOptionResponse.java | 420 +++++++++++ .../shopping/model/FulfillmentRequest.java | 173 +++++ .../shopping/model/FulfillmentResponse.java | 213 ++++++ .../shopping/model/ItemCreateRequest.java | 170 +++++ .../service/shopping/model/ItemResponse.java | 264 +++++++ .../shopping/model/ItemUpdateRequest.java | 170 +++++ .../shopping/model/LineItemCreateRequest.java | 204 ++++++ .../shopping/model/LineItemResponse.java | 309 ++++++++ .../shopping/model/LineItemUpdateRequest.java | 264 +++++++ .../dev/ucp/service/shopping/model/Link.java | 230 ++++++ .../ucp/service/shopping/model/Message.java | 47 ++ .../service/shopping/model/MessageError.java | 430 +++++++++++ .../service/shopping/model/MessageInfo.java | 361 +++++++++ .../shopping/model/MessageWarning.java | 362 ++++++++++ .../dev/ucp/service/shopping/model/Order.java | 423 +++++++++++ .../shopping/model/OrderConfirmation.java | 203 ++++++ .../model/OrderEventWebhook200Response.java | 176 +++++ .../model/OrderEventWebhookRequest.java | 537 ++++++++++++++ .../shopping/model/OrderFulfillment.java | 213 ++++++ .../service/shopping/model/OrderLineItem.java | 377 ++++++++++ .../shopping/model/OrderLineItemQuantity.java | 204 ++++++ .../shopping/model/PaymentCreateRequest.java | 203 ++++++ .../shopping/model/PaymentCredential.java | 47 ++ .../service/shopping/model/PaymentData.java | 172 +++++ .../model/PaymentHandlerResponse.java | 381 ++++++++++ .../shopping/model/PaymentResponse.java | 254 +++++++ .../shopping/model/PaymentUpdateRequest.java | 203 ++++++ .../service/shopping/model/PostalAddress.java | 430 +++++++++++ .../shopping/model/RetailLocationRequest.java | 252 +++++++ .../model/RetailLocationResponse.java | 283 ++++++++ .../model/ShippingDestinationRequest.java | 510 +++++++++++++ .../model/ShippingDestinationResponse.java | 521 +++++++++++++ .../model/TokenCredentialResponse.java | 222 ++++++ .../service/shopping/model/TotalResponse.java | 278 +++++++ .../shopping/model/UCPCheckoutResponse.java | 215 ++++++ .../shopping/model/UCPOrderResponse.java | 215 ++++++ rest/java-spring/license_template.txt | 13 + rest/java-spring/pom.xml | 190 +++++ .../java/com/dev/ucp/ExtensionsHelper.java | 143 ++++ .../com/dev/ucp/ExtensionsHelper.java.orig | 40 + .../java/com/dev/ucp/FulfillmentManager.java | 338 +++++++++ .../java/com/dev/ucp/FulfillmentUtils.java | 107 +++ .../com/dev/ucp/HttpDebugLoggingFilter.java | 98 +++ .../java/com/dev/ucp/InventoryManager.java | 112 +++ .../main/java/com/dev/ucp/JacksonConfig.java | 166 +++++ .../java/com/dev/ucp/LineItemConverter.java | 184 +++++ .../com/dev/ucp/OrderWebHookNotifier.java | 259 +++++++ .../com/dev/ucp/PlatformProfileResolver.java | 77 ++ .../main/java/com/dev/ucp/SpringDemoApp.java | 42 ++ .../java/com/dev/ucp/actions/BaseAction.java | 116 +++ .../dev/ucp/actions/BaseCheckoutAction.java | 281 +++++++ .../dev/ucp/actions/CancelCheckoutAction.java | 44 ++ .../ucp/actions/CompleteCheckoutAction.java | 204 ++++++ .../dev/ucp/actions/CreateCheckoutAction.java | 107 +++ .../dev/ucp/actions/GetCheckoutAction.java | 41 ++ .../dev/ucp/actions/UpdateCheckoutAction.java | 88 +++ .../com/dev/ucp/entities/AddressEntity.java | 137 ++++ .../ucp/entities/CheckoutSessionEntity.java | 76 ++ .../com/dev/ucp/entities/DiscountEntity.java | 97 +++ .../ucp/entities/IdempotencyRecordEntity.java | 104 +++ .../com/dev/ucp/entities/InventoryEntity.java | 75 ++ .../com/dev/ucp/entities/OrderEntity.java | 66 ++ .../com/dev/ucp/entities/ProductEntity.java | 85 +++ .../com/dev/ucp/entities/PromotionEntity.java | 98 +++ .../dev/ucp/entities/ShippingRateEntity.java | 109 +++ .../CheckoutNotModifiableException.java | 36 + .../CheckoutNotModifiableException.java.orig | 19 + .../ExtensionValidationException.java | 48 ++ .../IdempotencyConflictException.java | 37 + .../IdempotencyConflictException.java.orig | 20 + .../exceptions/InvalidRequestException.java | 36 + .../InvalidRequestException.java.orig | 19 + .../exceptions/PaymentFailedException.java | 35 + .../PaymentFailedException.java.orig | 18 + .../exceptions/ResourceNotFoundException.java | 36 + .../ResourceNotFoundException.java.orig | 19 + .../dev/ucp/service/DiscoveryController.java | 63 ++ .../service/UCPExceptionControllerAdvice.java | 146 ++++ .../com/dev/ucp/service/shopping/ApiUtil.java | 36 + .../CheckoutSessionsApiController.java | 233 ++++++ .../org/openapitools/RFC3339DateFormat.java | 53 ++ .../src/main/resources/application.properties | 14 + .../src/main/resources/cleanup.sql | 6 + rest/java-spring/src/main/resources/data.sql | 41 ++ .../src/main/resources/discovery_profile.json | 63 ++ .../main/resources/openapi.extensions.json | 24 + .../com/dev/ucp/FulfillmentManagerTest.java | 193 +++++ .../com/dev/ucp/InventoryManagerTest.java | 122 ++++ .../java/com/dev/ucp/JacksonConfigTest.java | 135 ++++ .../com/dev/ucp/LineItemConverterTest.java | 134 ++++ .../com/dev/ucp/OrderWebHookNotifierTest.java | 106 +++ .../java/com/dev/ucp/SpringDemoAppTests.java | 27 + .../ucp/actions/CancelCheckoutActionTest.java | 79 ++ .../actions/CompleteCheckoutActionTest.java | 126 ++++ .../ucp/actions/CreateCheckoutActionTest.java | 154 ++++ .../ucp/actions/GetCheckoutActionTest.java | 69 ++ .../ucp/actions/UpdateCheckoutActionTest.java | 103 +++ .../UCPExceptionControllerAdviceTest.java | 88 +++ 129 files changed, 24721 insertions(+) create mode 100644 rest/java-spring/README.md create mode 100644 rest/java-spring/codegen.pom.xml create mode 100644 rest/java-spring/conformance/src/main/java/com/dev/ucp/conformance/SimulationController.java create mode 100755 rest/java-spring/generate_ucp_models.sh create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApi.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Adjustment.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/AdjustmentLineItemsInner.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Buyer.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CapabilityResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardCredential.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardPaymentInstrument.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutUpdateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CompleteCheckoutRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInner.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInnerAllocationsInner.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Expectation.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ExpectationLineItemsInner.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentAvailableMethodResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEvent.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEventLineItemsInner.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentOptionResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemUpdateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemUpdateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Link.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Message.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageError.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageInfo.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageWarning.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Order.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderConfirmation.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhook200Response.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhookRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderFulfillment.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItem.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItemQuantity.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCreateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCredential.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentData.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentHandlerResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentUpdateRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PostalAddress.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationRequest.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TokenCredentialResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TotalResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPCheckoutResponse.java create mode 100644 rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPOrderResponse.java create mode 100644 rest/java-spring/license_template.txt create mode 100644 rest/java-spring/pom.xml create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/FulfillmentManager.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/FulfillmentUtils.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/HttpDebugLoggingFilter.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/InventoryManager.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/JacksonConfig.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/LineItemConverter.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/OrderWebHookNotifier.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/PlatformProfileResolver.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/SpringDemoApp.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/BaseAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/BaseCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/CancelCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/CompleteCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/CreateCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/GetCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/actions/UpdateCheckoutAction.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/AddressEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/CheckoutSessionEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/DiscountEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/IdempotencyRecordEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/InventoryEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/OrderEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/ProductEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/PromotionEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/entities/ShippingRateEntity.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/ExtensionValidationException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java.orig create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/service/DiscoveryController.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/service/UCPExceptionControllerAdvice.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/service/shopping/ApiUtil.java create mode 100644 rest/java-spring/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApiController.java create mode 100644 rest/java-spring/src/main/java/org/openapitools/RFC3339DateFormat.java create mode 100644 rest/java-spring/src/main/resources/application.properties create mode 100644 rest/java-spring/src/main/resources/cleanup.sql create mode 100644 rest/java-spring/src/main/resources/data.sql create mode 100644 rest/java-spring/src/main/resources/discovery_profile.json create mode 100644 rest/java-spring/src/main/resources/openapi.extensions.json create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/FulfillmentManagerTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/InventoryManagerTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/JacksonConfigTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/LineItemConverterTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/OrderWebHookNotifierTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/SpringDemoAppTests.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/actions/CancelCheckoutActionTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/actions/CompleteCheckoutActionTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/actions/CreateCheckoutActionTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/actions/GetCheckoutActionTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/actions/UpdateCheckoutActionTest.java create mode 100644 rest/java-spring/src/test/java/com/dev/ucp/service/UCPExceptionControllerAdviceTest.java diff --git a/.gitignore b/.gitignore index e10ddfd..13cf2f9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .ruff_cache uv.lock .venv +rest/java-spring/target diff --git a/rest/java-spring/README.md b/rest/java-spring/README.md new file mode 100644 index 0000000..1bd7ecf --- /dev/null +++ b/rest/java-spring/README.md @@ -0,0 +1,241 @@ + + +# UCP Merchant Server (Java/Spring Boot) + +This directory contains a reference implementation of a Universal Commerce +Protocol (UCP) server built with Java 25 and Spring Boot 3.5.9. It demonstrates +how to implement the UCP specifications for checkout sessions, including modular +support for fulfillment, discounts, and order extensions. + +## Project Structure + +* `src/main/java/com/dev/ucp/actions`: Contains the business logic for + checkout operations (Create, Update, Complete, etc.). +* `src/main/java/com/dev/ucp/entities`: JPA entities with nested repository + interfaces for persistence. +* `src/main/java/com/dev/ucp/service`: REST controllers and exception + handlers. +* `src/main/resources/openapi.extensions.json`: Defines modular UCP schema + extensions (e.g., fulfillment, discounts) used for model generation. +* `generated/`: Source root for models and interfaces generated from OpenAPI + specs. +* `pom.xml`: Main Maven configuration. +* `codegen.pom.xml`: Specialized Maven configuration for OpenAPI code + generation. + +## Prerequisites + +* Java 25 JDK +* Apache Maven 3.9+ +* (Optional) Official `ucp` repository for + [model generation](#generating-models). + +## Setup + +```shell +# Clone the samples repository if you haven't already +git clone https://github.com/Universal-Commerce-Protocol/samples.git +cd samples/rest/java-spring + +# Build the project (pre-generated models are included in the repository) +mvn clean install +``` + +## Run the Server + +Start the server on port 8182. Using the `conformance` profile enables +simulation endpoints required for testing. + +```bash +# Start the server in the background, redirecting output to server.log +mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8182 -Pconformance > server.log 2>&1 & +SERVER_PID=$! +``` + +Note: You can monitor the logs with `tail -f server.log`. + +## Discovery & Health Check + +To verify the operational status and discover the capabilities of your local +server, you can query the UCP discovery endpoint. This serves as a primary +validation that the service is correctly configured and accessible. + +```bash +curl -s http://localhost:8182/.well-known/ucp +``` + +### Example Discovery Response: + +```json +{ + "ucp": { + "version": "2026-01-11", + "services": { + "dev.ucp.shopping": { + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping", + "rest": { + "schema": "https://ucp.dev/services/shopping/openapi.json", + "endpoint": "http://localhost:8182/ucp" + } + } + }, + "capabilities": [ + { + "name": "dev.ucp.shopping.checkout", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/checkout", + "schema": "https://ucp.dev/schemas/shopping/checkout.json" + }, + { + "name": "dev.ucp.shopping.discount", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/discount", + "schema": "https://ucp.dev/schemas/shopping/discount.json", + "extends": "dev.ucp.shopping.checkout" + }, + { + "name": "dev.ucp.shopping.fulfillment", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/fulfillment", + "schema": "https://ucp.dev/schemas/shopping/fulfillment.json", + "extends": "dev.ucp.shopping.checkout" + }, + { + "name": "dev.ucp.shopping.order", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/order", + "schema": "https://ucp.dev/schemas/shopping/order.json" + } + ] + }, + "payment": { + "handlers": [ + { + "id": "mock_payment_handler", + "name": "dev.ucp.mock_payment", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/mock", + "config_schema": "https://ucp.dev/schemas/mock.json", + "instrument_schemas": [ + "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json" + ], + "config": { + "supported_tokens": [ + "success_token", + "fail_token" + ] + } + } + ] + } +} +``` + +## Running Conformance Tests + +To verify that this server implementation complies with the UCP specifications, +please use the official +[UCP Conformance Test Suite](https://github.com/Universal-Commerce-Protocol/conformance). +Refer to its documentation for prerequisites, installation, and detailed +execution instructions. + +Assuming the conformance suite is configured to target port **8182**, you can +run the fulfillment tests with a command similar to: + +```bash +.venv/bin/python3 fulfillment_test.py \ + --server_url=http://localhost:8182 \ + --simulation_secret=super-secret-sim-key \ + --conformance_input=test_data/flower_shop/conformance_input.json +``` + +## Testing Endpoints + +The server exposes additional endpoints for simulation and testing purposes. +These are only available when the server is started with the `conformance` +profile. + +**Note:** If you are relying on hot-swapping, ensure that you also include the +`-Pconformance` flag during your build or compilation steps to keep these +test-only classes active. + +* `POST /testing/simulate-shipping/{id}`: Triggers a simulated "order shipped" + event for the specified order ID. This updates the order status and sends a + webhook notification. This endpoint requires the `Simulation-Secret` header. + +## Database & Test Data + +The server uses an in-memory H2 database for the demo. The schema and initial +flower shop data are automatically loaded on startup. + +If you wish to add custom products, discounts, or inventory for testing, you can +modify `src/main/resources/data.sql` + +## Generating Models + +This project uses OpenAPI Generator to maintain strict adherence to the UCP +schemas. Pre-generated models are provided in the `generated/` directory. + +### Workspace Structure + +The generation process assumes that the main `ucp` repository (containing the +official specifications) is checked out in a directory sibling to the `samples` +repository: + +```text +/work/ + ├── ucp/ <-- Official specifications + └── samples/ <-- This repository +``` + +### Regeneration Steps + +If the specifications change, you can regenerate the models: + +1. **Run the preparation and generation script**: + + ```bash + ./generate_ucp_models.sh [optional_path_to_ucp_root] + ``` + + By default, it looks for UCP at `../../../ucp`. + +This script copies the official specs to `target/spec`, applies necessary +compatibility transformations, and runs the code generator. + +The generated code will be placed in the `generated/` directory and is +automatically included in the main build's classpath. + +## Terminate the server + +Terminate the server process when finished: + +```bash +kill -9 $(lsof -t -i :8182) +``` + +## Developer Guide: Core Framework Components + +The following files provide essential infrastructure for UCP compliance and are designed to be reused or extended as the protocol evolves: + +* **`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. +* **`DiscoveryController.java`**: Implements the mandatory UCP discovery endpoint (`.well-known/ucp`), advertising the server's supported protocol versions, services, and capabilities to the platform. +* **`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. +* **`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`. +* **`UCPExceptionControllerAdvice.java`**: Ensures that all service-layer exceptions are automatically transformed into structured, UCP-compliant error responses with appropriate protocol-standard error codes. +* **`PlatformProfileResolver.java`**: Orchestrates the discovery and resolution of platform UCP profiles, enabling the server to dynamically determine capability endpoints and webhook configurations. diff --git a/rest/java-spring/codegen.pom.xml b/rest/java-spring/codegen.pom.xml new file mode 100644 index 0000000..a4c7906 --- /dev/null +++ b/rest/java-spring/codegen.pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.9 + + + com.dev.ucp + api-generator + 2026.1.11-1-SNAPSHOT + ucp-api-generator + UCP API Generator + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + + + + + + + 25 + ${project.basedir}/spec + + + + + + org.openapitools + openapi-generator-maven-plugin + 7.17.0 + + spring + com.dev.ucp.service.shopping + com.dev.ucp.service.shopping.model + ${project.basedir}/generated + + Postal_Address_1=PostalAddress + Item_Response_1=ItemResponse + + + AnyType=JsonNode + object=JsonNode + + + JsonNode=com.fasterxml.jackson.databind.JsonNode + + + true + swagger2 + java8 + true + false + + false + false + + + + generate-core-models + + generate + + + ${spec.dir}/services/shopping/rest.openapi.json + true + true + + true + + + + + generate-extensions + + generate + + + ${basedir}/src/main/resources/openapi.extensions.json + + + + + + + com.mycila + license-maven-plugin + 4.6 + + apache_v2 +
${project.basedir}/license_template.txt
+ + SLASHSTAR_STYLE + + + **/*.java + + true +
+ + + add-license-headers + generate-sources + + format + + + ${project.basedir}/generated + + + +
+
+
+
diff --git a/rest/java-spring/conformance/src/main/java/com/dev/ucp/conformance/SimulationController.java b/rest/java-spring/conformance/src/main/java/com/dev/ucp/conformance/SimulationController.java new file mode 100644 index 0000000..0de4221 --- /dev/null +++ b/rest/java-spring/conformance/src/main/java/com/dev/ucp/conformance/SimulationController.java @@ -0,0 +1,66 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.conformance; + +import com.dev.ucp.OrderWebHookNotifier; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +/** + * Optional testing controller part of the UCP conformance extension. + * + *

This controller provides endpoints to simulate post-checkout lifecycle events that would + * normally occur within internal merchant systems (like ERP or WMS). It is intended only for + * protocol verification and automated conformance testing. + * + *

This controller is only active when the 'conformance' Maven profile is enabled. + */ +@RestController +public class SimulationController { + + private static final String SIMULATION_SECRET = "super-secret-sim-key"; + + @Autowired private OrderWebHookNotifier webhookNotifier; + + /** + * Manually triggers the 'order_shipped' webhook event for a given order. + * + *

The request must include a valid 'Simulation-Secret' header to be authorized. + * + * @param orderId the unique identifier of the order to simulate shipping for. + * @param secret the secret key provided in the 'Simulation-Secret' request header. + * @return a {@link ResponseEntity} with {@code 200 OK} if successful, or {@code 403 Forbidden} if + * the secret is invalid. + */ + @PostMapping("/testing/simulate-shipping/{orderId}") + public ResponseEntity simulateShipping( + @PathVariable String orderId, + @RequestHeader(value = "Simulation-Secret", required = false) String secret) { + + if (!SIMULATION_SECRET.equals(secret)) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); + } + + webhookNotifier.triggerShipping(orderId); + return ResponseEntity.ok().build(); + } +} diff --git a/rest/java-spring/generate_ucp_models.sh b/rest/java-spring/generate_ucp_models.sh new file mode 100755 index 0000000..d6b1c49 --- /dev/null +++ b/rest/java-spring/generate_ucp_models.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Copyright 2026 UCP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Default UCP root directory assuming standard checkout structure +UCP_ROOT_DEFAULT="../../../ucp" +UCP_ROOT="${1:-$UCP_ROOT_DEFAULT}" +SPEC_SRC="$UCP_ROOT/spec" +SPEC_DEST="target/spec" + +if [ ! -d "$SPEC_SRC" ]; then + echo "Error: UCP spec directory not found at $SPEC_SRC" + echo "Usage: $0 [path_to_ucp_root]" + exit 1 +fi + +echo "Preparing specs from $SPEC_SRC into $SPEC_DEST..." + +# Clean and recreate target/spec +rm -rf "$SPEC_DEST" +mkdir -p "$SPEC_DEST" + +# Copy original specs +cp -r "$SPEC_SRC/"* "$SPEC_DEST/" + +# Apply transformations for OpenAPI generator compatibility +# 1. Replace ucp.dev URLs with local relative paths for generator to find local files +sed -i "s/https:\/\/ucp.dev\//..\/..\//g" "$SPEC_DEST/services/shopping/rest.openapi.json" + +# 2. Remove $id fields to prevent the OpenAPI 3.1 generator from using them as +# base URIs for reference resolution. This ensures local file-based resolution +# and avoids errors when specs are not yet hosted or when testing local changes. +find "$SPEC_DEST" -name "*.json" -exec sed -i '/"$id":/d' {} + + +echo "Specs prepared. Running code generation..." + +# Run the code generator pointing to the modified specs +mvn -f codegen.pom.xml generate-sources -Dspec.dir="$SPEC_DEST" \ No newline at end of file diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApi.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApi.java new file mode 100644 index 0000000..3a17e23 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApi.java @@ -0,0 +1,379 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (7.17.0). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +package com.dev.ucp.service.shopping; + +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.dev.ucp.service.shopping.model.CompleteCheckoutRequest; +import org.springframework.lang.Nullable; +import java.util.UUID; +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import jakarta.annotation.Generated; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +@Validated +@Tag(name = "checkout-sessions", description = "the checkout-sessions API") +public interface CheckoutSessionsApi { + + default Optional getRequest() { + return Optional.empty(); + } + + String PATH_CANCEL_CHECKOUT = "/checkout-sessions/{id}/cancel"; + /** + * POST /checkout-sessions/{id}/cancel : Cancel Checkout + * Cancel a checkout session + * + * @param requestSignature Ensure the authenticity and integrity of an HTTP message. (required) + * @param idempotencyKey Ensures duplicate operations don't happen during retries. (required) + * @param requestId For tracing the requests across network layers and components. (required) + * @param ucPAgent Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\". (required) + * @param id The unique identifier of the checkout session. (required) + * @param authorization Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code). (optional) + * @param xAPIKey Authenticates the platform with a reusable api key allocated to the platform by the business. (optional) + * @param userAgent Identifies the user agent string making the call. (optional) + * @param contentType Representation Metadata. Tells the receiver what the data in the message body actually is. (optional) + * @param accept Content Negotiation. The client tells the server what data formats it is capable of understanding. (optional) + * @param acceptLanguage Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities. (optional) + * @param acceptEncoding Compression. The client tells the server which content-codings it supports, usually for compression (optional) + * @return Checkout session canceled (status code 200) + */ + @Operation( + operationId = "cancelCheckout", + summary = "Cancel Checkout", + description = "Cancel a checkout session", + responses = { + @ApiResponse(responseCode = "200", description = "Checkout session canceled", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = CheckoutResponse.class)) + }) + } + ) + @RequestMapping( + method = RequestMethod.POST, + value = CheckoutSessionsApi.PATH_CANCEL_CHECKOUT, + produces = { "application/json" } + ) + default ResponseEntity cancelCheckout( + @NotNull @Parameter(name = "Request-Signature", description = "Ensure the authenticity and integrity of an HTTP message.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Signature", required = true) String requestSignature, + @NotNull @Parameter(name = "Idempotency-Key", description = "Ensures duplicate operations don't happen during retries.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Idempotency-Key", required = true) UUID idempotencyKey, + @NotNull @Parameter(name = "Request-Id", description = "For tracing the requests across network layers and components.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Id", required = true) UUID requestId, + @NotNull @Parameter(name = "UCP-Agent", description = "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\".", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "UCP-Agent", required = true) String ucPAgent, + @NotNull @Parameter(name = "id", description = "The unique identifier of the checkout session.", required = true, in = ParameterIn.PATH) @PathVariable("id") String id, + @Parameter(name = "Authorization", description = "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code).", in = ParameterIn.HEADER) @RequestHeader(value = "Authorization", required = false) @Nullable String authorization, + @Parameter(name = "X-API-Key", description = "Authenticates the platform with a reusable api key allocated to the platform by the business.", in = ParameterIn.HEADER) @RequestHeader(value = "X-API-Key", required = false) @Nullable String xAPIKey, + @Parameter(name = "User-Agent", description = "Identifies the user agent string making the call.", in = ParameterIn.HEADER) @RequestHeader(value = "User-Agent", required = false) @Nullable String userAgent, + @Parameter(name = "Content-Type", description = "Representation Metadata. Tells the receiver what the data in the message body actually is.", in = ParameterIn.HEADER) @RequestHeader(value = "Content-Type", required = false) @Nullable String contentType, + @Parameter(name = "Accept", description = "Content Negotiation. The client tells the server what data formats it is capable of understanding.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept", required = false) @Nullable String accept, + @Parameter(name = "Accept-Language", description = "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Language", required = false) @Nullable String acceptLanguage, + @Parameter(name = "Accept-Encoding", description = "Compression. The client tells the server which content-codings it supports, usually for compression", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Encoding", required = false) @Nullable String acceptEncoding + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"ucp\" : { \"capabilities\" : [ { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" }, { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" } ], \"version\" : \"version\" }, \"line_items\" : [ { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] }, { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] } ], \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ], \"continue_url\" : \"https://openapi-generator.tech\", \"buyer\" : { \"full_name\" : \"full_name\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"first_name\" : \"first_name\", \"email\" : \"email\" }, \"expires_at\" : \"2000-01-23T04:56:07.000+00:00\", \"messages\" : [ { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" }, { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" } ], \"currency\" : \"currency\", \"links\" : [ { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" }, { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" } ], \"payment\" : { \"instruments\" : [ { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" }, { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" } ], \"selected_instrument_id\" : \"selected_instrument_id\", \"handlers\" : [ { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" }, { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" } ] }, \"id\" : \"id\", \"status\" : \"incomplete\", \"order\" : { \"id\" : \"id\", \"permalink_url\" : \"https://openapi-generator.tech\" } }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + String PATH_COMPLETE_CHECKOUT = "/checkout-sessions/{id}/complete"; + /** + * POST /checkout-sessions/{id}/complete : Complete Checkout + * Place the order + * + * @param requestSignature Ensure the authenticity and integrity of an HTTP message. (required) + * @param idempotencyKey Ensures duplicate operations don't happen during retries. (required) + * @param requestId For tracing the requests across network layers and components. (required) + * @param ucPAgent Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\". (required) + * @param id The unique identifier of the checkout session. (required) + * @param completeCheckoutRequest (required) + * @param authorization Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code). (optional) + * @param xAPIKey Authenticates the platform with a reusable api key allocated to the platform by the business. (optional) + * @param userAgent Identifies the user agent string making the call. (optional) + * @param contentType Representation Metadata. Tells the receiver what the data in the message body actually is. (optional) + * @param accept Content Negotiation. The client tells the server what data formats it is capable of understanding. (optional) + * @param acceptLanguage Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities. (optional) + * @param acceptEncoding Compression. The client tells the server which content-codings it supports, usually for compression (optional) + * @return Checkout session completed (status code 200) + */ + @Operation( + operationId = "completeCheckout", + summary = "Complete Checkout", + description = "Place the order", + responses = { + @ApiResponse(responseCode = "200", description = "Checkout session completed", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = CheckoutResponse.class)) + }) + } + ) + @RequestMapping( + method = RequestMethod.POST, + value = CheckoutSessionsApi.PATH_COMPLETE_CHECKOUT, + produces = { "application/json" }, + consumes = { "application/json" } + ) + default ResponseEntity completeCheckout( + @NotNull @Parameter(name = "Request-Signature", description = "Ensure the authenticity and integrity of an HTTP message.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Signature", required = true) String requestSignature, + @NotNull @Parameter(name = "Idempotency-Key", description = "Ensures duplicate operations don't happen during retries.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Idempotency-Key", required = true) UUID idempotencyKey, + @NotNull @Parameter(name = "Request-Id", description = "For tracing the requests across network layers and components.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Id", required = true) UUID requestId, + @NotNull @Parameter(name = "UCP-Agent", description = "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\".", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "UCP-Agent", required = true) String ucPAgent, + @NotNull @Parameter(name = "id", description = "The unique identifier of the checkout session.", required = true, in = ParameterIn.PATH) @PathVariable("id") String id, + @Parameter(name = "CompleteCheckoutRequest", description = "", required = true) @Valid @RequestBody CompleteCheckoutRequest completeCheckoutRequest, + @Parameter(name = "Authorization", description = "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code).", in = ParameterIn.HEADER) @RequestHeader(value = "Authorization", required = false) @Nullable String authorization, + @Parameter(name = "X-API-Key", description = "Authenticates the platform with a reusable api key allocated to the platform by the business.", in = ParameterIn.HEADER) @RequestHeader(value = "X-API-Key", required = false) @Nullable String xAPIKey, + @Parameter(name = "User-Agent", description = "Identifies the user agent string making the call.", in = ParameterIn.HEADER) @RequestHeader(value = "User-Agent", required = false) @Nullable String userAgent, + @Parameter(name = "Content-Type", description = "Representation Metadata. Tells the receiver what the data in the message body actually is.", in = ParameterIn.HEADER) @RequestHeader(value = "Content-Type", required = false) @Nullable String contentType, + @Parameter(name = "Accept", description = "Content Negotiation. The client tells the server what data formats it is capable of understanding.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept", required = false) @Nullable String accept, + @Parameter(name = "Accept-Language", description = "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Language", required = false) @Nullable String acceptLanguage, + @Parameter(name = "Accept-Encoding", description = "Compression. The client tells the server which content-codings it supports, usually for compression", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Encoding", required = false) @Nullable String acceptEncoding + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"ucp\" : { \"capabilities\" : [ { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" }, { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" } ], \"version\" : \"version\" }, \"line_items\" : [ { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] }, { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] } ], \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ], \"continue_url\" : \"https://openapi-generator.tech\", \"buyer\" : { \"full_name\" : \"full_name\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"first_name\" : \"first_name\", \"email\" : \"email\" }, \"expires_at\" : \"2000-01-23T04:56:07.000+00:00\", \"messages\" : [ { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" }, { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" } ], \"currency\" : \"currency\", \"links\" : [ { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" }, { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" } ], \"payment\" : { \"instruments\" : [ { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" }, { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" } ], \"selected_instrument_id\" : \"selected_instrument_id\", \"handlers\" : [ { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" }, { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" } ] }, \"id\" : \"id\", \"status\" : \"incomplete\", \"order\" : { \"id\" : \"id\", \"permalink_url\" : \"https://openapi-generator.tech\" } }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + String PATH_CREATE_CHECKOUT = "/checkout-sessions"; + /** + * POST /checkout-sessions : Create Checkout + * Create a checkout session. Called as soon as a user adds an item to a cart. + * + * @param requestSignature Ensure the authenticity and integrity of an HTTP message. (required) + * @param idempotencyKey Ensures duplicate operations don't happen during retries. (required) + * @param requestId For tracing the requests across network layers and components. (required) + * @param ucPAgent Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\". (required) + * @param checkoutCreateRequest (required) + * @param authorization Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code). (optional) + * @param xAPIKey Authenticates the platform with a reusable api key allocated to the platform by the business. (optional) + * @param userAgent Identifies the user agent string making the call. (optional) + * @param contentType Representation Metadata. Tells the receiver what the data in the message body actually is. (optional) + * @param accept Content Negotiation. The client tells the server what data formats it is capable of understanding. (optional) + * @param acceptLanguage Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities. (optional) + * @param acceptEncoding Compression. The client tells the server which content-codings it supports, usually for compression (optional) + * @return Checkout session created (status code 201) + */ + @Operation( + operationId = "createCheckout", + summary = "Create Checkout", + description = "Create a checkout session. Called as soon as a user adds an item to a cart.", + responses = { + @ApiResponse(responseCode = "201", description = "Checkout session created", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = CheckoutResponse.class)) + }) + } + ) + @RequestMapping( + method = RequestMethod.POST, + value = CheckoutSessionsApi.PATH_CREATE_CHECKOUT, + produces = { "application/json" }, + consumes = { "application/json" } + ) + default ResponseEntity createCheckout( + @NotNull @Parameter(name = "Request-Signature", description = "Ensure the authenticity and integrity of an HTTP message.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Signature", required = true) String requestSignature, + @NotNull @Parameter(name = "Idempotency-Key", description = "Ensures duplicate operations don't happen during retries.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Idempotency-Key", required = true) UUID idempotencyKey, + @NotNull @Parameter(name = "Request-Id", description = "For tracing the requests across network layers and components.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Id", required = true) UUID requestId, + @NotNull @Parameter(name = "UCP-Agent", description = "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\".", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "UCP-Agent", required = true) String ucPAgent, + @Parameter(name = "CheckoutCreateRequest", description = "", required = true) @Valid @RequestBody CheckoutCreateRequest checkoutCreateRequest, + @Parameter(name = "Authorization", description = "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code).", in = ParameterIn.HEADER) @RequestHeader(value = "Authorization", required = false) @Nullable String authorization, + @Parameter(name = "X-API-Key", description = "Authenticates the platform with a reusable api key allocated to the platform by the business.", in = ParameterIn.HEADER) @RequestHeader(value = "X-API-Key", required = false) @Nullable String xAPIKey, + @Parameter(name = "User-Agent", description = "Identifies the user agent string making the call.", in = ParameterIn.HEADER) @RequestHeader(value = "User-Agent", required = false) @Nullable String userAgent, + @Parameter(name = "Content-Type", description = "Representation Metadata. Tells the receiver what the data in the message body actually is.", in = ParameterIn.HEADER) @RequestHeader(value = "Content-Type", required = false) @Nullable String contentType, + @Parameter(name = "Accept", description = "Content Negotiation. The client tells the server what data formats it is capable of understanding.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept", required = false) @Nullable String accept, + @Parameter(name = "Accept-Language", description = "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Language", required = false) @Nullable String acceptLanguage, + @Parameter(name = "Accept-Encoding", description = "Compression. The client tells the server which content-codings it supports, usually for compression", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Encoding", required = false) @Nullable String acceptEncoding + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"ucp\" : { \"capabilities\" : [ { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" }, { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" } ], \"version\" : \"version\" }, \"line_items\" : [ { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] }, { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] } ], \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ], \"continue_url\" : \"https://openapi-generator.tech\", \"buyer\" : { \"full_name\" : \"full_name\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"first_name\" : \"first_name\", \"email\" : \"email\" }, \"expires_at\" : \"2000-01-23T04:56:07.000+00:00\", \"messages\" : [ { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" }, { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" } ], \"currency\" : \"currency\", \"links\" : [ { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" }, { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" } ], \"payment\" : { \"instruments\" : [ { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" }, { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" } ], \"selected_instrument_id\" : \"selected_instrument_id\", \"handlers\" : [ { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" }, { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" } ] }, \"id\" : \"id\", \"status\" : \"incomplete\", \"order\" : { \"id\" : \"id\", \"permalink_url\" : \"https://openapi-generator.tech\" } }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + String PATH_GET_CHECKOUT = "/checkout-sessions/{id}"; + /** + * GET /checkout-sessions/{id} : Get Checkout + * Get the latest state of a checkout session. + * + * @param requestSignature Ensure the authenticity and integrity of an HTTP message. (required) + * @param requestId For tracing the requests across network layers and components. (required) + * @param ucPAgent Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\". (required) + * @param id The unique identifier of the checkout session. (required) + * @param authorization Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code). (optional) + * @param xAPIKey Authenticates the platform with a reusable api key allocated to the platform by the business. (optional) + * @param userAgent Identifies the user agent string making the call. (optional) + * @param contentType Representation Metadata. Tells the receiver what the data in the message body actually is. (optional) + * @param accept Content Negotiation. The client tells the server what data formats it is capable of understanding. (optional) + * @param acceptLanguage Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities. (optional) + * @param acceptEncoding Compression. The client tells the server which content-codings it supports, usually for compression (optional) + * @return Checkout session retrieved (status code 200) + */ + @Operation( + operationId = "getCheckout", + summary = "Get Checkout", + description = "Get the latest state of a checkout session.", + responses = { + @ApiResponse(responseCode = "200", description = "Checkout session retrieved", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = CheckoutResponse.class)) + }) + } + ) + @RequestMapping( + method = RequestMethod.GET, + value = CheckoutSessionsApi.PATH_GET_CHECKOUT, + produces = { "application/json" } + ) + default ResponseEntity getCheckout( + @NotNull @Parameter(name = "Request-Signature", description = "Ensure the authenticity and integrity of an HTTP message.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Signature", required = true) String requestSignature, + @NotNull @Parameter(name = "Request-Id", description = "For tracing the requests across network layers and components.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Id", required = true) UUID requestId, + @NotNull @Parameter(name = "UCP-Agent", description = "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\".", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "UCP-Agent", required = true) String ucPAgent, + @NotNull @Parameter(name = "id", description = "The unique identifier of the checkout session.", required = true, in = ParameterIn.PATH) @PathVariable("id") String id, + @Parameter(name = "Authorization", description = "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code).", in = ParameterIn.HEADER) @RequestHeader(value = "Authorization", required = false) @Nullable String authorization, + @Parameter(name = "X-API-Key", description = "Authenticates the platform with a reusable api key allocated to the platform by the business.", in = ParameterIn.HEADER) @RequestHeader(value = "X-API-Key", required = false) @Nullable String xAPIKey, + @Parameter(name = "User-Agent", description = "Identifies the user agent string making the call.", in = ParameterIn.HEADER) @RequestHeader(value = "User-Agent", required = false) @Nullable String userAgent, + @Parameter(name = "Content-Type", description = "Representation Metadata. Tells the receiver what the data in the message body actually is.", in = ParameterIn.HEADER) @RequestHeader(value = "Content-Type", required = false) @Nullable String contentType, + @Parameter(name = "Accept", description = "Content Negotiation. The client tells the server what data formats it is capable of understanding.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept", required = false) @Nullable String accept, + @Parameter(name = "Accept-Language", description = "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Language", required = false) @Nullable String acceptLanguage, + @Parameter(name = "Accept-Encoding", description = "Compression. The client tells the server which content-codings it supports, usually for compression", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Encoding", required = false) @Nullable String acceptEncoding + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"ucp\" : { \"capabilities\" : [ { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" }, { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" } ], \"version\" : \"version\" }, \"line_items\" : [ { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] }, { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] } ], \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ], \"continue_url\" : \"https://openapi-generator.tech\", \"buyer\" : { \"full_name\" : \"full_name\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"first_name\" : \"first_name\", \"email\" : \"email\" }, \"expires_at\" : \"2000-01-23T04:56:07.000+00:00\", \"messages\" : [ { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" }, { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" } ], \"currency\" : \"currency\", \"links\" : [ { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" }, { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" } ], \"payment\" : { \"instruments\" : [ { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" }, { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" } ], \"selected_instrument_id\" : \"selected_instrument_id\", \"handlers\" : [ { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" }, { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" } ] }, \"id\" : \"id\", \"status\" : \"incomplete\", \"order\" : { \"id\" : \"id\", \"permalink_url\" : \"https://openapi-generator.tech\" } }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + String PATH_UPDATE_CHECKOUT = "/checkout-sessions/{id}"; + /** + * PUT /checkout-sessions/{id} : Update Checkout + * If an optional field is provided in the request body, its value is treated as a complete replacement for the corresponding data. If optional field is omitted, then current checkout session is unchanged. + * + * @param requestSignature Ensure the authenticity and integrity of an HTTP message. (required) + * @param idempotencyKey Ensures duplicate operations don't happen during retries. (required) + * @param requestId For tracing the requests across network layers and components. (required) + * @param ucPAgent Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\". (required) + * @param id The unique identifier of the checkout session. (required) + * @param checkoutUpdateRequest (required) + * @param authorization Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code). (optional) + * @param xAPIKey Authenticates the platform with a reusable api key allocated to the platform by the business. (optional) + * @param userAgent Identifies the user agent string making the call. (optional) + * @param contentType Representation Metadata. Tells the receiver what the data in the message body actually is. (optional) + * @param accept Content Negotiation. The client tells the server what data formats it is capable of understanding. (optional) + * @param acceptLanguage Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities. (optional) + * @param acceptEncoding Compression. The client tells the server which content-codings it supports, usually for compression (optional) + * @return Checkout session updated (status code 200) + */ + @Operation( + operationId = "updateCheckout", + summary = "Update Checkout", + description = "If an optional field is provided in the request body, its value is treated as a complete replacement for the corresponding data. If optional field is omitted, then current checkout session is unchanged.", + responses = { + @ApiResponse(responseCode = "200", description = "Checkout session updated", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = CheckoutResponse.class)) + }) + } + ) + @RequestMapping( + method = RequestMethod.PUT, + value = CheckoutSessionsApi.PATH_UPDATE_CHECKOUT, + produces = { "application/json" }, + consumes = { "application/json" } + ) + default ResponseEntity updateCheckout( + @NotNull @Parameter(name = "Request-Signature", description = "Ensure the authenticity and integrity of an HTTP message.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Signature", required = true) String requestSignature, + @NotNull @Parameter(name = "Idempotency-Key", description = "Ensures duplicate operations don't happen during retries.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Idempotency-Key", required = true) UUID idempotencyKey, + @NotNull @Parameter(name = "Request-Id", description = "For tracing the requests across network layers and components.", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "Request-Id", required = true) UUID requestId, + @NotNull @Parameter(name = "UCP-Agent", description = "Identifies the UCP agent making the call. All requests MUST include the UCP-Agent header containing the platform profile URI using Dictionary Structured Field syntax (RFC 8941). Format: profile=\"https://platform.example/profile\".", required = true, in = ParameterIn.HEADER) @RequestHeader(value = "UCP-Agent", required = true) String ucPAgent, + @NotNull @Parameter(name = "id", description = "The unique identifier of the checkout session.", required = true, in = ParameterIn.PATH) @PathVariable("id") String id, + @Parameter(name = "CheckoutUpdateRequest", description = "", required = true) @Valid @RequestBody CheckoutUpdateRequest checkoutUpdateRequest, + @Parameter(name = "Authorization", description = "Should contain oauth token representing the following 2 schemes: 1. Platform self authenticating (client_credentials). 2. Platform authenticating on behalf of end user (authorization_code).", in = ParameterIn.HEADER) @RequestHeader(value = "Authorization", required = false) @Nullable String authorization, + @Parameter(name = "X-API-Key", description = "Authenticates the platform with a reusable api key allocated to the platform by the business.", in = ParameterIn.HEADER) @RequestHeader(value = "X-API-Key", required = false) @Nullable String xAPIKey, + @Parameter(name = "User-Agent", description = "Identifies the user agent string making the call.", in = ParameterIn.HEADER) @RequestHeader(value = "User-Agent", required = false) @Nullable String userAgent, + @Parameter(name = "Content-Type", description = "Representation Metadata. Tells the receiver what the data in the message body actually is.", in = ParameterIn.HEADER) @RequestHeader(value = "Content-Type", required = false) @Nullable String contentType, + @Parameter(name = "Accept", description = "Content Negotiation. The client tells the server what data formats it is capable of understanding.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept", required = false) @Nullable String accept, + @Parameter(name = "Accept-Language", description = "Localization. Tells the receiver the user's preferred natural languages, often with \"weights\" or priorities.", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Language", required = false) @Nullable String acceptLanguage, + @Parameter(name = "Accept-Encoding", description = "Compression. The client tells the server which content-codings it supports, usually for compression", in = ParameterIn.HEADER) @RequestHeader(value = "Accept-Encoding", required = false) @Nullable String acceptEncoding + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"ucp\" : { \"capabilities\" : [ { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" }, { \"schema\" : \"https://openapi-generator.tech\", \"extends\" : \"extends\", \"name\" : \"name\", \"version\" : \"version\", \"config\" : \"{}\", \"spec\" : \"https://openapi-generator.tech\" } ], \"version\" : \"version\" }, \"line_items\" : [ { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] }, { \"item\" : { \"price\" : 0, \"image_url\" : \"https://openapi-generator.tech\", \"id\" : \"id\", \"title\" : \"title\" }, \"quantity\" : 1, \"parent_id\" : \"parent_id\", \"id\" : \"id\", \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ] } ], \"totals\" : [ { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" }, { \"amount\" : 0, \"type\" : \"items_discount\", \"display_text\" : \"display_text\" } ], \"continue_url\" : \"https://openapi-generator.tech\", \"buyer\" : { \"full_name\" : \"full_name\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"first_name\" : \"first_name\", \"email\" : \"email\" }, \"expires_at\" : \"2000-01-23T04:56:07.000+00:00\", \"messages\" : [ { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" }, { \"severity\" : \"recoverable\", \"path\" : \"path\", \"code\" : \"code\", \"content_type\" : \"plain\", \"type\" : \"error\", \"content\" : \"content\" } ], \"currency\" : \"currency\", \"links\" : [ { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" }, { \"type\" : \"type\", \"title\" : \"title\", \"url\" : \"https://openapi-generator.tech\" } ], \"payment\" : { \"instruments\" : [ { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" }, { \"expiry_month\" : 5, \"handler_id\" : \"handler_id\", \"credential\" : { \"type\" : \"type\" }, \"billing_address\" : { \"extended_address\" : \"extended_address\", \"street_address\" : \"street_address\", \"full_name\" : \"full_name\", \"address_region\" : \"address_region\", \"address_country\" : \"address_country\", \"address_locality\" : \"address_locality\", \"last_name\" : \"last_name\", \"phone_number\" : \"phone_number\", \"postal_code\" : \"postal_code\", \"first_name\" : \"first_name\" }, \"id\" : \"id\", \"type\" : \"card\", \"brand\" : \"brand\", \"last_digits\" : \"last_digits\", \"rich_text_description\" : \"rich_text_description\", \"expiry_year\" : 5, \"rich_card_art\" : \"https://openapi-generator.tech\" } ], \"selected_instrument_id\" : \"selected_instrument_id\", \"handlers\" : [ { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" }, { \"instrument_schemas\" : [ \"https://openapi-generator.tech\", \"https://openapi-generator.tech\" ], \"name\" : \"name\", \"id\" : \"id\", \"config_schema\" : \"https://openapi-generator.tech\", \"version\" : \"version\", \"config\" : { \"key\" : \"\" }, \"spec\" : \"https://openapi-generator.tech\" } ] }, \"id\" : \"id\", \"status\" : \"incomplete\", \"order\" : { \"id\" : \"id\", \"permalink_url\" : \"https://openapi-generator.tech\" } }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + +} diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Adjustment.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Adjustment.java new file mode 100644 index 0000000..7e55b3e --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Adjustment.java @@ -0,0 +1,406 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.AdjustmentLineItemsInner; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Append-only event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items. + */ + +@Schema(name = "Adjustment", description = "Append-only event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items.") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class Adjustment { + + private String id; + + private String type; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private OffsetDateTime occurredAt; + + /** + * Adjustment status. + */ + public enum StatusEnum { + PENDING("pending"), + + COMPLETED("completed"), + + FAILED("failed"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private StatusEnum status; + + @Valid + private List<@Valid AdjustmentLineItemsInner> lineItems = new ArrayList<>(); + + private @Nullable Integer amount; + + private @Nullable String description; + + public Adjustment() { + super(); + } + + /** + * Constructor with only required parameters + */ + public Adjustment(String id, String type, OffsetDateTime occurredAt, StatusEnum status) { + this.id = id; + this.type = type; + this.occurredAt = occurredAt; + this.status = status; + } + + public Adjustment id(String id) { + this.id = id; + return this; + } + + /** + * Adjustment event identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Adjustment event identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Adjustment type(String type) { + this.type = type; + return this; + } + + /** + * Type of adjustment (open string). Typically money-related like: refund, return, credit, price_adjustment, dispute, cancellation. Can be any value that makes sense for the merchant's business. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Type of adjustment (open string). Typically money-related like: refund, return, credit, price_adjustment, dispute, cancellation. Can be any value that makes sense for the merchant's business.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Adjustment occurredAt(OffsetDateTime occurredAt) { + this.occurredAt = occurredAt; + return this; + } + + /** + * RFC 3339 timestamp when this adjustment occurred. + * @return occurredAt + */ + @NotNull @Valid + @Schema(name = "occurred_at", description = "RFC 3339 timestamp when this adjustment occurred.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("occurred_at") + public OffsetDateTime getOccurredAt() { + return occurredAt; + } + + public void setOccurredAt(OffsetDateTime occurredAt) { + this.occurredAt = occurredAt; + } + + public Adjustment status(StatusEnum status) { + this.status = status; + return this; + } + + /** + * Adjustment status. + * @return status + */ + @NotNull + @Schema(name = "status", description = "Adjustment status.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("status") + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public Adjustment lineItems(List<@Valid AdjustmentLineItemsInner> lineItems) { + this.lineItems = lineItems; + return this; + } + + public Adjustment addLineItemsItem(AdjustmentLineItemsInner lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * Which line items and quantities are affected (optional). + * @return lineItems + */ + @Valid + @Schema(name = "line_items", description = "Which line items and quantities are affected (optional).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("line_items") + public List<@Valid AdjustmentLineItemsInner> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid AdjustmentLineItemsInner> lineItems) { + this.lineItems = lineItems; + } + + public Adjustment amount(@Nullable Integer amount) { + this.amount = amount; + return this; + } + + /** + * Amount in minor units (cents) for refunds, credits, price adjustments (optional). + * @return amount + */ + + @Schema(name = "amount", description = "Amount in minor units (cents) for refunds, credits, price adjustments (optional).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("amount") + public @Nullable Integer getAmount() { + return amount; + } + + public void setAmount(@Nullable Integer amount) { + this.amount = amount; + } + + public Adjustment description(@Nullable String description) { + this.description = description; + return this; + } + + /** + * Human-readable reason or description (e.g., 'Defective item', 'Customer requested'). + * @return description + */ + + @Schema(name = "description", description = "Human-readable reason or description (e.g., 'Defective item', 'Customer requested').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("description") + public @Nullable String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Adjustment adjustment = (Adjustment) o; + return Objects.equals(this.id, adjustment.id) && + Objects.equals(this.type, adjustment.type) && + Objects.equals(this.occurredAt, adjustment.occurredAt) && + Objects.equals(this.status, adjustment.status) && + Objects.equals(this.lineItems, adjustment.lineItems) && + Objects.equals(this.amount, adjustment.amount) && + Objects.equals(this.description, adjustment.description); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, occurredAt, status, lineItems, amount, description); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Adjustment {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" occurredAt: ").append(toIndentedString(occurredAt)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private Adjustment instance; + + public Builder() { + this(new Adjustment()); + } + + protected Builder(Adjustment instance) { + this.instance = instance; + } + + protected Builder copyOf(Adjustment value) { + this.instance.setId(value.id); + this.instance.setType(value.type); + this.instance.setOccurredAt(value.occurredAt); + this.instance.setStatus(value.status); + this.instance.setLineItems(value.lineItems); + this.instance.setAmount(value.amount); + this.instance.setDescription(value.description); + return this; + } + + public Adjustment.Builder id(String id) { + this.instance.id(id); + return this; + } + + public Adjustment.Builder type(String type) { + this.instance.type(type); + return this; + } + + public Adjustment.Builder occurredAt(OffsetDateTime occurredAt) { + this.instance.occurredAt(occurredAt); + return this; + } + + public Adjustment.Builder status(StatusEnum status) { + this.instance.status(status); + return this; + } + + public Adjustment.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public Adjustment.Builder amount(Integer amount) { + this.instance.amount(amount); + return this; + } + + public Adjustment.Builder description(String description) { + this.instance.description(description); + return this; + } + + /** + * returns a built Adjustment instance. + * + * The builder is not reusable (NullPointerException) + */ + public Adjustment build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static Adjustment.Builder builder() { + return new Adjustment.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public Adjustment.Builder toBuilder() { + Adjustment.Builder builder = new Adjustment.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/AdjustmentLineItemsInner.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/AdjustmentLineItemsInner.java new file mode 100644 index 0000000..d34290c --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/AdjustmentLineItemsInner.java @@ -0,0 +1,202 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * AdjustmentLineItemsInner + */ + +@JsonTypeName("Adjustment_line_items_inner") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class AdjustmentLineItemsInner { + + private String id; + + private Integer quantity; + + public AdjustmentLineItemsInner() { + super(); + } + + /** + * Constructor with only required parameters + */ + public AdjustmentLineItemsInner(String id, Integer quantity) { + this.id = id; + this.quantity = quantity; + } + + public AdjustmentLineItemsInner id(String id) { + this.id = id; + return this; + } + + /** + * Line item ID reference. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Line item ID reference.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public AdjustmentLineItemsInner quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity affected by this adjustment. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity affected by this adjustment.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AdjustmentLineItemsInner adjustmentLineItemsInner = (AdjustmentLineItemsInner) o; + return Objects.equals(this.id, adjustmentLineItemsInner.id) && + Objects.equals(this.quantity, adjustmentLineItemsInner.quantity); + } + + @Override + public int hashCode() { + return Objects.hash(id, quantity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class AdjustmentLineItemsInner {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private AdjustmentLineItemsInner instance; + + public Builder() { + this(new AdjustmentLineItemsInner()); + } + + protected Builder(AdjustmentLineItemsInner instance) { + this.instance = instance; + } + + protected Builder copyOf(AdjustmentLineItemsInner value) { + this.instance.setId(value.id); + this.instance.setQuantity(value.quantity); + return this; + } + + public AdjustmentLineItemsInner.Builder id(String id) { + this.instance.id(id); + return this; + } + + public AdjustmentLineItemsInner.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + /** + * returns a built AdjustmentLineItemsInner instance. + * + * The builder is not reusable (NullPointerException) + */ + public AdjustmentLineItemsInner build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static AdjustmentLineItemsInner.Builder builder() { + return new AdjustmentLineItemsInner.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public AdjustmentLineItemsInner.Builder toBuilder() { + AdjustmentLineItemsInner.Builder builder = new AdjustmentLineItemsInner.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Buyer.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Buyer.java new file mode 100644 index 0000000..13fa817 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Buyer.java @@ -0,0 +1,328 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Representation of the buyer. + */ + +@Schema(name = "Buyer", description = "Representation of the buyer.") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class Buyer { + + private @Nullable String firstName; + + private @Nullable String lastName; + + private @Nullable String fullName; + + private @Nullable String email; + + private @Nullable String phoneNumber; + + public Buyer firstName(@Nullable String firstName) { + this.firstName = firstName; + return this; + } + + /** + * First name of the buyer. + * @return firstName + */ + + @Schema(name = "first_name", description = "First name of the buyer.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("first_name") + public @Nullable String getFirstName() { + return firstName; + } + + public void setFirstName(@Nullable String firstName) { + this.firstName = firstName; + } + + public Buyer lastName(@Nullable String lastName) { + this.lastName = lastName; + return this; + } + + /** + * Last name of the buyer. + * @return lastName + */ + + @Schema(name = "last_name", description = "Last name of the buyer.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("last_name") + public @Nullable String getLastName() { + return lastName; + } + + public void setLastName(@Nullable String lastName) { + this.lastName = lastName; + } + + public Buyer fullName(@Nullable String fullName) { + this.fullName = fullName; + return this; + } + + /** + * Optional, buyer's full name (if first_name or last_name fields are present they take precedence). + * @return fullName + */ + + @Schema(name = "full_name", description = "Optional, buyer's full name (if first_name or last_name fields are present they take precedence).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("full_name") + public @Nullable String getFullName() { + return fullName; + } + + public void setFullName(@Nullable String fullName) { + this.fullName = fullName; + } + + public Buyer email(@Nullable String email) { + this.email = email; + return this; + } + + /** + * Email of the buyer. + * @return email + */ + + @Schema(name = "email", description = "Email of the buyer.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("email") + public @Nullable String getEmail() { + return email; + } + + public void setEmail(@Nullable String email) { + this.email = email; + } + + public Buyer phoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + /** + * E.164 standard. + * @return phoneNumber + */ + + @Schema(name = "phone_number", description = "E.164 standard.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("phone_number") + public @Nullable String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public Buyer putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Buyer buyer = (Buyer) o; + return Objects.equals(this.firstName, buyer.firstName) && + Objects.equals(this.lastName, buyer.lastName) && + Objects.equals(this.fullName, buyer.fullName) && + Objects.equals(this.email, buyer.email) && + Objects.equals(this.phoneNumber, buyer.phoneNumber) && + Objects.equals(this.additionalProperties, buyer.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(firstName, lastName, fullName, email, phoneNumber, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Buyer {\n"); + sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); + sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); + sb.append(" fullName: ").append(toIndentedString(fullName)).append("\n"); + sb.append(" email: ").append(toIndentedString(email)).append("\n"); + sb.append(" phoneNumber: ").append(toIndentedString(phoneNumber)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private Buyer instance; + + public Builder() { + this(new Buyer()); + } + + protected Builder(Buyer instance) { + this.instance = instance; + } + + protected Builder copyOf(Buyer value) { + this.instance.setFirstName(value.firstName); + this.instance.setLastName(value.lastName); + this.instance.setFullName(value.fullName); + this.instance.setEmail(value.email); + this.instance.setPhoneNumber(value.phoneNumber); + return this; + } + + public Buyer.Builder firstName(String firstName) { + this.instance.firstName(firstName); + return this; + } + + public Buyer.Builder lastName(String lastName) { + this.instance.lastName(lastName); + return this; + } + + public Buyer.Builder fullName(String fullName) { + this.instance.fullName(fullName); + return this; + } + + public Buyer.Builder email(String email) { + this.instance.email(email); + return this; + } + + public Buyer.Builder phoneNumber(String phoneNumber) { + this.instance.phoneNumber(phoneNumber); + return this; + } + + public Buyer.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built Buyer instance. + * + * The builder is not reusable (NullPointerException) + */ + public Buyer build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static Buyer.Builder builder() { + return new Buyer.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public Buyer.Builder toBuilder() { + Buyer.Builder builder = new Buyer.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CapabilityResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CapabilityResponse.java new file mode 100644 index 0000000..81011f0 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CapabilityResponse.java @@ -0,0 +1,361 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Capability reference in responses. Only name/version required to confirm active capabilities. + */ + +@Schema(name = "Capability__Response_", description = "Capability reference in responses. Only name/version required to confirm active capabilities.") +@JsonTypeName("Capability__Response_") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CapabilityResponse { + + private @Nullable String name; + + private @Nullable String version; + + private @Nullable URI spec; + + private @Nullable URI schema; + + private @Nullable String _extends; + + private @Nullable JsonNode config; + + public CapabilityResponse name(@Nullable String name) { + this.name = name; + return this; + } + + /** + * Stable capability identifier in reverse-domain notation (e.g., dev.ucp.shopping.checkout). Used in capability negotiation. + * @return name + */ + @Pattern(regexp = "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + @Schema(name = "name", description = "Stable capability identifier in reverse-domain notation (e.g., dev.ucp.shopping.checkout). Used in capability negotiation.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("name") + public @Nullable String getName() { + return name; + } + + public void setName(@Nullable String name) { + this.name = name; + } + + public CapabilityResponse version(@Nullable String version) { + this.version = version; + return this; + } + + /** + * Capability version in YYYY-MM-DD format. + * @return version + */ + @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$") + @Schema(name = "version", description = "Capability version in YYYY-MM-DD format.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("version") + public @Nullable String getVersion() { + return version; + } + + public void setVersion(@Nullable String version) { + this.version = version; + } + + public CapabilityResponse spec(@Nullable URI spec) { + this.spec = spec; + return this; + } + + /** + * URL to human-readable specification document. + * @return spec + */ + @Valid + @Schema(name = "spec", description = "URL to human-readable specification document.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("spec") + public @Nullable URI getSpec() { + return spec; + } + + public void setSpec(@Nullable URI spec) { + this.spec = spec; + } + + public CapabilityResponse schema(@Nullable URI schema) { + this.schema = schema; + return this; + } + + /** + * URL to JSON Schema for this capability's payload. + * @return schema + */ + @Valid + @Schema(name = "schema", description = "URL to JSON Schema for this capability's payload.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("schema") + public @Nullable URI getSchema() { + return schema; + } + + public void setSchema(@Nullable URI schema) { + this.schema = schema; + } + + public CapabilityResponse _extends(@Nullable String _extends) { + this._extends = _extends; + return this; + } + + /** + * Parent capability this extends. Present for extensions, absent for root capabilities. + * @return _extends + */ + @Pattern(regexp = "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9_]*)+$") + @Schema(name = "extends", description = "Parent capability this extends. Present for extensions, absent for root capabilities.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("extends") + public @Nullable String getExtends() { + return _extends; + } + + public void setExtends(@Nullable String _extends) { + this._extends = _extends; + } + + public CapabilityResponse config(@Nullable JsonNode config) { + this.config = config; + return this; + } + + /** + * Capability-specific configuration (structure defined by each capability). + * @return config + */ + @Valid + @Schema(name = "config", description = "Capability-specific configuration (structure defined by each capability).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("config") + public @Nullable JsonNode getConfig() { + return config; + } + + public void setConfig(@Nullable JsonNode config) { + this.config = config; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CapabilityResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CapabilityResponse capabilityResponse = (CapabilityResponse) o; + return Objects.equals(this.name, capabilityResponse.name) && + Objects.equals(this.version, capabilityResponse.version) && + Objects.equals(this.spec, capabilityResponse.spec) && + Objects.equals(this.schema, capabilityResponse.schema) && + Objects.equals(this._extends, capabilityResponse._extends) && + Objects.equals(this.config, capabilityResponse.config) && + Objects.equals(this.additionalProperties, capabilityResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(name, version, spec, schema, _extends, config, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CapabilityResponse {\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" spec: ").append(toIndentedString(spec)).append("\n"); + sb.append(" schema: ").append(toIndentedString(schema)).append("\n"); + sb.append(" _extends: ").append(toIndentedString(_extends)).append("\n"); + sb.append(" config: ").append(toIndentedString(config)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CapabilityResponse instance; + + public Builder() { + this(new CapabilityResponse()); + } + + protected Builder(CapabilityResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(CapabilityResponse value) { + this.instance.setName(value.name); + this.instance.setVersion(value.version); + this.instance.setSpec(value.spec); + this.instance.setSchema(value.schema); + this.instance.setExtends(value._extends); + this.instance.setConfig(value.config); + return this; + } + + public CapabilityResponse.Builder name(String name) { + this.instance.name(name); + return this; + } + + public CapabilityResponse.Builder version(String version) { + this.instance.version(version); + return this; + } + + public CapabilityResponse.Builder spec(URI spec) { + this.instance.spec(spec); + return this; + } + + public CapabilityResponse.Builder schema(URI schema) { + this.instance.schema(schema); + return this; + } + + public CapabilityResponse.Builder _extends(String _extends) { + this.instance._extends(_extends); + return this; + } + + public CapabilityResponse.Builder config(JsonNode config) { + this.instance.config(config); + return this; + } + + public CapabilityResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CapabilityResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public CapabilityResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CapabilityResponse.Builder builder() { + return new CapabilityResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CapabilityResponse.Builder toBuilder() { + CapabilityResponse.Builder builder = new CapabilityResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardCredential.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardCredential.java new file mode 100644 index 0000000..c2d36e8 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardCredential.java @@ -0,0 +1,456 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * A card credential containing sensitive payment card details including raw Primary Account Numbers (PANs). This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites. + */ + +@Schema(name = "Card_Credential", description = "A card credential containing sensitive payment card details including raw Primary Account Numbers (PANs). This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites.") +@JsonTypeName("Card_Credential") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CardCredential implements PaymentCredential { + + private JsonNullable type = JsonNullable.undefined(); + + /** + * The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details. + */ + public enum CardNumberTypeEnum { + FPAN("fpan"), + + NETWORK_TOKEN("network_token"), + + DPAN("dpan"); + + private final String value; + + CardNumberTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static CardNumberTypeEnum fromValue(String value) { + for (CardNumberTypeEnum b : CardNumberTypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private CardNumberTypeEnum cardNumberType; + + private @Nullable String number; + + private @Nullable Integer expiryMonth; + + private @Nullable Integer expiryYear; + + private @Nullable String name; + + private @Nullable String cvc; + + private @Nullable String cryptogram; + + private @Nullable String eciValue; + + public CardCredential() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CardCredential(JsonNode type, CardNumberTypeEnum cardNumberType) { + this.type = JsonNullable.of(type); + this.cardNumberType = cardNumberType; + } + + public CardCredential type(JsonNode type) { + this.type = JsonNullable.of(type); + return this; + } + + /** + * Get type + * @return type + */ + @NotNull @Valid + @Schema(name = "type", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public JsonNullable getType() { + return type; + } + + public void setType(JsonNullable type) { + this.type = type; + } + + public CardCredential cardNumberType(CardNumberTypeEnum cardNumberType) { + this.cardNumberType = cardNumberType; + return this; + } + + /** + * The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details. + * @return cardNumberType + */ + @NotNull + @Schema(name = "card_number_type", description = "The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("card_number_type") + public CardNumberTypeEnum getCardNumberType() { + return cardNumberType; + } + + public void setCardNumberType(CardNumberTypeEnum cardNumberType) { + this.cardNumberType = cardNumberType; + } + + public CardCredential number(@Nullable String number) { + this.number = number; + return this; + } + + /** + * Card number. + * @return number + */ + + @Schema(name = "number", description = "Card number.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("number") + public @Nullable String getNumber() { + return number; + } + + public void setNumber(@Nullable String number) { + this.number = number; + } + + public CardCredential expiryMonth(@Nullable Integer expiryMonth) { + this.expiryMonth = expiryMonth; + return this; + } + + /** + * The month of the card's expiration date (1-12). + * @return expiryMonth + */ + + @Schema(name = "expiry_month", description = "The month of the card's expiration date (1-12).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expiry_month") + public @Nullable Integer getExpiryMonth() { + return expiryMonth; + } + + public void setExpiryMonth(@Nullable Integer expiryMonth) { + this.expiryMonth = expiryMonth; + } + + public CardCredential expiryYear(@Nullable Integer expiryYear) { + this.expiryYear = expiryYear; + return this; + } + + /** + * The year of the card's expiration date. + * @return expiryYear + */ + + @Schema(name = "expiry_year", description = "The year of the card's expiration date.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expiry_year") + public @Nullable Integer getExpiryYear() { + return expiryYear; + } + + public void setExpiryYear(@Nullable Integer expiryYear) { + this.expiryYear = expiryYear; + } + + public CardCredential name(@Nullable String name) { + this.name = name; + return this; + } + + /** + * Cardholder name. + * @return name + */ + + @Schema(name = "name", description = "Cardholder name.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("name") + public @Nullable String getName() { + return name; + } + + public void setName(@Nullable String name) { + this.name = name; + } + + public CardCredential cvc(@Nullable String cvc) { + this.cvc = cvc; + return this; + } + + /** + * Card CVC number. + * @return cvc + */ + @Size(max = 4) + @Schema(name = "cvc", description = "Card CVC number.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("cvc") + public @Nullable String getCvc() { + return cvc; + } + + public void setCvc(@Nullable String cvc) { + this.cvc = cvc; + } + + public CardCredential cryptogram(@Nullable String cryptogram) { + this.cryptogram = cryptogram; + return this; + } + + /** + * Cryptogram provided with network tokens. + * @return cryptogram + */ + + @Schema(name = "cryptogram", description = "Cryptogram provided with network tokens.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("cryptogram") + public @Nullable String getCryptogram() { + return cryptogram; + } + + public void setCryptogram(@Nullable String cryptogram) { + this.cryptogram = cryptogram; + } + + public CardCredential eciValue(@Nullable String eciValue) { + this.eciValue = eciValue; + return this; + } + + /** + * Electronic Commerce Indicator / Security Level Indicator provided with network tokens. + * @return eciValue + */ + + @Schema(name = "eci_value", description = "Electronic Commerce Indicator / Security Level Indicator provided with network tokens.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("eci_value") + public @Nullable String getEciValue() { + return eciValue; + } + + public void setEciValue(@Nullable String eciValue) { + this.eciValue = eciValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CardCredential cardCredential = (CardCredential) o; + return Objects.equals(this.type, cardCredential.type) && + Objects.equals(this.cardNumberType, cardCredential.cardNumberType) && + Objects.equals(this.number, cardCredential.number) && + Objects.equals(this.expiryMonth, cardCredential.expiryMonth) && + Objects.equals(this.expiryYear, cardCredential.expiryYear) && + Objects.equals(this.name, cardCredential.name) && + Objects.equals(this.cvc, cardCredential.cvc) && + Objects.equals(this.cryptogram, cardCredential.cryptogram) && + Objects.equals(this.eciValue, cardCredential.eciValue); + } + + @Override + public int hashCode() { + return Objects.hash(type, cardNumberType, number, expiryMonth, expiryYear, name, cvc, cryptogram, eciValue); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CardCredential {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" cardNumberType: ").append(toIndentedString(cardNumberType)).append("\n"); + sb.append(" number: ").append(toIndentedString(number)).append("\n"); + sb.append(" expiryMonth: ").append(toIndentedString(expiryMonth)).append("\n"); + sb.append(" expiryYear: ").append(toIndentedString(expiryYear)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" cvc: ").append(toIndentedString(cvc)).append("\n"); + sb.append(" cryptogram: ").append(toIndentedString(cryptogram)).append("\n"); + sb.append(" eciValue: ").append(toIndentedString(eciValue)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CardCredential instance; + + public Builder() { + this(new CardCredential()); + } + + protected Builder(CardCredential instance) { + this.instance = instance; + } + + protected Builder copyOf(CardCredential value) { + this.instance.setType(value.type); + this.instance.setCardNumberType(value.cardNumberType); + this.instance.setNumber(value.number); + this.instance.setExpiryMonth(value.expiryMonth); + this.instance.setExpiryYear(value.expiryYear); + this.instance.setName(value.name); + this.instance.setCvc(value.cvc); + this.instance.setCryptogram(value.cryptogram); + this.instance.setEciValue(value.eciValue); + return this; + } + + public CardCredential.Builder type(JsonNode type) { + this.instance.type(type); + return this; + } + + public CardCredential.Builder type(JsonNullable type) { + this.instance.type = type; + return this; + } + + public CardCredential.Builder cardNumberType(CardNumberTypeEnum cardNumberType) { + this.instance.cardNumberType(cardNumberType); + return this; + } + + public CardCredential.Builder number(String number) { + this.instance.number(number); + return this; + } + + public CardCredential.Builder expiryMonth(Integer expiryMonth) { + this.instance.expiryMonth(expiryMonth); + return this; + } + + public CardCredential.Builder expiryYear(Integer expiryYear) { + this.instance.expiryYear(expiryYear); + return this; + } + + public CardCredential.Builder name(String name) { + this.instance.name(name); + return this; + } + + public CardCredential.Builder cvc(String cvc) { + this.instance.cvc(cvc); + return this; + } + + public CardCredential.Builder cryptogram(String cryptogram) { + this.instance.cryptogram(cryptogram); + return this; + } + + public CardCredential.Builder eciValue(String eciValue) { + this.instance.eciValue(eciValue); + return this; + } + + /** + * returns a built CardCredential instance. + * + * The builder is not reusable (NullPointerException) + */ + public CardCredential build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CardCredential.Builder builder() { + return new CardCredential.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CardCredential.Builder toBuilder() { + CardCredential.Builder builder = new CardCredential.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardPaymentInstrument.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardPaymentInstrument.java new file mode 100644 index 0000000..ee501cf --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CardPaymentInstrument.java @@ -0,0 +1,562 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.PaymentCredential; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A basic card payment instrument with visible card details. Can be inherited by a handler's instrument schema to define handler-specific display details or more complex credential structures. + */ + +@Schema(name = "Card_Payment_Instrument", description = "A basic card payment instrument with visible card details. Can be inherited by a handler's instrument schema to define handler-specific display details or more complex credential structures.") +@JsonTypeName("Card_Payment_Instrument") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CardPaymentInstrument { + + private String id; + + private String handlerId; + + /** + * Indicates this is a card payment instrument. + */ + public enum TypeEnum { + CARD("card"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + private @Nullable PostalAddress billingAddress; + + private @Nullable PaymentCredential credential; + + private String brand; + + private String lastDigits; + + private @Nullable Integer expiryMonth; + + private @Nullable Integer expiryYear; + + private @Nullable String richTextDescription; + + private @Nullable URI richCardArt; + + public CardPaymentInstrument() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CardPaymentInstrument(String id, String handlerId, TypeEnum type, String brand, String lastDigits) { + this.id = id; + this.handlerId = handlerId; + this.type = type; + this.brand = brand; + this.lastDigits = lastDigits; + } + + public CardPaymentInstrument id(String id) { + this.id = id; + return this; + } + + /** + * A unique identifier for this instrument instance, assigned by the Agent. Used to reference this specific instrument in the 'payment.selected_instrument_id' field. + * @return id + */ + @NotNull + @Schema(name = "id", description = "A unique identifier for this instrument instance, assigned by the Agent. Used to reference this specific instrument in the 'payment.selected_instrument_id' field.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public CardPaymentInstrument handlerId(String handlerId) { + this.handlerId = handlerId; + return this; + } + + /** + * The unique identifier for the handler instance that produced this instrument. This corresponds to the 'id' field in the Payment Handler definition. + * @return handlerId + */ + @NotNull + @Schema(name = "handler_id", description = "The unique identifier for the handler instance that produced this instrument. This corresponds to the 'id' field in the Payment Handler definition.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("handler_id") + public String getHandlerId() { + return handlerId; + } + + public void setHandlerId(String handlerId) { + this.handlerId = handlerId; + } + + public CardPaymentInstrument type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Indicates this is a card payment instrument. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Indicates this is a card payment instrument.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public CardPaymentInstrument billingAddress(@Nullable PostalAddress billingAddress) { + this.billingAddress = billingAddress; + return this; + } + + /** + * Get billingAddress + * @return billingAddress + */ + @Valid + @Schema(name = "billing_address", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("billing_address") + public @Nullable PostalAddress getBillingAddress() { + return billingAddress; + } + + public void setBillingAddress(@Nullable PostalAddress billingAddress) { + this.billingAddress = billingAddress; + } + + public CardPaymentInstrument credential(@Nullable PaymentCredential credential) { + this.credential = credential; + return this; + } + + /** + * Get credential + * @return credential + */ + @Valid + @Schema(name = "credential", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("credential") + public @Nullable PaymentCredential getCredential() { + return credential; + } + + public void setCredential(@Nullable PaymentCredential credential) { + this.credential = credential; + } + + public CardPaymentInstrument brand(String brand) { + this.brand = brand; + return this; + } + + /** + * The card brand/network (e.g., visa, mastercard, amex). + * @return brand + */ + @NotNull + @Schema(name = "brand", description = "The card brand/network (e.g., visa, mastercard, amex).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("brand") + public String getBrand() { + return brand; + } + + public void setBrand(String brand) { + this.brand = brand; + } + + public CardPaymentInstrument lastDigits(String lastDigits) { + this.lastDigits = lastDigits; + return this; + } + + /** + * Last 4 digits of the card number. + * @return lastDigits + */ + @NotNull + @Schema(name = "last_digits", description = "Last 4 digits of the card number.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("last_digits") + public String getLastDigits() { + return lastDigits; + } + + public void setLastDigits(String lastDigits) { + this.lastDigits = lastDigits; + } + + public CardPaymentInstrument expiryMonth(@Nullable Integer expiryMonth) { + this.expiryMonth = expiryMonth; + return this; + } + + /** + * The month of the card's expiration date (1-12). + * @return expiryMonth + */ + + @Schema(name = "expiry_month", description = "The month of the card's expiration date (1-12).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expiry_month") + public @Nullable Integer getExpiryMonth() { + return expiryMonth; + } + + public void setExpiryMonth(@Nullable Integer expiryMonth) { + this.expiryMonth = expiryMonth; + } + + public CardPaymentInstrument expiryYear(@Nullable Integer expiryYear) { + this.expiryYear = expiryYear; + return this; + } + + /** + * The year of the card's expiration date. + * @return expiryYear + */ + + @Schema(name = "expiry_year", description = "The year of the card's expiration date.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expiry_year") + public @Nullable Integer getExpiryYear() { + return expiryYear; + } + + public void setExpiryYear(@Nullable Integer expiryYear) { + this.expiryYear = expiryYear; + } + + public CardPaymentInstrument richTextDescription(@Nullable String richTextDescription) { + this.richTextDescription = richTextDescription; + return this; + } + + /** + * An optional rich text description of the card to display to the user (e.g., 'Visa ending in 1234, expires 12/2025'). + * @return richTextDescription + */ + + @Schema(name = "rich_text_description", description = "An optional rich text description of the card to display to the user (e.g., 'Visa ending in 1234, expires 12/2025').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("rich_text_description") + public @Nullable String getRichTextDescription() { + return richTextDescription; + } + + public void setRichTextDescription(@Nullable String richTextDescription) { + this.richTextDescription = richTextDescription; + } + + public CardPaymentInstrument richCardArt(@Nullable URI richCardArt) { + this.richCardArt = richCardArt; + return this; + } + + /** + * An optional URI to a rich image representing the card (e.g., card art provided by the issuer). + * @return richCardArt + */ + @Valid + @Schema(name = "rich_card_art", description = "An optional URI to a rich image representing the card (e.g., card art provided by the issuer).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("rich_card_art") + public @Nullable URI getRichCardArt() { + return richCardArt; + } + + public void setRichCardArt(@Nullable URI richCardArt) { + this.richCardArt = richCardArt; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CardPaymentInstrument putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CardPaymentInstrument cardPaymentInstrument = (CardPaymentInstrument) o; + return Objects.equals(this.id, cardPaymentInstrument.id) && + Objects.equals(this.handlerId, cardPaymentInstrument.handlerId) && + Objects.equals(this.type, cardPaymentInstrument.type) && + Objects.equals(this.billingAddress, cardPaymentInstrument.billingAddress) && + Objects.equals(this.credential, cardPaymentInstrument.credential) && + Objects.equals(this.brand, cardPaymentInstrument.brand) && + Objects.equals(this.lastDigits, cardPaymentInstrument.lastDigits) && + Objects.equals(this.expiryMonth, cardPaymentInstrument.expiryMonth) && + Objects.equals(this.expiryYear, cardPaymentInstrument.expiryYear) && + Objects.equals(this.richTextDescription, cardPaymentInstrument.richTextDescription) && + Objects.equals(this.richCardArt, cardPaymentInstrument.richCardArt) && + Objects.equals(this.additionalProperties, cardPaymentInstrument.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(id, handlerId, type, billingAddress, credential, brand, lastDigits, expiryMonth, expiryYear, richTextDescription, richCardArt, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CardPaymentInstrument {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" handlerId: ").append(toIndentedString(handlerId)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" billingAddress: ").append(toIndentedString(billingAddress)).append("\n"); + sb.append(" credential: ").append(toIndentedString(credential)).append("\n"); + sb.append(" brand: ").append(toIndentedString(brand)).append("\n"); + sb.append(" lastDigits: ").append(toIndentedString(lastDigits)).append("\n"); + sb.append(" expiryMonth: ").append(toIndentedString(expiryMonth)).append("\n"); + sb.append(" expiryYear: ").append(toIndentedString(expiryYear)).append("\n"); + sb.append(" richTextDescription: ").append(toIndentedString(richTextDescription)).append("\n"); + sb.append(" richCardArt: ").append(toIndentedString(richCardArt)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CardPaymentInstrument instance; + + public Builder() { + this(new CardPaymentInstrument()); + } + + protected Builder(CardPaymentInstrument instance) { + this.instance = instance; + } + + protected Builder copyOf(CardPaymentInstrument value) { + this.instance.setId(value.id); + this.instance.setHandlerId(value.handlerId); + this.instance.setType(value.type); + this.instance.setBillingAddress(value.billingAddress); + this.instance.setCredential(value.credential); + this.instance.setBrand(value.brand); + this.instance.setLastDigits(value.lastDigits); + this.instance.setExpiryMonth(value.expiryMonth); + this.instance.setExpiryYear(value.expiryYear); + this.instance.setRichTextDescription(value.richTextDescription); + this.instance.setRichCardArt(value.richCardArt); + return this; + } + + public CardPaymentInstrument.Builder id(String id) { + this.instance.id(id); + return this; + } + + public CardPaymentInstrument.Builder handlerId(String handlerId) { + this.instance.handlerId(handlerId); + return this; + } + + public CardPaymentInstrument.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public CardPaymentInstrument.Builder billingAddress(PostalAddress billingAddress) { + this.instance.billingAddress(billingAddress); + return this; + } + + public CardPaymentInstrument.Builder credential(PaymentCredential credential) { + this.instance.credential(credential); + return this; + } + + public CardPaymentInstrument.Builder brand(String brand) { + this.instance.brand(brand); + return this; + } + + public CardPaymentInstrument.Builder lastDigits(String lastDigits) { + this.instance.lastDigits(lastDigits); + return this; + } + + public CardPaymentInstrument.Builder expiryMonth(Integer expiryMonth) { + this.instance.expiryMonth(expiryMonth); + return this; + } + + public CardPaymentInstrument.Builder expiryYear(Integer expiryYear) { + this.instance.expiryYear(expiryYear); + return this; + } + + public CardPaymentInstrument.Builder richTextDescription(String richTextDescription) { + this.instance.richTextDescription(richTextDescription); + return this; + } + + public CardPaymentInstrument.Builder richCardArt(URI richCardArt) { + this.instance.richCardArt(richCardArt); + return this; + } + + public CardPaymentInstrument.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CardPaymentInstrument instance. + * + * The builder is not reusable (NullPointerException) + */ + public CardPaymentInstrument build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CardPaymentInstrument.Builder builder() { + return new CardPaymentInstrument.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CardPaymentInstrument.Builder toBuilder() { + CardPaymentInstrument.Builder builder = new CardPaymentInstrument.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutCreateRequest.java new file mode 100644 index 0000000..55de6a3 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutCreateRequest.java @@ -0,0 +1,328 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Buyer; +import com.dev.ucp.service.shopping.model.LineItemCreateRequest; +import com.dev.ucp.service.shopping.model.PaymentCreateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Base checkout schema. Extensions compose onto this using allOf. + */ + +@Schema(name = "checkout_create_request", description = "Base checkout schema. Extensions compose onto this using allOf.") +@JsonTypeName("checkout_create_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CheckoutCreateRequest { + + @Valid + private List<@Valid LineItemCreateRequest> lineItems = new ArrayList<>(); + + private @Nullable Buyer buyer; + + private String currency; + + private PaymentCreateRequest payment; + + public CheckoutCreateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CheckoutCreateRequest(List<@Valid LineItemCreateRequest> lineItems, String currency, PaymentCreateRequest payment) { + this.lineItems = lineItems; + this.currency = currency; + this.payment = payment; + } + + public CheckoutCreateRequest lineItems(List<@Valid LineItemCreateRequest> lineItems) { + this.lineItems = lineItems; + return this; + } + + public CheckoutCreateRequest addLineItemsItem(LineItemCreateRequest lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * List of line items being checked out. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "List of line items being checked out.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid LineItemCreateRequest> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid LineItemCreateRequest> lineItems) { + this.lineItems = lineItems; + } + + public CheckoutCreateRequest buyer(@Nullable Buyer buyer) { + this.buyer = buyer; + return this; + } + + /** + * Get buyer + * @return buyer + */ + @Valid + @Schema(name = "buyer", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("buyer") + public @Nullable Buyer getBuyer() { + return buyer; + } + + public void setBuyer(@Nullable Buyer buyer) { + this.buyer = buyer; + } + + public CheckoutCreateRequest currency(String currency) { + this.currency = currency; + return this; + } + + /** + * ISO 4217 currency code. + * @return currency + */ + @NotNull + @Schema(name = "currency", description = "ISO 4217 currency code.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("currency") + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public CheckoutCreateRequest payment(PaymentCreateRequest payment) { + this.payment = payment; + return this; + } + + /** + * Get payment + * @return payment + */ + @NotNull @Valid + @Schema(name = "payment", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("payment") + public PaymentCreateRequest getPayment() { + return payment; + } + + public void setPayment(PaymentCreateRequest payment) { + this.payment = payment; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CheckoutCreateRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CheckoutCreateRequest checkoutCreateRequest = (CheckoutCreateRequest) o; + return Objects.equals(this.lineItems, checkoutCreateRequest.lineItems) && + Objects.equals(this.buyer, checkoutCreateRequest.buyer) && + Objects.equals(this.currency, checkoutCreateRequest.currency) && + Objects.equals(this.payment, checkoutCreateRequest.payment) && + Objects.equals(this.additionalProperties, checkoutCreateRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(lineItems, buyer, currency, payment, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CheckoutCreateRequest {\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" buyer: ").append(toIndentedString(buyer)).append("\n"); + sb.append(" currency: ").append(toIndentedString(currency)).append("\n"); + sb.append(" payment: ").append(toIndentedString(payment)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CheckoutCreateRequest instance; + + public Builder() { + this(new CheckoutCreateRequest()); + } + + protected Builder(CheckoutCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(CheckoutCreateRequest value) { + this.instance.setLineItems(value.lineItems); + this.instance.setBuyer(value.buyer); + this.instance.setCurrency(value.currency); + this.instance.setPayment(value.payment); + return this; + } + + public CheckoutCreateRequest.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public CheckoutCreateRequest.Builder buyer(Buyer buyer) { + this.instance.buyer(buyer); + return this; + } + + public CheckoutCreateRequest.Builder currency(String currency) { + this.instance.currency(currency); + return this; + } + + public CheckoutCreateRequest.Builder payment(PaymentCreateRequest payment) { + this.instance.payment(payment); + return this; + } + + public CheckoutCreateRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CheckoutCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public CheckoutCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CheckoutCreateRequest.Builder builder() { + return new CheckoutCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CheckoutCreateRequest.Builder toBuilder() { + CheckoutCreateRequest.Builder builder = new CheckoutCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutResponse.java new file mode 100644 index 0000000..20a6eb7 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutResponse.java @@ -0,0 +1,683 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Buyer; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.Link; +import com.dev.ucp.service.shopping.model.Message; +import com.dev.ucp.service.shopping.model.OrderConfirmation; +import com.dev.ucp.service.shopping.model.PaymentResponse; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.dev.ucp.service.shopping.model.UCPCheckoutResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Base checkout schema. Extensions compose onto this using allOf. + */ + +@Schema(name = "checkout_response", description = "Base checkout schema. Extensions compose onto this using allOf.") +@JsonTypeName("checkout_response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CheckoutResponse { + + private UCPCheckoutResponse ucp; + + private String id; + + @Valid + private List<@Valid LineItemResponse> lineItems = new ArrayList<>(); + + private @Nullable Buyer buyer; + + /** + * Checkout state indicating the current phase and required action. See Checkout Status lifecycle documentation for state transition details. + */ + public enum StatusEnum { + INCOMPLETE("incomplete"), + + REQUIRES_ESCALATION("requires_escalation"), + + READY_FOR_COMPLETE("ready_for_complete"), + + COMPLETE_IN_PROGRESS("complete_in_progress"), + + COMPLETED("completed"), + + CANCELED("canceled"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private StatusEnum status; + + private String currency; + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + @Valid + private List<@Valid Message> messages = new ArrayList<>(); + + @Valid + private List<@Valid Link> links = new ArrayList<>(); + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private @Nullable OffsetDateTime expiresAt; + + private @Nullable URI continueUrl; + + private PaymentResponse payment; + + private @Nullable OrderConfirmation order; + + public CheckoutResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CheckoutResponse(UCPCheckoutResponse ucp, String id, List<@Valid LineItemResponse> lineItems, StatusEnum status, String currency, List<@Valid TotalResponse> totals, List<@Valid Link> links, PaymentResponse payment) { + this.ucp = ucp; + this.id = id; + this.lineItems = lineItems; + this.status = status; + this.currency = currency; + this.totals = totals; + this.links = links; + this.payment = payment; + } + + public CheckoutResponse ucp(UCPCheckoutResponse ucp) { + this.ucp = ucp; + return this; + } + + /** + * Get ucp + * @return ucp + */ + @NotNull @Valid + @Schema(name = "ucp", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("ucp") + public UCPCheckoutResponse getUcp() { + return ucp; + } + + public void setUcp(UCPCheckoutResponse ucp) { + this.ucp = ucp; + } + + public CheckoutResponse id(String id) { + this.id = id; + return this; + } + + /** + * Unique identifier of the checkout session. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique identifier of the checkout session.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public CheckoutResponse lineItems(List<@Valid LineItemResponse> lineItems) { + this.lineItems = lineItems; + return this; + } + + public CheckoutResponse addLineItemsItem(LineItemResponse lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * List of line items being checked out. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "List of line items being checked out.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid LineItemResponse> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid LineItemResponse> lineItems) { + this.lineItems = lineItems; + } + + public CheckoutResponse buyer(@Nullable Buyer buyer) { + this.buyer = buyer; + return this; + } + + /** + * Get buyer + * @return buyer + */ + @Valid + @Schema(name = "buyer", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("buyer") + public @Nullable Buyer getBuyer() { + return buyer; + } + + public void setBuyer(@Nullable Buyer buyer) { + this.buyer = buyer; + } + + public CheckoutResponse status(StatusEnum status) { + this.status = status; + return this; + } + + /** + * Checkout state indicating the current phase and required action. See Checkout Status lifecycle documentation for state transition details. + * @return status + */ + @NotNull + @Schema(name = "status", description = "Checkout state indicating the current phase and required action. See Checkout Status lifecycle documentation for state transition details.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("status") + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public CheckoutResponse currency(String currency) { + this.currency = currency; + return this; + } + + /** + * ISO 4217 currency code. + * @return currency + */ + @NotNull + @Schema(name = "currency", description = "ISO 4217 currency code.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("currency") + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public CheckoutResponse totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public CheckoutResponse addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Different cart totals. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Different cart totals.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + + public CheckoutResponse messages(List<@Valid Message> messages) { + this.messages = messages; + return this; + } + + public CheckoutResponse addMessagesItem(Message messagesItem) { + if (this.messages == null) { + this.messages = new ArrayList<>(); + } + this.messages.add(messagesItem); + return this; + } + + /** + * List of messages with error and info about the checkout session state. + * @return messages + */ + @Valid + @Schema(name = "messages", description = "List of messages with error and info about the checkout session state.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("messages") + public List<@Valid Message> getMessages() { + return messages; + } + + public void setMessages(List<@Valid Message> messages) { + this.messages = messages; + } + + public CheckoutResponse links(List<@Valid Link> links) { + this.links = links; + return this; + } + + public CheckoutResponse addLinksItem(Link linksItem) { + if (this.links == null) { + this.links = new ArrayList<>(); + } + this.links.add(linksItem); + return this; + } + + /** + * Links to be displayed by the platform (Privacy Policy, TOS). Mandatory for legal compliance. + * @return links + */ + @NotNull @Valid + @Schema(name = "links", description = "Links to be displayed by the platform (Privacy Policy, TOS). Mandatory for legal compliance.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("links") + public List<@Valid Link> getLinks() { + return links; + } + + public void setLinks(List<@Valid Link> links) { + this.links = links; + } + + public CheckoutResponse expiresAt(@Nullable OffsetDateTime expiresAt) { + this.expiresAt = expiresAt; + return this; + } + + /** + * RFC 3339 expiry timestamp. Default TTL is 6 hours from creation if not sent. + * @return expiresAt + */ + @Valid + @Schema(name = "expires_at", description = "RFC 3339 expiry timestamp. Default TTL is 6 hours from creation if not sent.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expires_at") + public @Nullable OffsetDateTime getExpiresAt() { + return expiresAt; + } + + public void setExpiresAt(@Nullable OffsetDateTime expiresAt) { + this.expiresAt = expiresAt; + } + + public CheckoutResponse continueUrl(@Nullable URI continueUrl) { + this.continueUrl = continueUrl; + return this; + } + + /** + * URL for checkout handoff and session recovery. MUST be provided when status is requires_escalation. See specification for format and availability requirements. + * @return continueUrl + */ + @Valid + @Schema(name = "continue_url", description = "URL for checkout handoff and session recovery. MUST be provided when status is requires_escalation. See specification for format and availability requirements.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("continue_url") + public @Nullable URI getContinueUrl() { + return continueUrl; + } + + public void setContinueUrl(@Nullable URI continueUrl) { + this.continueUrl = continueUrl; + } + + public CheckoutResponse payment(PaymentResponse payment) { + this.payment = payment; + return this; + } + + /** + * Get payment + * @return payment + */ + @NotNull @Valid + @Schema(name = "payment", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("payment") + public PaymentResponse getPayment() { + return payment; + } + + public void setPayment(PaymentResponse payment) { + this.payment = payment; + } + + public CheckoutResponse order(@Nullable OrderConfirmation order) { + this.order = order; + return this; + } + + /** + * Get order + * @return order + */ + @Valid + @Schema(name = "order", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("order") + public @Nullable OrderConfirmation getOrder() { + return order; + } + + public void setOrder(@Nullable OrderConfirmation order) { + this.order = order; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CheckoutResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CheckoutResponse checkoutResponse = (CheckoutResponse) o; + return Objects.equals(this.ucp, checkoutResponse.ucp) && + Objects.equals(this.id, checkoutResponse.id) && + Objects.equals(this.lineItems, checkoutResponse.lineItems) && + Objects.equals(this.buyer, checkoutResponse.buyer) && + Objects.equals(this.status, checkoutResponse.status) && + Objects.equals(this.currency, checkoutResponse.currency) && + Objects.equals(this.totals, checkoutResponse.totals) && + Objects.equals(this.messages, checkoutResponse.messages) && + Objects.equals(this.links, checkoutResponse.links) && + Objects.equals(this.expiresAt, checkoutResponse.expiresAt) && + Objects.equals(this.continueUrl, checkoutResponse.continueUrl) && + Objects.equals(this.payment, checkoutResponse.payment) && + Objects.equals(this.order, checkoutResponse.order) && + Objects.equals(this.additionalProperties, checkoutResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(ucp, id, lineItems, buyer, status, currency, totals, messages, links, expiresAt, continueUrl, payment, order, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CheckoutResponse {\n"); + sb.append(" ucp: ").append(toIndentedString(ucp)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" buyer: ").append(toIndentedString(buyer)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" currency: ").append(toIndentedString(currency)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + sb.append(" messages: ").append(toIndentedString(messages)).append("\n"); + sb.append(" links: ").append(toIndentedString(links)).append("\n"); + sb.append(" expiresAt: ").append(toIndentedString(expiresAt)).append("\n"); + sb.append(" continueUrl: ").append(toIndentedString(continueUrl)).append("\n"); + sb.append(" payment: ").append(toIndentedString(payment)).append("\n"); + sb.append(" order: ").append(toIndentedString(order)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CheckoutResponse instance; + + public Builder() { + this(new CheckoutResponse()); + } + + protected Builder(CheckoutResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(CheckoutResponse value) { + this.instance.setUcp(value.ucp); + this.instance.setId(value.id); + this.instance.setLineItems(value.lineItems); + this.instance.setBuyer(value.buyer); + this.instance.setStatus(value.status); + this.instance.setCurrency(value.currency); + this.instance.setTotals(value.totals); + this.instance.setMessages(value.messages); + this.instance.setLinks(value.links); + this.instance.setExpiresAt(value.expiresAt); + this.instance.setContinueUrl(value.continueUrl); + this.instance.setPayment(value.payment); + this.instance.setOrder(value.order); + return this; + } + + public CheckoutResponse.Builder ucp(UCPCheckoutResponse ucp) { + this.instance.ucp(ucp); + return this; + } + + public CheckoutResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public CheckoutResponse.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public CheckoutResponse.Builder buyer(Buyer buyer) { + this.instance.buyer(buyer); + return this; + } + + public CheckoutResponse.Builder status(StatusEnum status) { + this.instance.status(status); + return this; + } + + public CheckoutResponse.Builder currency(String currency) { + this.instance.currency(currency); + return this; + } + + public CheckoutResponse.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + public CheckoutResponse.Builder messages(List messages) { + this.instance.messages(messages); + return this; + } + + public CheckoutResponse.Builder links(List links) { + this.instance.links(links); + return this; + } + + public CheckoutResponse.Builder expiresAt(OffsetDateTime expiresAt) { + this.instance.expiresAt(expiresAt); + return this; + } + + public CheckoutResponse.Builder continueUrl(URI continueUrl) { + this.instance.continueUrl(continueUrl); + return this; + } + + public CheckoutResponse.Builder payment(PaymentResponse payment) { + this.instance.payment(payment); + return this; + } + + public CheckoutResponse.Builder order(OrderConfirmation order) { + this.instance.order(order); + return this; + } + + public CheckoutResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CheckoutResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public CheckoutResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CheckoutResponse.Builder builder() { + return new CheckoutResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CheckoutResponse.Builder toBuilder() { + CheckoutResponse.Builder builder = new CheckoutResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutUpdateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutUpdateRequest.java new file mode 100644 index 0000000..5e71db5 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CheckoutUpdateRequest.java @@ -0,0 +1,359 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Buyer; +import com.dev.ucp.service.shopping.model.LineItemUpdateRequest; +import com.dev.ucp.service.shopping.model.PaymentUpdateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Base checkout schema. Extensions compose onto this using allOf. + */ + +@Schema(name = "checkout_update_request", description = "Base checkout schema. Extensions compose onto this using allOf.") +@JsonTypeName("checkout_update_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CheckoutUpdateRequest { + + private String id; + + @Valid + private List<@Valid LineItemUpdateRequest> lineItems = new ArrayList<>(); + + private @Nullable Buyer buyer; + + private String currency; + + private PaymentUpdateRequest payment; + + public CheckoutUpdateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CheckoutUpdateRequest(String id, List<@Valid LineItemUpdateRequest> lineItems, String currency, PaymentUpdateRequest payment) { + this.id = id; + this.lineItems = lineItems; + this.currency = currency; + this.payment = payment; + } + + public CheckoutUpdateRequest id(String id) { + this.id = id; + return this; + } + + /** + * Unique identifier of the checkout session. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique identifier of the checkout session.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public CheckoutUpdateRequest lineItems(List<@Valid LineItemUpdateRequest> lineItems) { + this.lineItems = lineItems; + return this; + } + + public CheckoutUpdateRequest addLineItemsItem(LineItemUpdateRequest lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * List of line items being checked out. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "List of line items being checked out.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid LineItemUpdateRequest> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid LineItemUpdateRequest> lineItems) { + this.lineItems = lineItems; + } + + public CheckoutUpdateRequest buyer(@Nullable Buyer buyer) { + this.buyer = buyer; + return this; + } + + /** + * Get buyer + * @return buyer + */ + @Valid + @Schema(name = "buyer", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("buyer") + public @Nullable Buyer getBuyer() { + return buyer; + } + + public void setBuyer(@Nullable Buyer buyer) { + this.buyer = buyer; + } + + public CheckoutUpdateRequest currency(String currency) { + this.currency = currency; + return this; + } + + /** + * ISO 4217 currency code. + * @return currency + */ + @NotNull + @Schema(name = "currency", description = "ISO 4217 currency code.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("currency") + public String getCurrency() { + return currency; + } + + public void setCurrency(String currency) { + this.currency = currency; + } + + public CheckoutUpdateRequest payment(PaymentUpdateRequest payment) { + this.payment = payment; + return this; + } + + /** + * Get payment + * @return payment + */ + @NotNull @Valid + @Schema(name = "payment", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("payment") + public PaymentUpdateRequest getPayment() { + return payment; + } + + public void setPayment(PaymentUpdateRequest payment) { + this.payment = payment; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CheckoutUpdateRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CheckoutUpdateRequest checkoutUpdateRequest = (CheckoutUpdateRequest) o; + return Objects.equals(this.id, checkoutUpdateRequest.id) && + Objects.equals(this.lineItems, checkoutUpdateRequest.lineItems) && + Objects.equals(this.buyer, checkoutUpdateRequest.buyer) && + Objects.equals(this.currency, checkoutUpdateRequest.currency) && + Objects.equals(this.payment, checkoutUpdateRequest.payment) && + Objects.equals(this.additionalProperties, checkoutUpdateRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(id, lineItems, buyer, currency, payment, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CheckoutUpdateRequest {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" buyer: ").append(toIndentedString(buyer)).append("\n"); + sb.append(" currency: ").append(toIndentedString(currency)).append("\n"); + sb.append(" payment: ").append(toIndentedString(payment)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CheckoutUpdateRequest instance; + + public Builder() { + this(new CheckoutUpdateRequest()); + } + + protected Builder(CheckoutUpdateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(CheckoutUpdateRequest value) { + this.instance.setId(value.id); + this.instance.setLineItems(value.lineItems); + this.instance.setBuyer(value.buyer); + this.instance.setCurrency(value.currency); + this.instance.setPayment(value.payment); + return this; + } + + public CheckoutUpdateRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + public CheckoutUpdateRequest.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public CheckoutUpdateRequest.Builder buyer(Buyer buyer) { + this.instance.buyer(buyer); + return this; + } + + public CheckoutUpdateRequest.Builder currency(String currency) { + this.instance.currency(currency); + return this; + } + + public CheckoutUpdateRequest.Builder payment(PaymentUpdateRequest payment) { + this.instance.payment(payment); + return this; + } + + public CheckoutUpdateRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CheckoutUpdateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public CheckoutUpdateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CheckoutUpdateRequest.Builder builder() { + return new CheckoutUpdateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CheckoutUpdateRequest.Builder toBuilder() { + CheckoutUpdateRequest.Builder builder = new CheckoutUpdateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CompleteCheckoutRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CompleteCheckoutRequest.java new file mode 100644 index 0000000..ec2cad3 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/CompleteCheckoutRequest.java @@ -0,0 +1,251 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * CompleteCheckoutRequest + */ + +@JsonTypeName("complete_checkout_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class CompleteCheckoutRequest { + + private CardPaymentInstrument paymentData; + + private @Nullable JsonNode riskSignals; + + public CompleteCheckoutRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public CompleteCheckoutRequest(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + } + + public CompleteCheckoutRequest paymentData(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + return this; + } + + /** + * Get paymentData + * @return paymentData + */ + @NotNull @Valid + @Schema(name = "payment_data", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("payment_data") + public CardPaymentInstrument getPaymentData() { + return paymentData; + } + + public void setPaymentData(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + } + + public CompleteCheckoutRequest riskSignals(@Nullable JsonNode riskSignals) { + this.riskSignals = riskSignals; + return this; + } + + /** + * Key-value pairs of risk signals. + * @return riskSignals + */ + @Valid + @Schema(name = "risk_signals", description = "Key-value pairs of risk signals.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("risk_signals") + public @Nullable JsonNode getRiskSignals() { + return riskSignals; + } + + public void setRiskSignals(@Nullable JsonNode riskSignals) { + this.riskSignals = riskSignals; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public CompleteCheckoutRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CompleteCheckoutRequest completeCheckoutRequest = (CompleteCheckoutRequest) o; + return Objects.equals(this.paymentData, completeCheckoutRequest.paymentData) && + Objects.equals(this.riskSignals, completeCheckoutRequest.riskSignals) && + Objects.equals(this.additionalProperties, completeCheckoutRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(paymentData, riskSignals, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CompleteCheckoutRequest {\n"); + sb.append(" paymentData: ").append(toIndentedString(paymentData)).append("\n"); + sb.append(" riskSignals: ").append(toIndentedString(riskSignals)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private CompleteCheckoutRequest instance; + + public Builder() { + this(new CompleteCheckoutRequest()); + } + + protected Builder(CompleteCheckoutRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(CompleteCheckoutRequest value) { + this.instance.setPaymentData(value.paymentData); + this.instance.setRiskSignals(value.riskSignals); + return this; + } + + public CompleteCheckoutRequest.Builder paymentData(CardPaymentInstrument paymentData) { + this.instance.paymentData(paymentData); + return this; + } + + public CompleteCheckoutRequest.Builder riskSignals(JsonNode riskSignals) { + this.instance.riskSignals(riskSignals); + return this; + } + + public CompleteCheckoutRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built CompleteCheckoutRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public CompleteCheckoutRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static CompleteCheckoutRequest.Builder builder() { + return new CompleteCheckoutRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public CompleteCheckoutRequest.Builder toBuilder() { + CompleteCheckoutRequest.Builder builder = new CompleteCheckoutRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequest.java new file mode 100644 index 0000000..09bed61 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.DiscountRequestAppliedInner; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Discount codes input and applied discounts output. + */ + +@Schema(name = "discount_request", description = "Discount codes input and applied discounts output.") +@JsonTypeName("discount_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class DiscountRequest { + + @Valid + private List codes = new ArrayList<>(); + + @Valid + private List<@Valid DiscountRequestAppliedInner> applied = new ArrayList<>(); + + public DiscountRequest codes(List codes) { + this.codes = codes; + return this; + } + + public DiscountRequest addCodesItem(String codesItem) { + if (this.codes == null) { + this.codes = new ArrayList<>(); + } + this.codes.add(codesItem); + return this; + } + + /** + * Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear. + * @return codes + */ + + @Schema(name = "codes", description = "Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("codes") + public List getCodes() { + return codes; + } + + public void setCodes(List codes) { + this.codes = codes; + } + + public DiscountRequest applied(List<@Valid DiscountRequestAppliedInner> applied) { + this.applied = applied; + return this; + } + + public DiscountRequest addAppliedItem(DiscountRequestAppliedInner appliedItem) { + if (this.applied == null) { + this.applied = new ArrayList<>(); + } + this.applied.add(appliedItem); + return this; + } + + /** + * Discounts successfully applied (code-based and automatic). + * @return applied + */ + @Valid + @Schema(name = "applied", description = "Discounts successfully applied (code-based and automatic).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("applied") + public List<@Valid DiscountRequestAppliedInner> getApplied() { + return applied; + } + + public void setApplied(List<@Valid DiscountRequestAppliedInner> applied) { + this.applied = applied; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscountRequest discountRequest = (DiscountRequest) o; + return Objects.equals(this.codes, discountRequest.codes) && + Objects.equals(this.applied, discountRequest.applied); + } + + @Override + public int hashCode() { + return Objects.hash(codes, applied); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DiscountRequest {\n"); + sb.append(" codes: ").append(toIndentedString(codes)).append("\n"); + sb.append(" applied: ").append(toIndentedString(applied)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private DiscountRequest instance; + + public Builder() { + this(new DiscountRequest()); + } + + protected Builder(DiscountRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(DiscountRequest value) { + this.instance.setCodes(value.codes); + this.instance.setApplied(value.applied); + return this; + } + + public DiscountRequest.Builder codes(List codes) { + this.instance.codes(codes); + return this; + } + + public DiscountRequest.Builder applied(List applied) { + this.instance.applied(applied); + return this; + } + + /** + * returns a built DiscountRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public DiscountRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static DiscountRequest.Builder builder() { + return new DiscountRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public DiscountRequest.Builder toBuilder() { + DiscountRequest.Builder builder = new DiscountRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInner.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInner.java new file mode 100644 index 0000000..41f2ee1 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInner.java @@ -0,0 +1,403 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.DiscountRequestAppliedInnerAllocationsInner; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * A discount that was successfully applied. + */ + +@Schema(name = "discount_request_applied_inner", description = "A discount that was successfully applied.") +@JsonTypeName("discount_request_applied_inner") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class DiscountRequestAppliedInner { + + private @Nullable String code; + + private String title; + + private Integer amount; + + private Boolean automatic = false; + + /** + * Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value. + */ + public enum MethodEnum { + EACH("each"), + + ACROSS("across"); + + private final String value; + + MethodEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static MethodEnum fromValue(String value) { + for (MethodEnum b : MethodEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private @Nullable MethodEnum method; + + private @Nullable Integer priority; + + @Valid + private List<@Valid DiscountRequestAppliedInnerAllocationsInner> allocations = new ArrayList<>(); + + public DiscountRequestAppliedInner() { + super(); + } + + /** + * Constructor with only required parameters + */ + public DiscountRequestAppliedInner(String title, Integer amount) { + this.title = title; + this.amount = amount; + } + + public DiscountRequestAppliedInner code(@Nullable String code) { + this.code = code; + return this; + } + + /** + * The discount code. Omitted for automatic discounts. + * @return code + */ + + @Schema(name = "code", description = "The discount code. Omitted for automatic discounts.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("code") + public @Nullable String getCode() { + return code; + } + + public void setCode(@Nullable String code) { + this.code = code; + } + + public DiscountRequestAppliedInner title(String title) { + this.title = title; + return this; + } + + /** + * Human-readable discount name (e.g., 'Summer Sale 20% Off'). + * @return title + */ + @NotNull + @Schema(name = "title", description = "Human-readable discount name (e.g., 'Summer Sale 20% Off').", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public DiscountRequestAppliedInner amount(Integer amount) { + this.amount = amount; + return this; + } + + /** + * Total discount amount in minor (cents) currency units. + * minimum: 0 + * @return amount + */ + @NotNull @Min(value = 0) + @Schema(name = "amount", description = "Total discount amount in minor (cents) currency units.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("amount") + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public DiscountRequestAppliedInner automatic(Boolean automatic) { + this.automatic = automatic; + return this; + } + + /** + * True if applied automatically by merchant rules (no code required). + * @return automatic + */ + + @Schema(name = "automatic", description = "True if applied automatically by merchant rules (no code required).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("automatic") + public Boolean getAutomatic() { + return automatic; + } + + public void setAutomatic(Boolean automatic) { + this.automatic = automatic; + } + + public DiscountRequestAppliedInner method(@Nullable MethodEnum method) { + this.method = method; + return this; + } + + /** + * Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value. + * @return method + */ + + @Schema(name = "method", description = "Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("method") + public @Nullable MethodEnum getMethod() { + return method; + } + + public void setMethod(@Nullable MethodEnum method) { + this.method = method; + } + + public DiscountRequestAppliedInner priority(@Nullable Integer priority) { + this.priority = priority; + return this; + } + + /** + * Stacking order for discount calculation. Lower numbers applied first (1 = first). + * minimum: 1 + * @return priority + */ + @Min(value = 1) + @Schema(name = "priority", description = "Stacking order for discount calculation. Lower numbers applied first (1 = first).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("priority") + public @Nullable Integer getPriority() { + return priority; + } + + public void setPriority(@Nullable Integer priority) { + this.priority = priority; + } + + public DiscountRequestAppliedInner allocations(List<@Valid DiscountRequestAppliedInnerAllocationsInner> allocations) { + this.allocations = allocations; + return this; + } + + public DiscountRequestAppliedInner addAllocationsItem(DiscountRequestAppliedInnerAllocationsInner allocationsItem) { + if (this.allocations == null) { + this.allocations = new ArrayList<>(); + } + this.allocations.add(allocationsItem); + return this; + } + + /** + * Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount. + * @return allocations + */ + @Valid + @Schema(name = "allocations", description = "Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("allocations") + public List<@Valid DiscountRequestAppliedInnerAllocationsInner> getAllocations() { + return allocations; + } + + public void setAllocations(List<@Valid DiscountRequestAppliedInnerAllocationsInner> allocations) { + this.allocations = allocations; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscountRequestAppliedInner discountRequestAppliedInner = (DiscountRequestAppliedInner) o; + return Objects.equals(this.code, discountRequestAppliedInner.code) && + Objects.equals(this.title, discountRequestAppliedInner.title) && + Objects.equals(this.amount, discountRequestAppliedInner.amount) && + Objects.equals(this.automatic, discountRequestAppliedInner.automatic) && + Objects.equals(this.method, discountRequestAppliedInner.method) && + Objects.equals(this.priority, discountRequestAppliedInner.priority) && + Objects.equals(this.allocations, discountRequestAppliedInner.allocations); + } + + @Override + public int hashCode() { + return Objects.hash(code, title, amount, automatic, method, priority, allocations); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DiscountRequestAppliedInner {\n"); + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); + sb.append(" automatic: ").append(toIndentedString(automatic)).append("\n"); + sb.append(" method: ").append(toIndentedString(method)).append("\n"); + sb.append(" priority: ").append(toIndentedString(priority)).append("\n"); + sb.append(" allocations: ").append(toIndentedString(allocations)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private DiscountRequestAppliedInner instance; + + public Builder() { + this(new DiscountRequestAppliedInner()); + } + + protected Builder(DiscountRequestAppliedInner instance) { + this.instance = instance; + } + + protected Builder copyOf(DiscountRequestAppliedInner value) { + this.instance.setCode(value.code); + this.instance.setTitle(value.title); + this.instance.setAmount(value.amount); + this.instance.setAutomatic(value.automatic); + this.instance.setMethod(value.method); + this.instance.setPriority(value.priority); + this.instance.setAllocations(value.allocations); + return this; + } + + public DiscountRequestAppliedInner.Builder code(String code) { + this.instance.code(code); + return this; + } + + public DiscountRequestAppliedInner.Builder title(String title) { + this.instance.title(title); + return this; + } + + public DiscountRequestAppliedInner.Builder amount(Integer amount) { + this.instance.amount(amount); + return this; + } + + public DiscountRequestAppliedInner.Builder automatic(Boolean automatic) { + this.instance.automatic(automatic); + return this; + } + + public DiscountRequestAppliedInner.Builder method(MethodEnum method) { + this.instance.method(method); + return this; + } + + public DiscountRequestAppliedInner.Builder priority(Integer priority) { + this.instance.priority(priority); + return this; + } + + public DiscountRequestAppliedInner.Builder allocations(List allocations) { + this.instance.allocations(allocations); + return this; + } + + /** + * returns a built DiscountRequestAppliedInner instance. + * + * The builder is not reusable (NullPointerException) + */ + public DiscountRequestAppliedInner build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static DiscountRequestAppliedInner.Builder builder() { + return new DiscountRequestAppliedInner.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public DiscountRequestAppliedInner.Builder toBuilder() { + DiscountRequestAppliedInner.Builder builder = new DiscountRequestAppliedInner.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInnerAllocationsInner.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInnerAllocationsInner.java new file mode 100644 index 0000000..86a6922 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountRequestAppliedInnerAllocationsInner.java @@ -0,0 +1,203 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Breakdown of how a discount amount was allocated to a specific target. + */ + +@Schema(name = "discount_request_applied_inner_allocations_inner", description = "Breakdown of how a discount amount was allocated to a specific target.") +@JsonTypeName("discount_request_applied_inner_allocations_inner") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class DiscountRequestAppliedInnerAllocationsInner { + + private String path; + + private Integer amount; + + public DiscountRequestAppliedInnerAllocationsInner() { + super(); + } + + /** + * Constructor with only required parameters + */ + public DiscountRequestAppliedInnerAllocationsInner(String path, Integer amount) { + this.path = path; + this.amount = amount; + } + + public DiscountRequestAppliedInnerAllocationsInner path(String path) { + this.path = path; + return this; + } + + /** + * JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping'). + * @return path + */ + @NotNull + @Schema(name = "path", description = "JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping').", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("path") + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public DiscountRequestAppliedInnerAllocationsInner amount(Integer amount) { + this.amount = amount; + return this; + } + + /** + * Amount allocated to this target in minor (cents) currency units. + * minimum: 0 + * @return amount + */ + @NotNull @Min(value = 0) + @Schema(name = "amount", description = "Amount allocated to this target in minor (cents) currency units.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("amount") + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscountRequestAppliedInnerAllocationsInner discountRequestAppliedInnerAllocationsInner = (DiscountRequestAppliedInnerAllocationsInner) o; + return Objects.equals(this.path, discountRequestAppliedInnerAllocationsInner.path) && + Objects.equals(this.amount, discountRequestAppliedInnerAllocationsInner.amount); + } + + @Override + public int hashCode() { + return Objects.hash(path, amount); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DiscountRequestAppliedInnerAllocationsInner {\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private DiscountRequestAppliedInnerAllocationsInner instance; + + public Builder() { + this(new DiscountRequestAppliedInnerAllocationsInner()); + } + + protected Builder(DiscountRequestAppliedInnerAllocationsInner instance) { + this.instance = instance; + } + + protected Builder copyOf(DiscountRequestAppliedInnerAllocationsInner value) { + this.instance.setPath(value.path); + this.instance.setAmount(value.amount); + return this; + } + + public DiscountRequestAppliedInnerAllocationsInner.Builder path(String path) { + this.instance.path(path); + return this; + } + + public DiscountRequestAppliedInnerAllocationsInner.Builder amount(Integer amount) { + this.instance.amount(amount); + return this; + } + + /** + * returns a built DiscountRequestAppliedInnerAllocationsInner instance. + * + * The builder is not reusable (NullPointerException) + */ + public DiscountRequestAppliedInnerAllocationsInner build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static DiscountRequestAppliedInnerAllocationsInner.Builder builder() { + return new DiscountRequestAppliedInnerAllocationsInner.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public DiscountRequestAppliedInnerAllocationsInner.Builder toBuilder() { + DiscountRequestAppliedInnerAllocationsInner.Builder builder = new DiscountRequestAppliedInnerAllocationsInner.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountResponse.java new file mode 100644 index 0000000..07a2868 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/DiscountResponse.java @@ -0,0 +1,212 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.DiscountRequestAppliedInner; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Discount codes input and applied discounts output. + */ + +@Schema(name = "discount_response", description = "Discount codes input and applied discounts output.") +@JsonTypeName("discount_response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class DiscountResponse { + + @Valid + private List codes = new ArrayList<>(); + + @Valid + private List<@Valid DiscountRequestAppliedInner> applied = new ArrayList<>(); + + public DiscountResponse codes(List codes) { + this.codes = codes; + return this; + } + + public DiscountResponse addCodesItem(String codesItem) { + if (this.codes == null) { + this.codes = new ArrayList<>(); + } + this.codes.add(codesItem); + return this; + } + + /** + * Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear. + * @return codes + */ + + @Schema(name = "codes", description = "Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("codes") + public List getCodes() { + return codes; + } + + public void setCodes(List codes) { + this.codes = codes; + } + + public DiscountResponse applied(List<@Valid DiscountRequestAppliedInner> applied) { + this.applied = applied; + return this; + } + + public DiscountResponse addAppliedItem(DiscountRequestAppliedInner appliedItem) { + if (this.applied == null) { + this.applied = new ArrayList<>(); + } + this.applied.add(appliedItem); + return this; + } + + /** + * Discounts successfully applied (code-based and automatic). + * @return applied + */ + @Valid + @Schema(name = "applied", description = "Discounts successfully applied (code-based and automatic).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("applied") + public List<@Valid DiscountRequestAppliedInner> getApplied() { + return applied; + } + + public void setApplied(List<@Valid DiscountRequestAppliedInner> applied) { + this.applied = applied; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + DiscountResponse discountResponse = (DiscountResponse) o; + return Objects.equals(this.codes, discountResponse.codes) && + Objects.equals(this.applied, discountResponse.applied); + } + + @Override + public int hashCode() { + return Objects.hash(codes, applied); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class DiscountResponse {\n"); + sb.append(" codes: ").append(toIndentedString(codes)).append("\n"); + sb.append(" applied: ").append(toIndentedString(applied)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private DiscountResponse instance; + + public Builder() { + this(new DiscountResponse()); + } + + protected Builder(DiscountResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(DiscountResponse value) { + this.instance.setCodes(value.codes); + this.instance.setApplied(value.applied); + return this; + } + + public DiscountResponse.Builder codes(List codes) { + this.instance.codes(codes); + return this; + } + + public DiscountResponse.Builder applied(List applied) { + this.instance.applied(applied); + return this; + } + + /** + * returns a built DiscountResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public DiscountResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static DiscountResponse.Builder builder() { + return new DiscountResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public DiscountResponse.Builder toBuilder() { + DiscountResponse.Builder builder = new DiscountResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Expectation.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Expectation.java new file mode 100644 index 0000000..97e99fd --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Expectation.java @@ -0,0 +1,374 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.ExpectationLineItemsInner; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Buyer-facing fulfillment expectation representing logical groupings of items (e.g., 'package'). Can be split, merged, or adjusted post-order to set buyer expectations for when/how items arrive. + */ + +@Schema(name = "Expectation", description = "Buyer-facing fulfillment expectation representing logical groupings of items (e.g., 'package'). Can be split, merged, or adjusted post-order to set buyer expectations for when/how items arrive.") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class Expectation { + + private String id; + + @Valid + private List<@Valid ExpectationLineItemsInner> lineItems = new ArrayList<>(); + + /** + * Delivery method type (shipping, pickup, digital). + */ + public enum MethodTypeEnum { + SHIPPING("shipping"), + + PICKUP("pickup"), + + DIGITAL("digital"); + + private final String value; + + MethodTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static MethodTypeEnum fromValue(String value) { + for (MethodTypeEnum b : MethodTypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private MethodTypeEnum methodType; + + private PostalAddress destination; + + private @Nullable String description; + + private @Nullable String fulfillableOn; + + public Expectation() { + super(); + } + + /** + * Constructor with only required parameters + */ + public Expectation(String id, List<@Valid ExpectationLineItemsInner> lineItems, MethodTypeEnum methodType, PostalAddress destination) { + this.id = id; + this.lineItems = lineItems; + this.methodType = methodType; + this.destination = destination; + } + + public Expectation id(String id) { + this.id = id; + return this; + } + + /** + * Expectation identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Expectation identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Expectation lineItems(List<@Valid ExpectationLineItemsInner> lineItems) { + this.lineItems = lineItems; + return this; + } + + public Expectation addLineItemsItem(ExpectationLineItemsInner lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * Which line items and quantities are in this expectation. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "Which line items and quantities are in this expectation.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid ExpectationLineItemsInner> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid ExpectationLineItemsInner> lineItems) { + this.lineItems = lineItems; + } + + public Expectation methodType(MethodTypeEnum methodType) { + this.methodType = methodType; + return this; + } + + /** + * Delivery method type (shipping, pickup, digital). + * @return methodType + */ + @NotNull + @Schema(name = "method_type", description = "Delivery method type (shipping, pickup, digital).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("method_type") + public MethodTypeEnum getMethodType() { + return methodType; + } + + public void setMethodType(MethodTypeEnum methodType) { + this.methodType = methodType; + } + + public Expectation destination(PostalAddress destination) { + this.destination = destination; + return this; + } + + /** + * Get destination + * @return destination + */ + @NotNull @Valid + @Schema(name = "destination", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("destination") + public PostalAddress getDestination() { + return destination; + } + + public void setDestination(PostalAddress destination) { + this.destination = destination; + } + + public Expectation description(@Nullable String description) { + this.description = description; + return this; + } + + /** + * Human-readable delivery description (e.g., 'Arrives in 5-8 business days'). + * @return description + */ + + @Schema(name = "description", description = "Human-readable delivery description (e.g., 'Arrives in 5-8 business days').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("description") + public @Nullable String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + public Expectation fulfillableOn(@Nullable String fulfillableOn) { + this.fulfillableOn = fulfillableOn; + return this; + } + + /** + * When this expectation can be fulfilled: 'now' or ISO 8601 timestamp for future date (backorder, pre-order). + * @return fulfillableOn + */ + + @Schema(name = "fulfillable_on", description = "When this expectation can be fulfilled: 'now' or ISO 8601 timestamp for future date (backorder, pre-order).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("fulfillable_on") + public @Nullable String getFulfillableOn() { + return fulfillableOn; + } + + public void setFulfillableOn(@Nullable String fulfillableOn) { + this.fulfillableOn = fulfillableOn; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Expectation expectation = (Expectation) o; + return Objects.equals(this.id, expectation.id) && + Objects.equals(this.lineItems, expectation.lineItems) && + Objects.equals(this.methodType, expectation.methodType) && + Objects.equals(this.destination, expectation.destination) && + Objects.equals(this.description, expectation.description) && + Objects.equals(this.fulfillableOn, expectation.fulfillableOn); + } + + @Override + public int hashCode() { + return Objects.hash(id, lineItems, methodType, destination, description, fulfillableOn); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Expectation {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" methodType: ").append(toIndentedString(methodType)).append("\n"); + sb.append(" destination: ").append(toIndentedString(destination)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" fulfillableOn: ").append(toIndentedString(fulfillableOn)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private Expectation instance; + + public Builder() { + this(new Expectation()); + } + + protected Builder(Expectation instance) { + this.instance = instance; + } + + protected Builder copyOf(Expectation value) { + this.instance.setId(value.id); + this.instance.setLineItems(value.lineItems); + this.instance.setMethodType(value.methodType); + this.instance.setDestination(value.destination); + this.instance.setDescription(value.description); + this.instance.setFulfillableOn(value.fulfillableOn); + return this; + } + + public Expectation.Builder id(String id) { + this.instance.id(id); + return this; + } + + public Expectation.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public Expectation.Builder methodType(MethodTypeEnum methodType) { + this.instance.methodType(methodType); + return this; + } + + public Expectation.Builder destination(PostalAddress destination) { + this.instance.destination(destination); + return this; + } + + public Expectation.Builder description(String description) { + this.instance.description(description); + return this; + } + + public Expectation.Builder fulfillableOn(String fulfillableOn) { + this.instance.fulfillableOn(fulfillableOn); + return this; + } + + /** + * returns a built Expectation instance. + * + * The builder is not reusable (NullPointerException) + */ + public Expectation build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static Expectation.Builder builder() { + return new Expectation.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public Expectation.Builder toBuilder() { + Expectation.Builder builder = new Expectation.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ExpectationLineItemsInner.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ExpectationLineItemsInner.java new file mode 100644 index 0000000..3a4b795 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ExpectationLineItemsInner.java @@ -0,0 +1,202 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * ExpectationLineItemsInner + */ + +@JsonTypeName("Expectation_line_items_inner") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ExpectationLineItemsInner { + + private String id; + + private Integer quantity; + + public ExpectationLineItemsInner() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ExpectationLineItemsInner(String id, Integer quantity) { + this.id = id; + this.quantity = quantity; + } + + public ExpectationLineItemsInner id(String id) { + this.id = id; + return this; + } + + /** + * Line item ID reference. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Line item ID reference.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ExpectationLineItemsInner quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity of this item in this expectation. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity of this item in this expectation.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ExpectationLineItemsInner expectationLineItemsInner = (ExpectationLineItemsInner) o; + return Objects.equals(this.id, expectationLineItemsInner.id) && + Objects.equals(this.quantity, expectationLineItemsInner.quantity); + } + + @Override + public int hashCode() { + return Objects.hash(id, quantity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ExpectationLineItemsInner {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ExpectationLineItemsInner instance; + + public Builder() { + this(new ExpectationLineItemsInner()); + } + + protected Builder(ExpectationLineItemsInner instance) { + this.instance = instance; + } + + protected Builder copyOf(ExpectationLineItemsInner value) { + this.instance.setId(value.id); + this.instance.setQuantity(value.quantity); + return this; + } + + public ExpectationLineItemsInner.Builder id(String id) { + this.instance.id(id); + return this; + } + + public ExpectationLineItemsInner.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + /** + * returns a built ExpectationLineItemsInner instance. + * + * The builder is not reusable (NullPointerException) + */ + public ExpectationLineItemsInner build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ExpectationLineItemsInner.Builder builder() { + return new ExpectationLineItemsInner.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ExpectationLineItemsInner.Builder toBuilder() { + ExpectationLineItemsInner.Builder builder = new ExpectationLineItemsInner.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentAvailableMethodResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentAvailableMethodResponse.java new file mode 100644 index 0000000..156d598 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentAvailableMethodResponse.java @@ -0,0 +1,378 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.lang.Nullable; +import java.util.NoSuchElementException; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Inventory availability hint for a fulfillment method type. + */ + +@Schema(name = "Fulfillment_Available_Method_Response", description = "Inventory availability hint for a fulfillment method type.") +@JsonTypeName("Fulfillment_Available_Method_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentAvailableMethodResponse { + + /** + * Fulfillment method type this availability applies to. + */ + public enum TypeEnum { + SHIPPING("shipping"), + + PICKUP("pickup"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + @Valid + private List lineItemIds = new ArrayList<>(); + + private JsonNullable fulfillableOn = JsonNullable.undefined(); + + private @Nullable String description; + + public FulfillmentAvailableMethodResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentAvailableMethodResponse(TypeEnum type, List lineItemIds) { + this.type = type; + this.lineItemIds = lineItemIds; + } + + public FulfillmentAvailableMethodResponse type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Fulfillment method type this availability applies to. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Fulfillment method type this availability applies to.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public FulfillmentAvailableMethodResponse lineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + return this; + } + + public FulfillmentAvailableMethodResponse addLineItemIdsItem(String lineItemIdsItem) { + if (this.lineItemIds == null) { + this.lineItemIds = new ArrayList<>(); + } + this.lineItemIds.add(lineItemIdsItem); + return this; + } + + /** + * Line items available for this fulfillment method. + * @return lineItemIds + */ + @NotNull + @Schema(name = "line_item_ids", description = "Line items available for this fulfillment method.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_item_ids") + public List getLineItemIds() { + return lineItemIds; + } + + public void setLineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + } + + public FulfillmentAvailableMethodResponse fulfillableOn(String fulfillableOn) { + this.fulfillableOn = JsonNullable.of(fulfillableOn); + return this; + } + + /** + * 'now' for immediate availability, or ISO 8601 date for future (preorders, transfers). + * @return fulfillableOn + */ + + @Schema(name = "fulfillable_on", description = "'now' for immediate availability, or ISO 8601 date for future (preorders, transfers).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("fulfillable_on") + public JsonNullable getFulfillableOn() { + return fulfillableOn; + } + + public void setFulfillableOn(JsonNullable fulfillableOn) { + this.fulfillableOn = fulfillableOn; + } + + public FulfillmentAvailableMethodResponse description(@Nullable String description) { + this.description = description; + return this; + } + + /** + * Human-readable availability info (e.g., 'Available for pickup at Downtown Store today'). + * @return description + */ + + @Schema(name = "description", description = "Human-readable availability info (e.g., 'Available for pickup at Downtown Store today').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("description") + public @Nullable String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentAvailableMethodResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentAvailableMethodResponse fulfillmentAvailableMethodResponse = (FulfillmentAvailableMethodResponse) o; + return Objects.equals(this.type, fulfillmentAvailableMethodResponse.type) && + Objects.equals(this.lineItemIds, fulfillmentAvailableMethodResponse.lineItemIds) && + equalsNullable(this.fulfillableOn, fulfillmentAvailableMethodResponse.fulfillableOn) && + Objects.equals(this.description, fulfillmentAvailableMethodResponse.description) && + Objects.equals(this.additionalProperties, fulfillmentAvailableMethodResponse.additionalProperties); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + } + + @Override + public int hashCode() { + return Objects.hash(type, lineItemIds, hashCodeNullable(fulfillableOn), description, additionalProperties); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentAvailableMethodResponse {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" lineItemIds: ").append(toIndentedString(lineItemIds)).append("\n"); + sb.append(" fulfillableOn: ").append(toIndentedString(fulfillableOn)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentAvailableMethodResponse instance; + + public Builder() { + this(new FulfillmentAvailableMethodResponse()); + } + + protected Builder(FulfillmentAvailableMethodResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentAvailableMethodResponse value) { + this.instance.setType(value.type); + this.instance.setLineItemIds(value.lineItemIds); + this.instance.setFulfillableOn(value.fulfillableOn); + this.instance.setDescription(value.description); + return this; + } + + public FulfillmentAvailableMethodResponse.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public FulfillmentAvailableMethodResponse.Builder lineItemIds(List lineItemIds) { + this.instance.lineItemIds(lineItemIds); + return this; + } + + public FulfillmentAvailableMethodResponse.Builder fulfillableOn(String fulfillableOn) { + this.instance.fulfillableOn(fulfillableOn); + return this; + } + + public FulfillmentAvailableMethodResponse.Builder fulfillableOn(JsonNullable fulfillableOn) { + this.instance.fulfillableOn = fulfillableOn; + return this; + } + + public FulfillmentAvailableMethodResponse.Builder description(String description) { + this.instance.description(description); + return this; + } + + public FulfillmentAvailableMethodResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentAvailableMethodResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentAvailableMethodResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentAvailableMethodResponse.Builder builder() { + return new FulfillmentAvailableMethodResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentAvailableMethodResponse.Builder toBuilder() { + FulfillmentAvailableMethodResponse.Builder builder = new FulfillmentAvailableMethodResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationRequest.java new file mode 100644 index 0000000..9a16a9c --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationRequest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.dev.ucp.service.shopping.model.RetailLocationRequest; +import com.dev.ucp.service.shopping.model.ShippingDestinationRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public interface FulfillmentDestinationRequest { +} diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationResponse.java new file mode 100644 index 0000000..5b51eb9 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentDestinationResponse.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.dev.ucp.service.shopping.model.RetailLocationResponse; +import com.dev.ucp.service.shopping.model.ShippingDestinationResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public interface FulfillmentDestinationResponse { +} diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEvent.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEvent.java new file mode 100644 index 0000000..a017396 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEvent.java @@ -0,0 +1,401 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentEventLineItemsInner; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Append-only fulfillment event representing an actual shipment. References line items by ID. + */ + +@Schema(name = "Fulfillment_Event", description = "Append-only fulfillment event representing an actual shipment. References line items by ID.") +@JsonTypeName("Fulfillment_Event") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentEvent { + + private String id; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private OffsetDateTime occurredAt; + + private String type; + + @Valid + private List<@Valid FulfillmentEventLineItemsInner> lineItems = new ArrayList<>(); + + private @Nullable String trackingNumber; + + private @Nullable URI trackingUrl; + + private @Nullable String carrier; + + private @Nullable String description; + + public FulfillmentEvent() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentEvent(String id, OffsetDateTime occurredAt, String type, List<@Valid FulfillmentEventLineItemsInner> lineItems) { + this.id = id; + this.occurredAt = occurredAt; + this.type = type; + this.lineItems = lineItems; + } + + public FulfillmentEvent id(String id) { + this.id = id; + return this; + } + + /** + * Fulfillment event identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Fulfillment event identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public FulfillmentEvent occurredAt(OffsetDateTime occurredAt) { + this.occurredAt = occurredAt; + return this; + } + + /** + * RFC 3339 timestamp when this fulfillment event occurred. + * @return occurredAt + */ + @NotNull @Valid + @Schema(name = "occurred_at", description = "RFC 3339 timestamp when this fulfillment event occurred.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("occurred_at") + public OffsetDateTime getOccurredAt() { + return occurredAt; + } + + public void setOccurredAt(OffsetDateTime occurredAt) { + this.occurredAt = occurredAt; + } + + public FulfillmentEvent type(String type) { + this.type = type; + return this; + } + + /** + * Fulfillment event type. Common values include: processing (preparing to ship), shipped (handed to carrier), in_transit (in delivery network), delivered (received by buyer), failed_attempt (delivery attempt failed), canceled (fulfillment canceled), undeliverable (cannot be delivered), returned_to_sender (returned to merchant). + * @return type + */ + @NotNull + @Schema(name = "type", description = "Fulfillment event type. Common values include: processing (preparing to ship), shipped (handed to carrier), in_transit (in delivery network), delivered (received by buyer), failed_attempt (delivery attempt failed), canceled (fulfillment canceled), undeliverable (cannot be delivered), returned_to_sender (returned to merchant).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public FulfillmentEvent lineItems(List<@Valid FulfillmentEventLineItemsInner> lineItems) { + this.lineItems = lineItems; + return this; + } + + public FulfillmentEvent addLineItemsItem(FulfillmentEventLineItemsInner lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * Which line items and quantities are fulfilled in this event. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "Which line items and quantities are fulfilled in this event.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid FulfillmentEventLineItemsInner> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid FulfillmentEventLineItemsInner> lineItems) { + this.lineItems = lineItems; + } + + public FulfillmentEvent trackingNumber(@Nullable String trackingNumber) { + this.trackingNumber = trackingNumber; + return this; + } + + /** + * Carrier tracking number (required if type != processing). + * @return trackingNumber + */ + + @Schema(name = "tracking_number", description = "Carrier tracking number (required if type != processing).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("tracking_number") + public @Nullable String getTrackingNumber() { + return trackingNumber; + } + + public void setTrackingNumber(@Nullable String trackingNumber) { + this.trackingNumber = trackingNumber; + } + + public FulfillmentEvent trackingUrl(@Nullable URI trackingUrl) { + this.trackingUrl = trackingUrl; + return this; + } + + /** + * URL to track this shipment (required if type != processing). + * @return trackingUrl + */ + @Valid + @Schema(name = "tracking_url", description = "URL to track this shipment (required if type != processing).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("tracking_url") + public @Nullable URI getTrackingUrl() { + return trackingUrl; + } + + public void setTrackingUrl(@Nullable URI trackingUrl) { + this.trackingUrl = trackingUrl; + } + + public FulfillmentEvent carrier(@Nullable String carrier) { + this.carrier = carrier; + return this; + } + + /** + * Carrier name (e.g., 'FedEx', 'USPS'). + * @return carrier + */ + + @Schema(name = "carrier", description = "Carrier name (e.g., 'FedEx', 'USPS').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("carrier") + public @Nullable String getCarrier() { + return carrier; + } + + public void setCarrier(@Nullable String carrier) { + this.carrier = carrier; + } + + public FulfillmentEvent description(@Nullable String description) { + this.description = description; + return this; + } + + /** + * Human-readable description of the shipment status or delivery information (e.g., 'Delivered to front door', 'Out for delivery'). + * @return description + */ + + @Schema(name = "description", description = "Human-readable description of the shipment status or delivery information (e.g., 'Delivered to front door', 'Out for delivery').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("description") + public @Nullable String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentEvent fulfillmentEvent = (FulfillmentEvent) o; + return Objects.equals(this.id, fulfillmentEvent.id) && + Objects.equals(this.occurredAt, fulfillmentEvent.occurredAt) && + Objects.equals(this.type, fulfillmentEvent.type) && + Objects.equals(this.lineItems, fulfillmentEvent.lineItems) && + Objects.equals(this.trackingNumber, fulfillmentEvent.trackingNumber) && + Objects.equals(this.trackingUrl, fulfillmentEvent.trackingUrl) && + Objects.equals(this.carrier, fulfillmentEvent.carrier) && + Objects.equals(this.description, fulfillmentEvent.description); + } + + @Override + public int hashCode() { + return Objects.hash(id, occurredAt, type, lineItems, trackingNumber, trackingUrl, carrier, description); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentEvent {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" occurredAt: ").append(toIndentedString(occurredAt)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" trackingNumber: ").append(toIndentedString(trackingNumber)).append("\n"); + sb.append(" trackingUrl: ").append(toIndentedString(trackingUrl)).append("\n"); + sb.append(" carrier: ").append(toIndentedString(carrier)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentEvent instance; + + public Builder() { + this(new FulfillmentEvent()); + } + + protected Builder(FulfillmentEvent instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentEvent value) { + this.instance.setId(value.id); + this.instance.setOccurredAt(value.occurredAt); + this.instance.setType(value.type); + this.instance.setLineItems(value.lineItems); + this.instance.setTrackingNumber(value.trackingNumber); + this.instance.setTrackingUrl(value.trackingUrl); + this.instance.setCarrier(value.carrier); + this.instance.setDescription(value.description); + return this; + } + + public FulfillmentEvent.Builder id(String id) { + this.instance.id(id); + return this; + } + + public FulfillmentEvent.Builder occurredAt(OffsetDateTime occurredAt) { + this.instance.occurredAt(occurredAt); + return this; + } + + public FulfillmentEvent.Builder type(String type) { + this.instance.type(type); + return this; + } + + public FulfillmentEvent.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public FulfillmentEvent.Builder trackingNumber(String trackingNumber) { + this.instance.trackingNumber(trackingNumber); + return this; + } + + public FulfillmentEvent.Builder trackingUrl(URI trackingUrl) { + this.instance.trackingUrl(trackingUrl); + return this; + } + + public FulfillmentEvent.Builder carrier(String carrier) { + this.instance.carrier(carrier); + return this; + } + + public FulfillmentEvent.Builder description(String description) { + this.instance.description(description); + return this; + } + + /** + * returns a built FulfillmentEvent instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentEvent build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentEvent.Builder builder() { + return new FulfillmentEvent.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentEvent.Builder toBuilder() { + FulfillmentEvent.Builder builder = new FulfillmentEvent.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEventLineItemsInner.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEventLineItemsInner.java new file mode 100644 index 0000000..865d1bf --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentEventLineItemsInner.java @@ -0,0 +1,202 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * FulfillmentEventLineItemsInner + */ + +@JsonTypeName("Fulfillment_Event_line_items_inner") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentEventLineItemsInner { + + private String id; + + private Integer quantity; + + public FulfillmentEventLineItemsInner() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentEventLineItemsInner(String id, Integer quantity) { + this.id = id; + this.quantity = quantity; + } + + public FulfillmentEventLineItemsInner id(String id) { + this.id = id; + return this; + } + + /** + * Line item ID reference. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Line item ID reference.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public FulfillmentEventLineItemsInner quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity fulfilled in this event. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity fulfilled in this event.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentEventLineItemsInner fulfillmentEventLineItemsInner = (FulfillmentEventLineItemsInner) o; + return Objects.equals(this.id, fulfillmentEventLineItemsInner.id) && + Objects.equals(this.quantity, fulfillmentEventLineItemsInner.quantity); + } + + @Override + public int hashCode() { + return Objects.hash(id, quantity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentEventLineItemsInner {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentEventLineItemsInner instance; + + public Builder() { + this(new FulfillmentEventLineItemsInner()); + } + + protected Builder(FulfillmentEventLineItemsInner instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentEventLineItemsInner value) { + this.instance.setId(value.id); + this.instance.setQuantity(value.quantity); + return this; + } + + public FulfillmentEventLineItemsInner.Builder id(String id) { + this.instance.id(id); + return this; + } + + public FulfillmentEventLineItemsInner.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + /** + * returns a built FulfillmentEventLineItemsInner instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentEventLineItemsInner build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentEventLineItemsInner.Builder builder() { + return new FulfillmentEventLineItemsInner.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentEventLineItemsInner.Builder toBuilder() { + FulfillmentEventLineItemsInner.Builder builder = new FulfillmentEventLineItemsInner.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupCreateRequest.java new file mode 100644 index 0000000..dc29e41 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupCreateRequest.java @@ -0,0 +1,229 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Arrays; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.lang.Nullable; +import java.util.NoSuchElementException; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A merchant-generated package/group of line items with fulfillment options. + */ + +@Schema(name = "Fulfillment_Group_Create_Request", description = "A merchant-generated package/group of line items with fulfillment options.") +@JsonTypeName("Fulfillment_Group_Create_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentGroupCreateRequest { + + private JsonNullable selectedOptionId = JsonNullable.undefined(); + + public FulfillmentGroupCreateRequest selectedOptionId(String selectedOptionId) { + this.selectedOptionId = JsonNullable.of(selectedOptionId); + return this; + } + + /** + * ID of the selected fulfillment option for this group. + * @return selectedOptionId + */ + + @Schema(name = "selected_option_id", description = "ID of the selected fulfillment option for this group.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_option_id") + public JsonNullable getSelectedOptionId() { + return selectedOptionId; + } + + public void setSelectedOptionId(JsonNullable selectedOptionId) { + this.selectedOptionId = selectedOptionId; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentGroupCreateRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentGroupCreateRequest fulfillmentGroupCreateRequest = (FulfillmentGroupCreateRequest) o; + return equalsNullable(this.selectedOptionId, fulfillmentGroupCreateRequest.selectedOptionId) && + Objects.equals(this.additionalProperties, fulfillmentGroupCreateRequest.additionalProperties); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + } + + @Override + public int hashCode() { + return Objects.hash(hashCodeNullable(selectedOptionId), additionalProperties); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentGroupCreateRequest {\n"); + sb.append(" selectedOptionId: ").append(toIndentedString(selectedOptionId)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentGroupCreateRequest instance; + + public Builder() { + this(new FulfillmentGroupCreateRequest()); + } + + protected Builder(FulfillmentGroupCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentGroupCreateRequest value) { + this.instance.setSelectedOptionId(value.selectedOptionId); + return this; + } + + public FulfillmentGroupCreateRequest.Builder selectedOptionId(String selectedOptionId) { + this.instance.selectedOptionId(selectedOptionId); + return this; + } + + public FulfillmentGroupCreateRequest.Builder selectedOptionId(JsonNullable selectedOptionId) { + this.instance.selectedOptionId = selectedOptionId; + return this; + } + + public FulfillmentGroupCreateRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentGroupCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentGroupCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentGroupCreateRequest.Builder builder() { + return new FulfillmentGroupCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentGroupCreateRequest.Builder toBuilder() { + FulfillmentGroupCreateRequest.Builder builder = new FulfillmentGroupCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupResponse.java new file mode 100644 index 0000000..4d0e41d --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentGroupResponse.java @@ -0,0 +1,352 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentOptionResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.lang.Nullable; +import java.util.NoSuchElementException; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A merchant-generated package/group of line items with fulfillment options. + */ + +@Schema(name = "Fulfillment_Group_Response", description = "A merchant-generated package/group of line items with fulfillment options.") +@JsonTypeName("Fulfillment_Group_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentGroupResponse { + + private String id; + + @Valid + private List lineItemIds = new ArrayList<>(); + + @Valid + private List options = new ArrayList<>(); + + private JsonNullable selectedOptionId = JsonNullable.undefined(); + + public FulfillmentGroupResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentGroupResponse(String id, List lineItemIds) { + this.id = id; + this.lineItemIds = lineItemIds; + } + + public FulfillmentGroupResponse id(String id) { + this.id = id; + return this; + } + + /** + * Group identifier for referencing merchant-generated groups in updates. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Group identifier for referencing merchant-generated groups in updates.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public FulfillmentGroupResponse lineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + return this; + } + + public FulfillmentGroupResponse addLineItemIdsItem(String lineItemIdsItem) { + if (this.lineItemIds == null) { + this.lineItemIds = new ArrayList<>(); + } + this.lineItemIds.add(lineItemIdsItem); + return this; + } + + /** + * Line item IDs included in this group/package. + * @return lineItemIds + */ + @NotNull + @Schema(name = "line_item_ids", description = "Line item IDs included in this group/package.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_item_ids") + public List getLineItemIds() { + return lineItemIds; + } + + public void setLineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + } + + public FulfillmentGroupResponse options(List options) { + this.options = options; + return this; + } + + public FulfillmentGroupResponse addOptionsItem(FulfillmentOptionResponse optionsItem) { + if (this.options == null) { + this.options = new ArrayList<>(); + } + this.options.add(optionsItem); + return this; + } + + /** + * Available fulfillment options for this group. + * @return options + */ + @Valid + @Schema(name = "options", description = "Available fulfillment options for this group.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("options") + public List getOptions() { + return options; + } + + public void setOptions(List options) { + this.options = options; + } + + public FulfillmentGroupResponse selectedOptionId(String selectedOptionId) { + this.selectedOptionId = JsonNullable.of(selectedOptionId); + return this; + } + + /** + * ID of the selected fulfillment option for this group. + * @return selectedOptionId + */ + + @Schema(name = "selected_option_id", description = "ID of the selected fulfillment option for this group.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_option_id") + public JsonNullable getSelectedOptionId() { + return selectedOptionId; + } + + public void setSelectedOptionId(JsonNullable selectedOptionId) { + this.selectedOptionId = selectedOptionId; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentGroupResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentGroupResponse fulfillmentGroupResponse = (FulfillmentGroupResponse) o; + return Objects.equals(this.id, fulfillmentGroupResponse.id) && + Objects.equals(this.lineItemIds, fulfillmentGroupResponse.lineItemIds) && + Objects.equals(this.options, fulfillmentGroupResponse.options) && + equalsNullable(this.selectedOptionId, fulfillmentGroupResponse.selectedOptionId) && + Objects.equals(this.additionalProperties, fulfillmentGroupResponse.additionalProperties); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + } + + @Override + public int hashCode() { + return Objects.hash(id, lineItemIds, options, hashCodeNullable(selectedOptionId), additionalProperties); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentGroupResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" lineItemIds: ").append(toIndentedString(lineItemIds)).append("\n"); + sb.append(" options: ").append(toIndentedString(options)).append("\n"); + sb.append(" selectedOptionId: ").append(toIndentedString(selectedOptionId)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentGroupResponse instance; + + public Builder() { + this(new FulfillmentGroupResponse()); + } + + protected Builder(FulfillmentGroupResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentGroupResponse value) { + this.instance.setId(value.id); + this.instance.setLineItemIds(value.lineItemIds); + this.instance.setOptions(value.options); + this.instance.setSelectedOptionId(value.selectedOptionId); + return this; + } + + public FulfillmentGroupResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public FulfillmentGroupResponse.Builder lineItemIds(List lineItemIds) { + this.instance.lineItemIds(lineItemIds); + return this; + } + + public FulfillmentGroupResponse.Builder options(List options) { + this.instance.options(options); + return this; + } + + public FulfillmentGroupResponse.Builder selectedOptionId(String selectedOptionId) { + this.instance.selectedOptionId(selectedOptionId); + return this; + } + + public FulfillmentGroupResponse.Builder selectedOptionId(JsonNullable selectedOptionId) { + this.instance.selectedOptionId = selectedOptionId; + return this; + } + + public FulfillmentGroupResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentGroupResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentGroupResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentGroupResponse.Builder builder() { + return new FulfillmentGroupResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentGroupResponse.Builder toBuilder() { + FulfillmentGroupResponse.Builder builder = new FulfillmentGroupResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodCreateRequest.java new file mode 100644 index 0000000..29714e3 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodCreateRequest.java @@ -0,0 +1,427 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationRequest; +import com.dev.ucp.service.shopping.model.FulfillmentGroupCreateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.lang.Nullable; +import java.util.NoSuchElementException; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A fulfillment method (shipping or pickup) with destinations and groups. + */ + +@Schema(name = "Fulfillment_Method_Create_Request", description = "A fulfillment method (shipping or pickup) with destinations and groups.") +@JsonTypeName("Fulfillment_Method_Create_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentMethodCreateRequest { + + /** + * Fulfillment method type. + */ + public enum TypeEnum { + SHIPPING("shipping"), + + PICKUP("pickup"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + @Valid + private List lineItemIds = new ArrayList<>(); + + @Valid + private List<@Valid FulfillmentDestinationRequest> destinations = new ArrayList<>(); + + private JsonNullable selectedDestinationId = JsonNullable.undefined(); + + @Valid + private List groups = new ArrayList<>(); + + public FulfillmentMethodCreateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentMethodCreateRequest(TypeEnum type) { + this.type = type; + } + + public FulfillmentMethodCreateRequest type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Fulfillment method type. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Fulfillment method type.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public FulfillmentMethodCreateRequest lineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + return this; + } + + public FulfillmentMethodCreateRequest addLineItemIdsItem(String lineItemIdsItem) { + if (this.lineItemIds == null) { + this.lineItemIds = new ArrayList<>(); + } + this.lineItemIds.add(lineItemIdsItem); + return this; + } + + /** + * Line item IDs fulfilled via this method. + * @return lineItemIds + */ + + @Schema(name = "line_item_ids", description = "Line item IDs fulfilled via this method.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("line_item_ids") + public List getLineItemIds() { + return lineItemIds; + } + + public void setLineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + } + + public FulfillmentMethodCreateRequest destinations(List<@Valid FulfillmentDestinationRequest> destinations) { + this.destinations = destinations; + return this; + } + + public FulfillmentMethodCreateRequest addDestinationsItem(FulfillmentDestinationRequest destinationsItem) { + if (this.destinations == null) { + this.destinations = new ArrayList<>(); + } + this.destinations.add(destinationsItem); + return this; + } + + /** + * Available destinations. For shipping: addresses. For pickup: retail locations. + * @return destinations + */ + @Valid + @Schema(name = "destinations", description = "Available destinations. For shipping: addresses. For pickup: retail locations.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("destinations") + public List<@Valid FulfillmentDestinationRequest> getDestinations() { + return destinations; + } + + public void setDestinations(List<@Valid FulfillmentDestinationRequest> destinations) { + this.destinations = destinations; + } + + public FulfillmentMethodCreateRequest selectedDestinationId(String selectedDestinationId) { + this.selectedDestinationId = JsonNullable.of(selectedDestinationId); + return this; + } + + /** + * ID of the selected destination. + * @return selectedDestinationId + */ + + @Schema(name = "selected_destination_id", description = "ID of the selected destination.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_destination_id") + public JsonNullable getSelectedDestinationId() { + return selectedDestinationId; + } + + public void setSelectedDestinationId(JsonNullable selectedDestinationId) { + this.selectedDestinationId = selectedDestinationId; + } + + public FulfillmentMethodCreateRequest groups(List groups) { + this.groups = groups; + return this; + } + + public FulfillmentMethodCreateRequest addGroupsItem(FulfillmentGroupCreateRequest groupsItem) { + if (this.groups == null) { + this.groups = new ArrayList<>(); + } + this.groups.add(groupsItem); + return this; + } + + /** + * Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method. + * @return groups + */ + @Valid + @Schema(name = "groups", description = "Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("groups") + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentMethodCreateRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentMethodCreateRequest fulfillmentMethodCreateRequest = (FulfillmentMethodCreateRequest) o; + return Objects.equals(this.type, fulfillmentMethodCreateRequest.type) && + Objects.equals(this.lineItemIds, fulfillmentMethodCreateRequest.lineItemIds) && + Objects.equals(this.destinations, fulfillmentMethodCreateRequest.destinations) && + equalsNullable(this.selectedDestinationId, fulfillmentMethodCreateRequest.selectedDestinationId) && + Objects.equals(this.groups, fulfillmentMethodCreateRequest.groups) && + Objects.equals(this.additionalProperties, fulfillmentMethodCreateRequest.additionalProperties); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + } + + @Override + public int hashCode() { + return Objects.hash(type, lineItemIds, destinations, hashCodeNullable(selectedDestinationId), groups, additionalProperties); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentMethodCreateRequest {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" lineItemIds: ").append(toIndentedString(lineItemIds)).append("\n"); + sb.append(" destinations: ").append(toIndentedString(destinations)).append("\n"); + sb.append(" selectedDestinationId: ").append(toIndentedString(selectedDestinationId)).append("\n"); + sb.append(" groups: ").append(toIndentedString(groups)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentMethodCreateRequest instance; + + public Builder() { + this(new FulfillmentMethodCreateRequest()); + } + + protected Builder(FulfillmentMethodCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentMethodCreateRequest value) { + this.instance.setType(value.type); + this.instance.setLineItemIds(value.lineItemIds); + this.instance.setDestinations(value.destinations); + this.instance.setSelectedDestinationId(value.selectedDestinationId); + this.instance.setGroups(value.groups); + return this; + } + + public FulfillmentMethodCreateRequest.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public FulfillmentMethodCreateRequest.Builder lineItemIds(List lineItemIds) { + this.instance.lineItemIds(lineItemIds); + return this; + } + + public FulfillmentMethodCreateRequest.Builder destinations(List destinations) { + this.instance.destinations(destinations); + return this; + } + + public FulfillmentMethodCreateRequest.Builder selectedDestinationId(String selectedDestinationId) { + this.instance.selectedDestinationId(selectedDestinationId); + return this; + } + + public FulfillmentMethodCreateRequest.Builder selectedDestinationId(JsonNullable selectedDestinationId) { + this.instance.selectedDestinationId = selectedDestinationId; + return this; + } + + public FulfillmentMethodCreateRequest.Builder groups(List groups) { + this.instance.groups(groups); + return this; + } + + public FulfillmentMethodCreateRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentMethodCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentMethodCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentMethodCreateRequest.Builder builder() { + return new FulfillmentMethodCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentMethodCreateRequest.Builder toBuilder() { + FulfillmentMethodCreateRequest.Builder builder = new FulfillmentMethodCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodResponse.java new file mode 100644 index 0000000..4ccde5a --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentMethodResponse.java @@ -0,0 +1,459 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationResponse; +import com.dev.ucp.service.shopping.model.FulfillmentGroupResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.lang.Nullable; +import java.util.NoSuchElementException; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A fulfillment method (shipping or pickup) with destinations and groups. + */ + +@Schema(name = "Fulfillment_Method_Response", description = "A fulfillment method (shipping or pickup) with destinations and groups.") +@JsonTypeName("Fulfillment_Method_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentMethodResponse { + + private String id; + + /** + * Fulfillment method type. + */ + public enum TypeEnum { + SHIPPING("shipping"), + + PICKUP("pickup"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + @Valid + private List lineItemIds = new ArrayList<>(); + + @Valid + private List<@Valid FulfillmentDestinationResponse> destinations = new ArrayList<>(); + + private JsonNullable selectedDestinationId = JsonNullable.undefined(); + + @Valid + private List groups = new ArrayList<>(); + + public FulfillmentMethodResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentMethodResponse(String id, TypeEnum type, List lineItemIds) { + this.id = id; + this.type = type; + this.lineItemIds = lineItemIds; + } + + public FulfillmentMethodResponse id(String id) { + this.id = id; + return this; + } + + /** + * Unique fulfillment method identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique fulfillment method identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public FulfillmentMethodResponse type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Fulfillment method type. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Fulfillment method type.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public FulfillmentMethodResponse lineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + return this; + } + + public FulfillmentMethodResponse addLineItemIdsItem(String lineItemIdsItem) { + if (this.lineItemIds == null) { + this.lineItemIds = new ArrayList<>(); + } + this.lineItemIds.add(lineItemIdsItem); + return this; + } + + /** + * Line item IDs fulfilled via this method. + * @return lineItemIds + */ + @NotNull + @Schema(name = "line_item_ids", description = "Line item IDs fulfilled via this method.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_item_ids") + public List getLineItemIds() { + return lineItemIds; + } + + public void setLineItemIds(List lineItemIds) { + this.lineItemIds = lineItemIds; + } + + public FulfillmentMethodResponse destinations(List<@Valid FulfillmentDestinationResponse> destinations) { + this.destinations = destinations; + return this; + } + + public FulfillmentMethodResponse addDestinationsItem(FulfillmentDestinationResponse destinationsItem) { + if (this.destinations == null) { + this.destinations = new ArrayList<>(); + } + this.destinations.add(destinationsItem); + return this; + } + + /** + * Available destinations. For shipping: addresses. For pickup: retail locations. + * @return destinations + */ + @Valid + @Schema(name = "destinations", description = "Available destinations. For shipping: addresses. For pickup: retail locations.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("destinations") + public List<@Valid FulfillmentDestinationResponse> getDestinations() { + return destinations; + } + + public void setDestinations(List<@Valid FulfillmentDestinationResponse> destinations) { + this.destinations = destinations; + } + + public FulfillmentMethodResponse selectedDestinationId(String selectedDestinationId) { + this.selectedDestinationId = JsonNullable.of(selectedDestinationId); + return this; + } + + /** + * ID of the selected destination. + * @return selectedDestinationId + */ + + @Schema(name = "selected_destination_id", description = "ID of the selected destination.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_destination_id") + public JsonNullable getSelectedDestinationId() { + return selectedDestinationId; + } + + public void setSelectedDestinationId(JsonNullable selectedDestinationId) { + this.selectedDestinationId = selectedDestinationId; + } + + public FulfillmentMethodResponse groups(List groups) { + this.groups = groups; + return this; + } + + public FulfillmentMethodResponse addGroupsItem(FulfillmentGroupResponse groupsItem) { + if (this.groups == null) { + this.groups = new ArrayList<>(); + } + this.groups.add(groupsItem); + return this; + } + + /** + * Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method. + * @return groups + */ + @Valid + @Schema(name = "groups", description = "Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("groups") + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentMethodResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentMethodResponse fulfillmentMethodResponse = (FulfillmentMethodResponse) o; + return Objects.equals(this.id, fulfillmentMethodResponse.id) && + Objects.equals(this.type, fulfillmentMethodResponse.type) && + Objects.equals(this.lineItemIds, fulfillmentMethodResponse.lineItemIds) && + Objects.equals(this.destinations, fulfillmentMethodResponse.destinations) && + equalsNullable(this.selectedDestinationId, fulfillmentMethodResponse.selectedDestinationId) && + Objects.equals(this.groups, fulfillmentMethodResponse.groups) && + Objects.equals(this.additionalProperties, fulfillmentMethodResponse.additionalProperties); + } + + private static boolean equalsNullable(JsonNullable a, JsonNullable b) { + return a == b || (a != null && b != null && a.isPresent() && b.isPresent() && Objects.deepEquals(a.get(), b.get())); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, lineItemIds, destinations, hashCodeNullable(selectedDestinationId), groups, additionalProperties); + } + + private static int hashCodeNullable(JsonNullable a) { + if (a == null) { + return 1; + } + return a.isPresent() ? Arrays.deepHashCode(new Object[]{a.get()}) : 31; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentMethodResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" lineItemIds: ").append(toIndentedString(lineItemIds)).append("\n"); + sb.append(" destinations: ").append(toIndentedString(destinations)).append("\n"); + sb.append(" selectedDestinationId: ").append(toIndentedString(selectedDestinationId)).append("\n"); + sb.append(" groups: ").append(toIndentedString(groups)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentMethodResponse instance; + + public Builder() { + this(new FulfillmentMethodResponse()); + } + + protected Builder(FulfillmentMethodResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentMethodResponse value) { + this.instance.setId(value.id); + this.instance.setType(value.type); + this.instance.setLineItemIds(value.lineItemIds); + this.instance.setDestinations(value.destinations); + this.instance.setSelectedDestinationId(value.selectedDestinationId); + this.instance.setGroups(value.groups); + return this; + } + + public FulfillmentMethodResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public FulfillmentMethodResponse.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public FulfillmentMethodResponse.Builder lineItemIds(List lineItemIds) { + this.instance.lineItemIds(lineItemIds); + return this; + } + + public FulfillmentMethodResponse.Builder destinations(List destinations) { + this.instance.destinations(destinations); + return this; + } + + public FulfillmentMethodResponse.Builder selectedDestinationId(String selectedDestinationId) { + this.instance.selectedDestinationId(selectedDestinationId); + return this; + } + + public FulfillmentMethodResponse.Builder selectedDestinationId(JsonNullable selectedDestinationId) { + this.instance.selectedDestinationId = selectedDestinationId; + return this; + } + + public FulfillmentMethodResponse.Builder groups(List groups) { + this.instance.groups(groups); + return this; + } + + public FulfillmentMethodResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentMethodResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentMethodResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentMethodResponse.Builder builder() { + return new FulfillmentMethodResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentMethodResponse.Builder toBuilder() { + FulfillmentMethodResponse.Builder builder = new FulfillmentMethodResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentOptionResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentOptionResponse.java new file mode 100644 index 0000000..59e6b5e --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentOptionResponse.java @@ -0,0 +1,420 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A fulfillment option within a group (e.g., Standard Shipping $5, Express $15). + */ + +@Schema(name = "Fulfillment_Option_Response", description = "A fulfillment option within a group (e.g., Standard Shipping $5, Express $15).") +@JsonTypeName("Fulfillment_Option_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentOptionResponse { + + private String id; + + private String title; + + private @Nullable String description; + + private @Nullable String carrier; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private @Nullable OffsetDateTime earliestFulfillmentTime; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private @Nullable OffsetDateTime latestFulfillmentTime; + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + public FulfillmentOptionResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public FulfillmentOptionResponse(String id, String title, List<@Valid TotalResponse> totals) { + this.id = id; + this.title = title; + this.totals = totals; + } + + public FulfillmentOptionResponse id(String id) { + this.id = id; + return this; + } + + /** + * Unique fulfillment option identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique fulfillment option identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public FulfillmentOptionResponse title(String title) { + this.title = title; + return this; + } + + /** + * Short label (e.g., 'Express Shipping', 'Curbside Pickup'). + * @return title + */ + @NotNull + @Schema(name = "title", description = "Short label (e.g., 'Express Shipping', 'Curbside Pickup').", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public FulfillmentOptionResponse description(@Nullable String description) { + this.description = description; + return this; + } + + /** + * Complete context for buyer decision (e.g., 'Arrives Dec 12-15 via FedEx'). + * @return description + */ + + @Schema(name = "description", description = "Complete context for buyer decision (e.g., 'Arrives Dec 12-15 via FedEx').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("description") + public @Nullable String getDescription() { + return description; + } + + public void setDescription(@Nullable String description) { + this.description = description; + } + + public FulfillmentOptionResponse carrier(@Nullable String carrier) { + this.carrier = carrier; + return this; + } + + /** + * Carrier name (for shipping). + * @return carrier + */ + + @Schema(name = "carrier", description = "Carrier name (for shipping).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("carrier") + public @Nullable String getCarrier() { + return carrier; + } + + public void setCarrier(@Nullable String carrier) { + this.carrier = carrier; + } + + public FulfillmentOptionResponse earliestFulfillmentTime(@Nullable OffsetDateTime earliestFulfillmentTime) { + this.earliestFulfillmentTime = earliestFulfillmentTime; + return this; + } + + /** + * Earliest fulfillment date. + * @return earliestFulfillmentTime + */ + @Valid + @Schema(name = "earliest_fulfillment_time", description = "Earliest fulfillment date.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("earliest_fulfillment_time") + public @Nullable OffsetDateTime getEarliestFulfillmentTime() { + return earliestFulfillmentTime; + } + + public void setEarliestFulfillmentTime(@Nullable OffsetDateTime earliestFulfillmentTime) { + this.earliestFulfillmentTime = earliestFulfillmentTime; + } + + public FulfillmentOptionResponse latestFulfillmentTime(@Nullable OffsetDateTime latestFulfillmentTime) { + this.latestFulfillmentTime = latestFulfillmentTime; + return this; + } + + /** + * Latest fulfillment date. + * @return latestFulfillmentTime + */ + @Valid + @Schema(name = "latest_fulfillment_time", description = "Latest fulfillment date.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("latest_fulfillment_time") + public @Nullable OffsetDateTime getLatestFulfillmentTime() { + return latestFulfillmentTime; + } + + public void setLatestFulfillmentTime(@Nullable OffsetDateTime latestFulfillmentTime) { + this.latestFulfillmentTime = latestFulfillmentTime; + } + + public FulfillmentOptionResponse totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public FulfillmentOptionResponse addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Fulfillment option totals breakdown. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Fulfillment option totals breakdown.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public FulfillmentOptionResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentOptionResponse fulfillmentOptionResponse = (FulfillmentOptionResponse) o; + return Objects.equals(this.id, fulfillmentOptionResponse.id) && + Objects.equals(this.title, fulfillmentOptionResponse.title) && + Objects.equals(this.description, fulfillmentOptionResponse.description) && + Objects.equals(this.carrier, fulfillmentOptionResponse.carrier) && + Objects.equals(this.earliestFulfillmentTime, fulfillmentOptionResponse.earliestFulfillmentTime) && + Objects.equals(this.latestFulfillmentTime, fulfillmentOptionResponse.latestFulfillmentTime) && + Objects.equals(this.totals, fulfillmentOptionResponse.totals) && + Objects.equals(this.additionalProperties, fulfillmentOptionResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, description, carrier, earliestFulfillmentTime, latestFulfillmentTime, totals, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentOptionResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append(" description: ").append(toIndentedString(description)).append("\n"); + sb.append(" carrier: ").append(toIndentedString(carrier)).append("\n"); + sb.append(" earliestFulfillmentTime: ").append(toIndentedString(earliestFulfillmentTime)).append("\n"); + sb.append(" latestFulfillmentTime: ").append(toIndentedString(latestFulfillmentTime)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentOptionResponse instance; + + public Builder() { + this(new FulfillmentOptionResponse()); + } + + protected Builder(FulfillmentOptionResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentOptionResponse value) { + this.instance.setId(value.id); + this.instance.setTitle(value.title); + this.instance.setDescription(value.description); + this.instance.setCarrier(value.carrier); + this.instance.setEarliestFulfillmentTime(value.earliestFulfillmentTime); + this.instance.setLatestFulfillmentTime(value.latestFulfillmentTime); + this.instance.setTotals(value.totals); + return this; + } + + public FulfillmentOptionResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public FulfillmentOptionResponse.Builder title(String title) { + this.instance.title(title); + return this; + } + + public FulfillmentOptionResponse.Builder description(String description) { + this.instance.description(description); + return this; + } + + public FulfillmentOptionResponse.Builder carrier(String carrier) { + this.instance.carrier(carrier); + return this; + } + + public FulfillmentOptionResponse.Builder earliestFulfillmentTime(OffsetDateTime earliestFulfillmentTime) { + this.instance.earliestFulfillmentTime(earliestFulfillmentTime); + return this; + } + + public FulfillmentOptionResponse.Builder latestFulfillmentTime(OffsetDateTime latestFulfillmentTime) { + this.instance.latestFulfillmentTime(latestFulfillmentTime); + return this; + } + + public FulfillmentOptionResponse.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + public FulfillmentOptionResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built FulfillmentOptionResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentOptionResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentOptionResponse.Builder builder() { + return new FulfillmentOptionResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentOptionResponse.Builder toBuilder() { + FulfillmentOptionResponse.Builder builder = new FulfillmentOptionResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentRequest.java new file mode 100644 index 0000000..3c9bfa0 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentRequest.java @@ -0,0 +1,173 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentMethodCreateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Container for fulfillment methods and availability. + */ + +@Schema(name = "fulfillment_request", description = "Container for fulfillment methods and availability.") +@JsonTypeName("fulfillment_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentRequest { + + @Valid + private List methods = new ArrayList<>(); + + public FulfillmentRequest methods(List methods) { + this.methods = methods; + return this; + } + + public FulfillmentRequest addMethodsItem(FulfillmentMethodCreateRequest methodsItem) { + if (this.methods == null) { + this.methods = new ArrayList<>(); + } + this.methods.add(methodsItem); + return this; + } + + /** + * Fulfillment methods for cart items. + * @return methods + */ + @Valid + @Schema(name = "methods", description = "Fulfillment methods for cart items.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("methods") + public List getMethods() { + return methods; + } + + public void setMethods(List methods) { + this.methods = methods; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentRequest fulfillmentRequest = (FulfillmentRequest) o; + return Objects.equals(this.methods, fulfillmentRequest.methods); + } + + @Override + public int hashCode() { + return Objects.hash(methods); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentRequest {\n"); + sb.append(" methods: ").append(toIndentedString(methods)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentRequest instance; + + public Builder() { + this(new FulfillmentRequest()); + } + + protected Builder(FulfillmentRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentRequest value) { + this.instance.setMethods(value.methods); + return this; + } + + public FulfillmentRequest.Builder methods(List methods) { + this.instance.methods(methods); + return this; + } + + /** + * returns a built FulfillmentRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentRequest.Builder builder() { + return new FulfillmentRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentRequest.Builder toBuilder() { + FulfillmentRequest.Builder builder = new FulfillmentRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentResponse.java new file mode 100644 index 0000000..9a856ca --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/FulfillmentResponse.java @@ -0,0 +1,213 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.FulfillmentAvailableMethodResponse; +import com.dev.ucp.service.shopping.model.FulfillmentMethodResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Container for fulfillment methods and availability. + */ + +@Schema(name = "fulfillment_response", description = "Container for fulfillment methods and availability.") +@JsonTypeName("fulfillment_response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class FulfillmentResponse { + + @Valid + private List methods = new ArrayList<>(); + + @Valid + private List availableMethods = new ArrayList<>(); + + public FulfillmentResponse methods(List methods) { + this.methods = methods; + return this; + } + + public FulfillmentResponse addMethodsItem(FulfillmentMethodResponse methodsItem) { + if (this.methods == null) { + this.methods = new ArrayList<>(); + } + this.methods.add(methodsItem); + return this; + } + + /** + * Fulfillment methods for cart items. + * @return methods + */ + @Valid + @Schema(name = "methods", description = "Fulfillment methods for cart items.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("methods") + public List getMethods() { + return methods; + } + + public void setMethods(List methods) { + this.methods = methods; + } + + public FulfillmentResponse availableMethods(List availableMethods) { + this.availableMethods = availableMethods; + return this; + } + + public FulfillmentResponse addAvailableMethodsItem(FulfillmentAvailableMethodResponse availableMethodsItem) { + if (this.availableMethods == null) { + this.availableMethods = new ArrayList<>(); + } + this.availableMethods.add(availableMethodsItem); + return this; + } + + /** + * Inventory availability hints. + * @return availableMethods + */ + @Valid + @Schema(name = "available_methods", description = "Inventory availability hints.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("available_methods") + public List getAvailableMethods() { + return availableMethods; + } + + public void setAvailableMethods(List availableMethods) { + this.availableMethods = availableMethods; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + FulfillmentResponse fulfillmentResponse = (FulfillmentResponse) o; + return Objects.equals(this.methods, fulfillmentResponse.methods) && + Objects.equals(this.availableMethods, fulfillmentResponse.availableMethods); + } + + @Override + public int hashCode() { + return Objects.hash(methods, availableMethods); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class FulfillmentResponse {\n"); + sb.append(" methods: ").append(toIndentedString(methods)).append("\n"); + sb.append(" availableMethods: ").append(toIndentedString(availableMethods)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private FulfillmentResponse instance; + + public Builder() { + this(new FulfillmentResponse()); + } + + protected Builder(FulfillmentResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(FulfillmentResponse value) { + this.instance.setMethods(value.methods); + this.instance.setAvailableMethods(value.availableMethods); + return this; + } + + public FulfillmentResponse.Builder methods(List methods) { + this.instance.methods(methods); + return this; + } + + public FulfillmentResponse.Builder availableMethods(List availableMethods) { + this.instance.availableMethods(availableMethods); + return this; + } + + /** + * returns a built FulfillmentResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public FulfillmentResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static FulfillmentResponse.Builder builder() { + return new FulfillmentResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public FulfillmentResponse.Builder toBuilder() { + FulfillmentResponse.Builder builder = new FulfillmentResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemCreateRequest.java new file mode 100644 index 0000000..7809655 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemCreateRequest.java @@ -0,0 +1,170 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * ItemCreateRequest + */ + +@JsonTypeName("Item_Create_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ItemCreateRequest { + + private String id; + + public ItemCreateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ItemCreateRequest(String id) { + this.id = id; + } + + public ItemCreateRequest id(String id) { + this.id = id; + return this; + } + + /** + * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ItemCreateRequest itemCreateRequest = (ItemCreateRequest) o; + return Objects.equals(this.id, itemCreateRequest.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ItemCreateRequest {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ItemCreateRequest instance; + + public Builder() { + this(new ItemCreateRequest()); + } + + protected Builder(ItemCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(ItemCreateRequest value) { + this.instance.setId(value.id); + return this; + } + + public ItemCreateRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + /** + * returns a built ItemCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public ItemCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ItemCreateRequest.Builder builder() { + return new ItemCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ItemCreateRequest.Builder toBuilder() { + ItemCreateRequest.Builder builder = new ItemCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemResponse.java new file mode 100644 index 0000000..ce38370 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemResponse.java @@ -0,0 +1,264 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.net.URI; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * ItemResponse + */ + +@JsonTypeName("Item_Response_1") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ItemResponse { + + private String id; + + private String title; + + private Integer price; + + private @Nullable URI imageUrl; + + public ItemResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ItemResponse(String id, String title, Integer price) { + this.id = id; + this.title = title; + this.price = price; + } + + public ItemResponse id(String id) { + this.id = id; + return this; + } + + /** + * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ItemResponse title(String title) { + this.title = title; + return this; + } + + /** + * Product title. + * @return title + */ + @NotNull + @Schema(name = "title", description = "Product title.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public ItemResponse price(Integer price) { + this.price = price; + return this; + } + + /** + * Unit price in minor (cents) currency units. + * minimum: 0 + * @return price + */ + @NotNull @Min(value = 0) + @Schema(name = "price", description = "Unit price in minor (cents) currency units.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("price") + public Integer getPrice() { + return price; + } + + public void setPrice(Integer price) { + this.price = price; + } + + public ItemResponse imageUrl(@Nullable URI imageUrl) { + this.imageUrl = imageUrl; + return this; + } + + /** + * Product image URI. + * @return imageUrl + */ + @Valid + @Schema(name = "image_url", description = "Product image URI.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("image_url") + public @Nullable URI getImageUrl() { + return imageUrl; + } + + public void setImageUrl(@Nullable URI imageUrl) { + this.imageUrl = imageUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ItemResponse itemResponse1 = (ItemResponse) o; + return Objects.equals(this.id, itemResponse1.id) && + Objects.equals(this.title, itemResponse1.title) && + Objects.equals(this.price, itemResponse1.price) && + Objects.equals(this.imageUrl, itemResponse1.imageUrl); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, price, imageUrl); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ItemResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append(" price: ").append(toIndentedString(price)).append("\n"); + sb.append(" imageUrl: ").append(toIndentedString(imageUrl)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ItemResponse instance; + + public Builder() { + this(new ItemResponse()); + } + + protected Builder(ItemResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(ItemResponse value) { + this.instance.setId(value.id); + this.instance.setTitle(value.title); + this.instance.setPrice(value.price); + this.instance.setImageUrl(value.imageUrl); + return this; + } + + public ItemResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public ItemResponse.Builder title(String title) { + this.instance.title(title); + return this; + } + + public ItemResponse.Builder price(Integer price) { + this.instance.price(price); + return this; + } + + public ItemResponse.Builder imageUrl(URI imageUrl) { + this.instance.imageUrl(imageUrl); + return this; + } + + /** + * returns a built ItemResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public ItemResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ItemResponse.Builder builder() { + return new ItemResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ItemResponse.Builder toBuilder() { + ItemResponse.Builder builder = new ItemResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemUpdateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemUpdateRequest.java new file mode 100644 index 0000000..fcfdde9 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ItemUpdateRequest.java @@ -0,0 +1,170 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * ItemUpdateRequest + */ + +@JsonTypeName("Item_Update_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ItemUpdateRequest { + + private String id; + + public ItemUpdateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ItemUpdateRequest(String id) { + this.id = id; + } + + public ItemUpdateRequest id(String id) { + this.id = id; + return this; + } + + /** + * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the \"id\" field in the product feed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ItemUpdateRequest itemUpdateRequest = (ItemUpdateRequest) o; + return Objects.equals(this.id, itemUpdateRequest.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ItemUpdateRequest {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ItemUpdateRequest instance; + + public Builder() { + this(new ItemUpdateRequest()); + } + + protected Builder(ItemUpdateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(ItemUpdateRequest value) { + this.instance.setId(value.id); + return this; + } + + public ItemUpdateRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + /** + * returns a built ItemUpdateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public ItemUpdateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ItemUpdateRequest.Builder builder() { + return new ItemUpdateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ItemUpdateRequest.Builder toBuilder() { + ItemUpdateRequest.Builder builder = new ItemUpdateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemCreateRequest.java new file mode 100644 index 0000000..5376ca1 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemCreateRequest.java @@ -0,0 +1,204 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.ItemCreateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Line item object. Expected to use the currency of the parent object. + */ + +@Schema(name = "Line_Item_Create_Request", description = "Line item object. Expected to use the currency of the parent object.") +@JsonTypeName("Line_Item_Create_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class LineItemCreateRequest { + + private ItemCreateRequest item; + + private Integer quantity; + + public LineItemCreateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public LineItemCreateRequest(ItemCreateRequest item, Integer quantity) { + this.item = item; + this.quantity = quantity; + } + + public LineItemCreateRequest item(ItemCreateRequest item) { + this.item = item; + return this; + } + + /** + * Get item + * @return item + */ + @NotNull @Valid + @Schema(name = "item", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("item") + public ItemCreateRequest getItem() { + return item; + } + + public void setItem(ItemCreateRequest item) { + this.item = item; + } + + public LineItemCreateRequest quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity of the item being purchased. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity of the item being purchased.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LineItemCreateRequest lineItemCreateRequest = (LineItemCreateRequest) o; + return Objects.equals(this.item, lineItemCreateRequest.item) && + Objects.equals(this.quantity, lineItemCreateRequest.quantity); + } + + @Override + public int hashCode() { + return Objects.hash(item, quantity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class LineItemCreateRequest {\n"); + sb.append(" item: ").append(toIndentedString(item)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private LineItemCreateRequest instance; + + public Builder() { + this(new LineItemCreateRequest()); + } + + protected Builder(LineItemCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(LineItemCreateRequest value) { + this.instance.setItem(value.item); + this.instance.setQuantity(value.quantity); + return this; + } + + public LineItemCreateRequest.Builder item(ItemCreateRequest item) { + this.instance.item(item); + return this; + } + + public LineItemCreateRequest.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + /** + * returns a built LineItemCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public LineItemCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static LineItemCreateRequest.Builder builder() { + return new LineItemCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public LineItemCreateRequest.Builder toBuilder() { + LineItemCreateRequest.Builder builder = new LineItemCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemResponse.java new file mode 100644 index 0000000..b0e936a --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemResponse.java @@ -0,0 +1,309 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.ItemResponse; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Line item object. Expected to use the currency of the parent object. + */ + +@Schema(name = "Line_Item_Response", description = "Line item object. Expected to use the currency of the parent object.") +@JsonTypeName("Line_Item_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class LineItemResponse { + + private String id; + + private ItemResponse item; + + private Integer quantity; + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + private @Nullable String parentId; + + public LineItemResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public LineItemResponse(String id, ItemResponse item, Integer quantity, List<@Valid TotalResponse> totals) { + this.id = id; + this.item = item; + this.quantity = quantity; + this.totals = totals; + } + + public LineItemResponse id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + @NotNull + @Schema(name = "id", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public LineItemResponse item(ItemResponse item) { + this.item = item; + return this; + } + + /** + * Get item + * @return item + */ + @NotNull @Valid + @Schema(name = "item", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("item") + public ItemResponse getItem() { + return item; + } + + public void setItem(ItemResponse item) { + this.item = item; + } + + public LineItemResponse quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity of the item being purchased. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity of the item being purchased.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public LineItemResponse totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public LineItemResponse addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Line item totals breakdown. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Line item totals breakdown.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + + public LineItemResponse parentId(@Nullable String parentId) { + this.parentId = parentId; + return this; + } + + /** + * Parent line item identifier for any nested structures. + * @return parentId + */ + + @Schema(name = "parent_id", description = "Parent line item identifier for any nested structures.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("parent_id") + public @Nullable String getParentId() { + return parentId; + } + + public void setParentId(@Nullable String parentId) { + this.parentId = parentId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LineItemResponse lineItemResponse = (LineItemResponse) o; + return Objects.equals(this.id, lineItemResponse.id) && + Objects.equals(this.item, lineItemResponse.item) && + Objects.equals(this.quantity, lineItemResponse.quantity) && + Objects.equals(this.totals, lineItemResponse.totals) && + Objects.equals(this.parentId, lineItemResponse.parentId); + } + + @Override + public int hashCode() { + return Objects.hash(id, item, quantity, totals, parentId); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class LineItemResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" item: ").append(toIndentedString(item)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + sb.append(" parentId: ").append(toIndentedString(parentId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private LineItemResponse instance; + + public Builder() { + this(new LineItemResponse()); + } + + protected Builder(LineItemResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(LineItemResponse value) { + this.instance.setId(value.id); + this.instance.setItem(value.item); + this.instance.setQuantity(value.quantity); + this.instance.setTotals(value.totals); + this.instance.setParentId(value.parentId); + return this; + } + + public LineItemResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public LineItemResponse.Builder item(ItemResponse item) { + this.instance.item(item); + return this; + } + + public LineItemResponse.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + public LineItemResponse.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + public LineItemResponse.Builder parentId(String parentId) { + this.instance.parentId(parentId); + return this; + } + + /** + * returns a built LineItemResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public LineItemResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static LineItemResponse.Builder builder() { + return new LineItemResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public LineItemResponse.Builder toBuilder() { + LineItemResponse.Builder builder = new LineItemResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemUpdateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemUpdateRequest.java new file mode 100644 index 0000000..3310a25 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/LineItemUpdateRequest.java @@ -0,0 +1,264 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.ItemUpdateRequest; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Line item object. Expected to use the currency of the parent object. + */ + +@Schema(name = "Line_Item_Update_Request", description = "Line item object. Expected to use the currency of the parent object.") +@JsonTypeName("Line_Item_Update_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class LineItemUpdateRequest { + + private @Nullable String id; + + private ItemUpdateRequest item; + + private Integer quantity; + + private @Nullable String parentId; + + public LineItemUpdateRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public LineItemUpdateRequest(ItemUpdateRequest item, Integer quantity) { + this.item = item; + this.quantity = quantity; + } + + public LineItemUpdateRequest id(@Nullable String id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + + @Schema(name = "id", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("id") + public @Nullable String getId() { + return id; + } + + public void setId(@Nullable String id) { + this.id = id; + } + + public LineItemUpdateRequest item(ItemUpdateRequest item) { + this.item = item; + return this; + } + + /** + * Get item + * @return item + */ + @NotNull @Valid + @Schema(name = "item", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("item") + public ItemUpdateRequest getItem() { + return item; + } + + public void setItem(ItemUpdateRequest item) { + this.item = item; + } + + public LineItemUpdateRequest quantity(Integer quantity) { + this.quantity = quantity; + return this; + } + + /** + * Quantity of the item being purchased. + * minimum: 1 + * @return quantity + */ + @NotNull @Min(value = 1) + @Schema(name = "quantity", description = "Quantity of the item being purchased.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public LineItemUpdateRequest parentId(@Nullable String parentId) { + this.parentId = parentId; + return this; + } + + /** + * Parent line item identifier for any nested structures. + * @return parentId + */ + + @Schema(name = "parent_id", description = "Parent line item identifier for any nested structures.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("parent_id") + public @Nullable String getParentId() { + return parentId; + } + + public void setParentId(@Nullable String parentId) { + this.parentId = parentId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LineItemUpdateRequest lineItemUpdateRequest = (LineItemUpdateRequest) o; + return Objects.equals(this.id, lineItemUpdateRequest.id) && + Objects.equals(this.item, lineItemUpdateRequest.item) && + Objects.equals(this.quantity, lineItemUpdateRequest.quantity) && + Objects.equals(this.parentId, lineItemUpdateRequest.parentId); + } + + @Override + public int hashCode() { + return Objects.hash(id, item, quantity, parentId); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class LineItemUpdateRequest {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" item: ").append(toIndentedString(item)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append(" parentId: ").append(toIndentedString(parentId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private LineItemUpdateRequest instance; + + public Builder() { + this(new LineItemUpdateRequest()); + } + + protected Builder(LineItemUpdateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(LineItemUpdateRequest value) { + this.instance.setId(value.id); + this.instance.setItem(value.item); + this.instance.setQuantity(value.quantity); + this.instance.setParentId(value.parentId); + return this; + } + + public LineItemUpdateRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + public LineItemUpdateRequest.Builder item(ItemUpdateRequest item) { + this.instance.item(item); + return this; + } + + public LineItemUpdateRequest.Builder quantity(Integer quantity) { + this.instance.quantity(quantity); + return this; + } + + public LineItemUpdateRequest.Builder parentId(String parentId) { + this.instance.parentId(parentId); + return this; + } + + /** + * returns a built LineItemUpdateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public LineItemUpdateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static LineItemUpdateRequest.Builder builder() { + return new LineItemUpdateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public LineItemUpdateRequest.Builder toBuilder() { + LineItemUpdateRequest.Builder builder = new LineItemUpdateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Link.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Link.java new file mode 100644 index 0000000..e8e4e59 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Link.java @@ -0,0 +1,230 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.net.URI; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Link + */ + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class Link { + + private String type; + + private URI url; + + private @Nullable String title; + + public Link() { + super(); + } + + /** + * Constructor with only required parameters + */ + public Link(String type, URI url) { + this.type = type; + this.url = url; + } + + public Link type(String type) { + this.type = type; + return this; + } + + /** + * Type of link. Well-known values: `privacy_policy`, `terms_of_service`, `refund_policy`, `shipping_policy`, `faq`. Consumers SHOULD handle unknown values gracefully by displaying them using the `title` field or omitting the link. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Type of link. Well-known values: `privacy_policy`, `terms_of_service`, `refund_policy`, `shipping_policy`, `faq`. Consumers SHOULD handle unknown values gracefully by displaying them using the `title` field or omitting the link.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Link url(URI url) { + this.url = url; + return this; + } + + /** + * The actual URL pointing to the content to be displayed. + * @return url + */ + @NotNull @Valid + @Schema(name = "url", description = "The actual URL pointing to the content to be displayed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("url") + public URI getUrl() { + return url; + } + + public void setUrl(URI url) { + this.url = url; + } + + public Link title(@Nullable String title) { + this.title = title; + return this; + } + + /** + * Optional display text for the link. When provided, use this instead of generating from type. + * @return title + */ + + @Schema(name = "title", description = "Optional display text for the link. When provided, use this instead of generating from type.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("title") + public @Nullable String getTitle() { + return title; + } + + public void setTitle(@Nullable String title) { + this.title = title; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Link link = (Link) o; + return Objects.equals(this.type, link.type) && + Objects.equals(this.url, link.url) && + Objects.equals(this.title, link.title); + } + + @Override + public int hashCode() { + return Objects.hash(type, url, title); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Link {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" url: ").append(toIndentedString(url)).append("\n"); + sb.append(" title: ").append(toIndentedString(title)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private Link instance; + + public Builder() { + this(new Link()); + } + + protected Builder(Link instance) { + this.instance = instance; + } + + protected Builder copyOf(Link value) { + this.instance.setType(value.type); + this.instance.setUrl(value.url); + this.instance.setTitle(value.title); + return this; + } + + public Link.Builder type(String type) { + this.instance.type(type); + return this; + } + + public Link.Builder url(URI url) { + this.instance.url(url); + return this; + } + + public Link.Builder title(String title) { + this.instance.title(title); + return this; + } + + /** + * returns a built Link instance. + * + * The builder is not reusable (NullPointerException) + */ + public Link build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static Link.Builder builder() { + return new Link.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public Link.Builder toBuilder() { + Link.Builder builder = new Link.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Message.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Message.java new file mode 100644 index 0000000..5fe3a1f --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Message.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.MessageInfo; +import com.dev.ucp.service.shopping.model.MessageWarning; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public interface Message { +} diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageError.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageError.java new file mode 100644 index 0000000..f679f95 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageError.java @@ -0,0 +1,430 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.databind.JsonNode; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * MessageError + */ + +@JsonTypeName("Message_Error") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class MessageError implements Message { + + /** + * Message type discriminator. + */ + public enum TypeEnum { + ERROR("error"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + private String code; + + private @Nullable String path; + + /** + * Content format, default = plain. + */ + public enum ContentTypeEnum { + PLAIN("plain"), + + MARKDOWN("markdown"); + + private final String value; + + ContentTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ContentTypeEnum fromValue(String value) { + for (ContentTypeEnum b : ContentTypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private ContentTypeEnum contentType = ContentTypeEnum.PLAIN; + + private String content; + + /** + * Declares who resolves this error. 'recoverable': agent can fix via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules (checkout complete). Errors with 'requires_*' severity contribute to 'status: requires_escalation'. + */ + public enum SeverityEnum { + RECOVERABLE("recoverable"), + + REQUIRES_BUYER_INPUT("requires_buyer_input"), + + REQUIRES_BUYER_REVIEW("requires_buyer_review"); + + private final String value; + + SeverityEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static SeverityEnum fromValue(String value) { + for (SeverityEnum b : SeverityEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private SeverityEnum severity; + + public MessageError() { + super(); + } + + /** + * Constructor with only required parameters + */ + public MessageError(TypeEnum type, String code, String content, SeverityEnum severity) { + this.type = type; + this.code = code; + this.content = content; + this.severity = severity; + } + + public MessageError type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Message type discriminator. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Message type discriminator.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public MessageError code(String code) { + this.code = code; + return this; + } + + /** + * Error code. Possible values include: missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking. Freeform codes also allowed. + * @return code + */ + @NotNull + @Schema(name = "code", description = "Error code. Possible values include: missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking. Freeform codes also allowed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("code") + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public MessageError path(@Nullable String path) { + this.path = path; + return this; + } + + /** + * RFC 9535 JSONPath to the component the message refers to (e.g., $.items[1]). + * @return path + */ + + @Schema(name = "path", description = "RFC 9535 JSONPath to the component the message refers to (e.g., $.items[1]).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("path") + public @Nullable String getPath() { + return path; + } + + public void setPath(@Nullable String path) { + this.path = path; + } + + public MessageError contentType(ContentTypeEnum contentType) { + this.contentType = contentType; + return this; + } + + /** + * Content format, default = plain. + * @return contentType + */ + + @Schema(name = "content_type", description = "Content format, default = plain.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("content_type") + public ContentTypeEnum getContentType() { + return contentType; + } + + public void setContentType(ContentTypeEnum contentType) { + this.contentType = contentType; + } + + public MessageError content(String content) { + this.content = content; + return this; + } + + /** + * Human-readable message. + * @return content + */ + @NotNull + @Schema(name = "content", description = "Human-readable message.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("content") + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public MessageError severity(SeverityEnum severity) { + this.severity = severity; + return this; + } + + /** + * Declares who resolves this error. 'recoverable': agent can fix via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules (checkout complete). Errors with 'requires_*' severity contribute to 'status: requires_escalation'. + * @return severity + */ + @NotNull + @Schema(name = "severity", description = "Declares who resolves this error. 'recoverable': agent can fix via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules (checkout complete). Errors with 'requires_*' severity contribute to 'status: requires_escalation'.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("severity") + public SeverityEnum getSeverity() { + return severity; + } + + public void setSeverity(SeverityEnum severity) { + this.severity = severity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MessageError messageError = (MessageError) o; + return Objects.equals(this.type, messageError.type) && + Objects.equals(this.code, messageError.code) && + Objects.equals(this.path, messageError.path) && + Objects.equals(this.contentType, messageError.contentType) && + Objects.equals(this.content, messageError.content) && + Objects.equals(this.severity, messageError.severity); + } + + @Override + public int hashCode() { + return Objects.hash(type, code, path, contentType, content, severity); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class MessageError {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append(" severity: ").append(toIndentedString(severity)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private MessageError instance; + + public Builder() { + this(new MessageError()); + } + + protected Builder(MessageError instance) { + this.instance = instance; + } + + protected Builder copyOf(MessageError value) { + this.instance.setType(value.type); + this.instance.setCode(value.code); + this.instance.setPath(value.path); + this.instance.setContentType(value.contentType); + this.instance.setContent(value.content); + this.instance.setSeverity(value.severity); + return this; + } + + public MessageError.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public MessageError.Builder code(String code) { + this.instance.code(code); + return this; + } + + public MessageError.Builder path(String path) { + this.instance.path(path); + return this; + } + + public MessageError.Builder contentType(ContentTypeEnum contentType) { + this.instance.contentType(contentType); + return this; + } + + public MessageError.Builder content(String content) { + this.instance.content(content); + return this; + } + + public MessageError.Builder severity(SeverityEnum severity) { + this.instance.severity(severity); + return this; + } + + /** + * returns a built MessageError instance. + * + * The builder is not reusable (NullPointerException) + */ + public MessageError build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static MessageError.Builder builder() { + return new MessageError.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public MessageError.Builder toBuilder() { + MessageError.Builder builder = new MessageError.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageInfo.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageInfo.java new file mode 100644 index 0000000..a837c57 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageInfo.java @@ -0,0 +1,361 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.databind.JsonNode; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * MessageInfo + */ + +@JsonTypeName("Message_Info") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class MessageInfo implements Message { + + /** + * Message type discriminator. + */ + public enum TypeEnum { + INFO("info"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + private @Nullable String path; + + private @Nullable String code; + + /** + * Content format, default = plain. + */ + public enum ContentTypeEnum { + PLAIN("plain"), + + MARKDOWN("markdown"); + + private final String value; + + ContentTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ContentTypeEnum fromValue(String value) { + for (ContentTypeEnum b : ContentTypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private ContentTypeEnum contentType = ContentTypeEnum.PLAIN; + + private String content; + + public MessageInfo() { + super(); + } + + /** + * Constructor with only required parameters + */ + public MessageInfo(TypeEnum type, String content) { + this.type = type; + this.content = content; + } + + public MessageInfo type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Message type discriminator. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Message type discriminator.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public MessageInfo path(@Nullable String path) { + this.path = path; + return this; + } + + /** + * RFC 9535 JSONPath to the component the message refers to. + * @return path + */ + + @Schema(name = "path", description = "RFC 9535 JSONPath to the component the message refers to.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("path") + public @Nullable String getPath() { + return path; + } + + public void setPath(@Nullable String path) { + this.path = path; + } + + public MessageInfo code(@Nullable String code) { + this.code = code; + return this; + } + + /** + * Info code for programmatic handling. + * @return code + */ + + @Schema(name = "code", description = "Info code for programmatic handling.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("code") + public @Nullable String getCode() { + return code; + } + + public void setCode(@Nullable String code) { + this.code = code; + } + + public MessageInfo contentType(ContentTypeEnum contentType) { + this.contentType = contentType; + return this; + } + + /** + * Content format, default = plain. + * @return contentType + */ + + @Schema(name = "content_type", description = "Content format, default = plain.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("content_type") + public ContentTypeEnum getContentType() { + return contentType; + } + + public void setContentType(ContentTypeEnum contentType) { + this.contentType = contentType; + } + + public MessageInfo content(String content) { + this.content = content; + return this; + } + + /** + * Human-readable message. + * @return content + */ + @NotNull + @Schema(name = "content", description = "Human-readable message.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("content") + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MessageInfo messageInfo = (MessageInfo) o; + return Objects.equals(this.type, messageInfo.type) && + Objects.equals(this.path, messageInfo.path) && + Objects.equals(this.code, messageInfo.code) && + Objects.equals(this.contentType, messageInfo.contentType) && + Objects.equals(this.content, messageInfo.content); + } + + @Override + public int hashCode() { + return Objects.hash(type, path, code, contentType, content); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class MessageInfo {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private MessageInfo instance; + + public Builder() { + this(new MessageInfo()); + } + + protected Builder(MessageInfo instance) { + this.instance = instance; + } + + protected Builder copyOf(MessageInfo value) { + this.instance.setType(value.type); + this.instance.setPath(value.path); + this.instance.setCode(value.code); + this.instance.setContentType(value.contentType); + this.instance.setContent(value.content); + return this; + } + + public MessageInfo.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public MessageInfo.Builder path(String path) { + this.instance.path(path); + return this; + } + + public MessageInfo.Builder code(String code) { + this.instance.code(code); + return this; + } + + public MessageInfo.Builder contentType(ContentTypeEnum contentType) { + this.instance.contentType(contentType); + return this; + } + + public MessageInfo.Builder content(String content) { + this.instance.content(content); + return this; + } + + /** + * returns a built MessageInfo instance. + * + * The builder is not reusable (NullPointerException) + */ + public MessageInfo build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static MessageInfo.Builder builder() { + return new MessageInfo.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public MessageInfo.Builder toBuilder() { + MessageInfo.Builder builder = new MessageInfo.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageWarning.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageWarning.java new file mode 100644 index 0000000..410ac2f --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/MessageWarning.java @@ -0,0 +1,362 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.databind.JsonNode; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * MessageWarning + */ + +@JsonTypeName("Message_Warning") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class MessageWarning implements Message { + + /** + * Message type discriminator. + */ + public enum TypeEnum { + WARNING("warning"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + private @Nullable String path; + + private String code; + + private String content; + + /** + * Content format, default = plain. + */ + public enum ContentTypeEnum { + PLAIN("plain"), + + MARKDOWN("markdown"); + + private final String value; + + ContentTypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ContentTypeEnum fromValue(String value) { + for (ContentTypeEnum b : ContentTypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private ContentTypeEnum contentType = ContentTypeEnum.PLAIN; + + public MessageWarning() { + super(); + } + + /** + * Constructor with only required parameters + */ + public MessageWarning(TypeEnum type, String code, String content) { + this.type = type; + this.code = code; + this.content = content; + } + + public MessageWarning type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Message type discriminator. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Message type discriminator.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public MessageWarning path(@Nullable String path) { + this.path = path; + return this; + } + + /** + * JSONPath (RFC 9535) to related field (e.g., $.line_items[0]). + * @return path + */ + + @Schema(name = "path", description = "JSONPath (RFC 9535) to related field (e.g., $.line_items[0]).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("path") + public @Nullable String getPath() { + return path; + } + + public void setPath(@Nullable String path) { + this.path = path; + } + + public MessageWarning code(String code) { + this.code = code; + return this; + } + + /** + * Warning code. Machine-readable identifier for the warning type (e.g., final_sale, prop65, fulfillment_changed, age_restricted, etc.). + * @return code + */ + @NotNull + @Schema(name = "code", description = "Warning code. Machine-readable identifier for the warning type (e.g., final_sale, prop65, fulfillment_changed, age_restricted, etc.).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("code") + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public MessageWarning content(String content) { + this.content = content; + return this; + } + + /** + * Human-readable warning message that MUST be displayed. + * @return content + */ + @NotNull + @Schema(name = "content", description = "Human-readable warning message that MUST be displayed.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("content") + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public MessageWarning contentType(ContentTypeEnum contentType) { + this.contentType = contentType; + return this; + } + + /** + * Content format, default = plain. + * @return contentType + */ + + @Schema(name = "content_type", description = "Content format, default = plain.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("content_type") + public ContentTypeEnum getContentType() { + return contentType; + } + + public void setContentType(ContentTypeEnum contentType) { + this.contentType = contentType; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MessageWarning messageWarning = (MessageWarning) o; + return Objects.equals(this.type, messageWarning.type) && + Objects.equals(this.path, messageWarning.path) && + Objects.equals(this.code, messageWarning.code) && + Objects.equals(this.content, messageWarning.content) && + Objects.equals(this.contentType, messageWarning.contentType); + } + + @Override + public int hashCode() { + return Objects.hash(type, path, code, content, contentType); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class MessageWarning {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" path: ").append(toIndentedString(path)).append("\n"); + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" content: ").append(toIndentedString(content)).append("\n"); + sb.append(" contentType: ").append(toIndentedString(contentType)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private MessageWarning instance; + + public Builder() { + this(new MessageWarning()); + } + + protected Builder(MessageWarning instance) { + this.instance = instance; + } + + protected Builder copyOf(MessageWarning value) { + this.instance.setType(value.type); + this.instance.setPath(value.path); + this.instance.setCode(value.code); + this.instance.setContent(value.content); + this.instance.setContentType(value.contentType); + return this; + } + + public MessageWarning.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public MessageWarning.Builder path(String path) { + this.instance.path(path); + return this; + } + + public MessageWarning.Builder code(String code) { + this.instance.code(code); + return this; + } + + public MessageWarning.Builder content(String content) { + this.instance.content(content); + return this; + } + + public MessageWarning.Builder contentType(ContentTypeEnum contentType) { + this.instance.contentType(contentType); + return this; + } + + /** + * returns a built MessageWarning instance. + * + * The builder is not reusable (NullPointerException) + */ + public MessageWarning build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static MessageWarning.Builder builder() { + return new MessageWarning.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public MessageWarning.Builder toBuilder() { + MessageWarning.Builder builder = new MessageWarning.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Order.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Order.java new file mode 100644 index 0000000..8cab183 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/Order.java @@ -0,0 +1,423 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Adjustment; +import com.dev.ucp.service.shopping.model.OrderFulfillment; +import com.dev.ucp.service.shopping.model.OrderLineItem; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.dev.ucp.service.shopping.model.UCPOrderResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Order schema with immutable line items, buyer-facing fulfillment expectations, and append-only event logs. + */ + +@Schema(name = "order", description = "Order schema with immutable line items, buyer-facing fulfillment expectations, and append-only event logs.") +@JsonTypeName("order") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class Order { + + private UCPOrderResponse ucp; + + private String id; + + private String checkoutId; + + private URI permalinkUrl; + + @Valid + private List<@Valid OrderLineItem> lineItems = new ArrayList<>(); + + private OrderFulfillment fulfillment; + + @Valid + private List<@Valid Adjustment> adjustments = new ArrayList<>(); + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + public Order() { + super(); + } + + /** + * Constructor with only required parameters + */ + public Order(UCPOrderResponse ucp, String id, String checkoutId, URI permalinkUrl, List<@Valid OrderLineItem> lineItems, OrderFulfillment fulfillment, List<@Valid TotalResponse> totals) { + this.ucp = ucp; + this.id = id; + this.checkoutId = checkoutId; + this.permalinkUrl = permalinkUrl; + this.lineItems = lineItems; + this.fulfillment = fulfillment; + this.totals = totals; + } + + public Order ucp(UCPOrderResponse ucp) { + this.ucp = ucp; + return this; + } + + /** + * Get ucp + * @return ucp + */ + @NotNull @Valid + @Schema(name = "ucp", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("ucp") + public UCPOrderResponse getUcp() { + return ucp; + } + + public void setUcp(UCPOrderResponse ucp) { + this.ucp = ucp; + } + + public Order id(String id) { + this.id = id; + return this; + } + + /** + * Unique order identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique order identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Order checkoutId(String checkoutId) { + this.checkoutId = checkoutId; + return this; + } + + /** + * Associated checkout ID for reconciliation. + * @return checkoutId + */ + @NotNull + @Schema(name = "checkout_id", description = "Associated checkout ID for reconciliation.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("checkout_id") + public String getCheckoutId() { + return checkoutId; + } + + public void setCheckoutId(String checkoutId) { + this.checkoutId = checkoutId; + } + + public Order permalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + return this; + } + + /** + * Permalink to access the order on merchant site. + * @return permalinkUrl + */ + @NotNull @Valid + @Schema(name = "permalink_url", description = "Permalink to access the order on merchant site.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("permalink_url") + public URI getPermalinkUrl() { + return permalinkUrl; + } + + public void setPermalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + } + + public Order lineItems(List<@Valid OrderLineItem> lineItems) { + this.lineItems = lineItems; + return this; + } + + public Order addLineItemsItem(OrderLineItem lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * Immutable line items — source of truth for what was ordered. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "Immutable line items — source of truth for what was ordered.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid OrderLineItem> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid OrderLineItem> lineItems) { + this.lineItems = lineItems; + } + + public Order fulfillment(OrderFulfillment fulfillment) { + this.fulfillment = fulfillment; + return this; + } + + /** + * Get fulfillment + * @return fulfillment + */ + @NotNull @Valid + @Schema(name = "fulfillment", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("fulfillment") + public OrderFulfillment getFulfillment() { + return fulfillment; + } + + public void setFulfillment(OrderFulfillment fulfillment) { + this.fulfillment = fulfillment; + } + + public Order adjustments(List<@Valid Adjustment> adjustments) { + this.adjustments = adjustments; + return this; + } + + public Order addAdjustmentsItem(Adjustment adjustmentsItem) { + if (this.adjustments == null) { + this.adjustments = new ArrayList<>(); + } + this.adjustments.add(adjustmentsItem); + return this; + } + + /** + * Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + * @return adjustments + */ + @Valid + @Schema(name = "adjustments", description = "Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("adjustments") + public List<@Valid Adjustment> getAdjustments() { + return adjustments; + } + + public void setAdjustments(List<@Valid Adjustment> adjustments) { + this.adjustments = adjustments; + } + + public Order totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public Order addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Different totals for the order. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Different totals for the order.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Order order = (Order) o; + return Objects.equals(this.ucp, order.ucp) && + Objects.equals(this.id, order.id) && + Objects.equals(this.checkoutId, order.checkoutId) && + Objects.equals(this.permalinkUrl, order.permalinkUrl) && + Objects.equals(this.lineItems, order.lineItems) && + Objects.equals(this.fulfillment, order.fulfillment) && + Objects.equals(this.adjustments, order.adjustments) && + Objects.equals(this.totals, order.totals); + } + + @Override + public int hashCode() { + return Objects.hash(ucp, id, checkoutId, permalinkUrl, lineItems, fulfillment, adjustments, totals); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Order {\n"); + sb.append(" ucp: ").append(toIndentedString(ucp)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" checkoutId: ").append(toIndentedString(checkoutId)).append("\n"); + sb.append(" permalinkUrl: ").append(toIndentedString(permalinkUrl)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" fulfillment: ").append(toIndentedString(fulfillment)).append("\n"); + sb.append(" adjustments: ").append(toIndentedString(adjustments)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private Order instance; + + public Builder() { + this(new Order()); + } + + protected Builder(Order instance) { + this.instance = instance; + } + + protected Builder copyOf(Order value) { + this.instance.setUcp(value.ucp); + this.instance.setId(value.id); + this.instance.setCheckoutId(value.checkoutId); + this.instance.setPermalinkUrl(value.permalinkUrl); + this.instance.setLineItems(value.lineItems); + this.instance.setFulfillment(value.fulfillment); + this.instance.setAdjustments(value.adjustments); + this.instance.setTotals(value.totals); + return this; + } + + public Order.Builder ucp(UCPOrderResponse ucp) { + this.instance.ucp(ucp); + return this; + } + + public Order.Builder id(String id) { + this.instance.id(id); + return this; + } + + public Order.Builder checkoutId(String checkoutId) { + this.instance.checkoutId(checkoutId); + return this; + } + + public Order.Builder permalinkUrl(URI permalinkUrl) { + this.instance.permalinkUrl(permalinkUrl); + return this; + } + + public Order.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public Order.Builder fulfillment(OrderFulfillment fulfillment) { + this.instance.fulfillment(fulfillment); + return this; + } + + public Order.Builder adjustments(List adjustments) { + this.instance.adjustments(adjustments); + return this; + } + + public Order.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + /** + * returns a built Order instance. + * + * The builder is not reusable (NullPointerException) + */ + public Order build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static Order.Builder builder() { + return new Order.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public Order.Builder toBuilder() { + Order.Builder builder = new Order.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderConfirmation.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderConfirmation.java new file mode 100644 index 0000000..158a12e --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderConfirmation.java @@ -0,0 +1,203 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.net.URI; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Details about an order created for this checkout session. + */ + +@Schema(name = "Order_Confirmation", description = "Details about an order created for this checkout session.") +@JsonTypeName("Order_Confirmation") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderConfirmation { + + private String id; + + private URI permalinkUrl; + + public OrderConfirmation() { + super(); + } + + /** + * Constructor with only required parameters + */ + public OrderConfirmation(String id, URI permalinkUrl) { + this.id = id; + this.permalinkUrl = permalinkUrl; + } + + public OrderConfirmation id(String id) { + this.id = id; + return this; + } + + /** + * Unique order identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique order identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public OrderConfirmation permalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + return this; + } + + /** + * Permalink to access the order on merchant site. + * @return permalinkUrl + */ + @NotNull @Valid + @Schema(name = "permalink_url", description = "Permalink to access the order on merchant site.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("permalink_url") + public URI getPermalinkUrl() { + return permalinkUrl; + } + + public void setPermalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderConfirmation orderConfirmation = (OrderConfirmation) o; + return Objects.equals(this.id, orderConfirmation.id) && + Objects.equals(this.permalinkUrl, orderConfirmation.permalinkUrl); + } + + @Override + public int hashCode() { + return Objects.hash(id, permalinkUrl); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderConfirmation {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" permalinkUrl: ").append(toIndentedString(permalinkUrl)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderConfirmation instance; + + public Builder() { + this(new OrderConfirmation()); + } + + protected Builder(OrderConfirmation instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderConfirmation value) { + this.instance.setId(value.id); + this.instance.setPermalinkUrl(value.permalinkUrl); + return this; + } + + public OrderConfirmation.Builder id(String id) { + this.instance.id(id); + return this; + } + + public OrderConfirmation.Builder permalinkUrl(URI permalinkUrl) { + this.instance.permalinkUrl(permalinkUrl); + return this; + } + + /** + * returns a built OrderConfirmation instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderConfirmation build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderConfirmation.Builder builder() { + return new OrderConfirmation.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderConfirmation.Builder toBuilder() { + OrderConfirmation.Builder builder = new OrderConfirmation.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhook200Response.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhook200Response.java new file mode 100644 index 0000000..0192101 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhook200Response.java @@ -0,0 +1,176 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * OrderEventWebhook200Response + */ + +@JsonTypeName("order_event_webhook_200_response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderEventWebhook200Response { + + private JsonNullable ucp = JsonNullable.undefined(); + + public OrderEventWebhook200Response() { + super(); + } + + /** + * Constructor with only required parameters + */ + public OrderEventWebhook200Response(JsonNode ucp) { + this.ucp = JsonNullable.of(ucp); + } + + public OrderEventWebhook200Response ucp(JsonNode ucp) { + this.ucp = JsonNullable.of(ucp); + return this; + } + + /** + * Protocol metadata for discovery profiles and responses. Uses slim schema pattern with context-specific required fields. + * @return ucp + */ + @NotNull @Valid + @Schema(name = "ucp", description = "Protocol metadata for discovery profiles and responses. Uses slim schema pattern with context-specific required fields.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("ucp") + public JsonNullable getUcp() { + return ucp; + } + + public void setUcp(JsonNullable ucp) { + this.ucp = ucp; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderEventWebhook200Response orderEventWebhook200Response = (OrderEventWebhook200Response) o; + return Objects.equals(this.ucp, orderEventWebhook200Response.ucp); + } + + @Override + public int hashCode() { + return Objects.hash(ucp); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderEventWebhook200Response {\n"); + sb.append(" ucp: ").append(toIndentedString(ucp)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderEventWebhook200Response instance; + + public Builder() { + this(new OrderEventWebhook200Response()); + } + + protected Builder(OrderEventWebhook200Response instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderEventWebhook200Response value) { + this.instance.setUcp(value.ucp); + return this; + } + + public OrderEventWebhook200Response.Builder ucp(JsonNode ucp) { + this.instance.ucp(ucp); + return this; + } + + public OrderEventWebhook200Response.Builder ucp(JsonNullable ucp) { + this.instance.ucp = ucp; + return this; + } + + /** + * returns a built OrderEventWebhook200Response instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderEventWebhook200Response build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderEventWebhook200Response.Builder builder() { + return new OrderEventWebhook200Response.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderEventWebhook200Response.Builder toBuilder() { + OrderEventWebhook200Response.Builder builder = new OrderEventWebhook200Response.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhookRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhookRequest.java new file mode 100644 index 0000000..6df5e80 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderEventWebhookRequest.java @@ -0,0 +1,537 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Adjustment; +import com.dev.ucp.service.shopping.model.OrderFulfillment; +import com.dev.ucp.service.shopping.model.OrderLineItem; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.dev.ucp.service.shopping.model.UCPOrderResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * OrderEventWebhookRequest + */ + +@JsonTypeName("order_event_webhook_request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderEventWebhookRequest { + + private UCPOrderResponse ucp; + + private String id; + + private String checkoutId; + + private URI permalinkUrl; + + @Valid + private List<@Valid OrderLineItem> lineItems = new ArrayList<>(); + + private OrderFulfillment fulfillment; + + @Valid + private List<@Valid Adjustment> adjustments = new ArrayList<>(); + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + private String eventId; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private OffsetDateTime createdTime; + + public OrderEventWebhookRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public OrderEventWebhookRequest(UCPOrderResponse ucp, String id, String checkoutId, URI permalinkUrl, List<@Valid OrderLineItem> lineItems, OrderFulfillment fulfillment, List<@Valid TotalResponse> totals, String eventId, OffsetDateTime createdTime) { + this.ucp = ucp; + this.id = id; + this.checkoutId = checkoutId; + this.permalinkUrl = permalinkUrl; + this.lineItems = lineItems; + this.fulfillment = fulfillment; + this.totals = totals; + this.eventId = eventId; + this.createdTime = createdTime; + } + + public OrderEventWebhookRequest ucp(UCPOrderResponse ucp) { + this.ucp = ucp; + return this; + } + + /** + * Get ucp + * @return ucp + */ + @NotNull @Valid + @Schema(name = "ucp", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("ucp") + public UCPOrderResponse getUcp() { + return ucp; + } + + public void setUcp(UCPOrderResponse ucp) { + this.ucp = ucp; + } + + public OrderEventWebhookRequest id(String id) { + this.id = id; + return this; + } + + /** + * Unique order identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique order identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public OrderEventWebhookRequest checkoutId(String checkoutId) { + this.checkoutId = checkoutId; + return this; + } + + /** + * Associated checkout ID for reconciliation. + * @return checkoutId + */ + @NotNull + @Schema(name = "checkout_id", description = "Associated checkout ID for reconciliation.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("checkout_id") + public String getCheckoutId() { + return checkoutId; + } + + public void setCheckoutId(String checkoutId) { + this.checkoutId = checkoutId; + } + + public OrderEventWebhookRequest permalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + return this; + } + + /** + * Permalink to access the order on merchant site. + * @return permalinkUrl + */ + @NotNull @Valid + @Schema(name = "permalink_url", description = "Permalink to access the order on merchant site.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("permalink_url") + public URI getPermalinkUrl() { + return permalinkUrl; + } + + public void setPermalinkUrl(URI permalinkUrl) { + this.permalinkUrl = permalinkUrl; + } + + public OrderEventWebhookRequest lineItems(List<@Valid OrderLineItem> lineItems) { + this.lineItems = lineItems; + return this; + } + + public OrderEventWebhookRequest addLineItemsItem(OrderLineItem lineItemsItem) { + if (this.lineItems == null) { + this.lineItems = new ArrayList<>(); + } + this.lineItems.add(lineItemsItem); + return this; + } + + /** + * Immutable line items — source of truth for what was ordered. + * @return lineItems + */ + @NotNull @Valid + @Schema(name = "line_items", description = "Immutable line items — source of truth for what was ordered.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("line_items") + public List<@Valid OrderLineItem> getLineItems() { + return lineItems; + } + + public void setLineItems(List<@Valid OrderLineItem> lineItems) { + this.lineItems = lineItems; + } + + public OrderEventWebhookRequest fulfillment(OrderFulfillment fulfillment) { + this.fulfillment = fulfillment; + return this; + } + + /** + * Get fulfillment + * @return fulfillment + */ + @NotNull @Valid + @Schema(name = "fulfillment", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("fulfillment") + public OrderFulfillment getFulfillment() { + return fulfillment; + } + + public void setFulfillment(OrderFulfillment fulfillment) { + this.fulfillment = fulfillment; + } + + public OrderEventWebhookRequest adjustments(List<@Valid Adjustment> adjustments) { + this.adjustments = adjustments; + return this; + } + + public OrderEventWebhookRequest addAdjustmentsItem(Adjustment adjustmentsItem) { + if (this.adjustments == null) { + this.adjustments = new ArrayList<>(); + } + this.adjustments.add(adjustmentsItem); + return this; + } + + /** + * Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment. + * @return adjustments + */ + @Valid + @Schema(name = "adjustments", description = "Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("adjustments") + public List<@Valid Adjustment> getAdjustments() { + return adjustments; + } + + public void setAdjustments(List<@Valid Adjustment> adjustments) { + this.adjustments = adjustments; + } + + public OrderEventWebhookRequest totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public OrderEventWebhookRequest addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Different totals for the order. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Different totals for the order.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + + public OrderEventWebhookRequest eventId(String eventId) { + this.eventId = eventId; + return this; + } + + /** + * Unique event identifier. + * @return eventId + */ + @NotNull + @Schema(name = "event_id", description = "Unique event identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("event_id") + public String getEventId() { + return eventId; + } + + public void setEventId(String eventId) { + this.eventId = eventId; + } + + public OrderEventWebhookRequest createdTime(OffsetDateTime createdTime) { + this.createdTime = createdTime; + return this; + } + + /** + * Event creation timestamp in RFC 3339 format. + * @return createdTime + */ + @NotNull @Valid + @Schema(name = "created_time", description = "Event creation timestamp in RFC 3339 format.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("created_time") + public OffsetDateTime getCreatedTime() { + return createdTime; + } + + public void setCreatedTime(OffsetDateTime createdTime) { + this.createdTime = createdTime; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public OrderEventWebhookRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderEventWebhookRequest orderEventWebhookRequest = (OrderEventWebhookRequest) o; + return Objects.equals(this.ucp, orderEventWebhookRequest.ucp) && + Objects.equals(this.id, orderEventWebhookRequest.id) && + Objects.equals(this.checkoutId, orderEventWebhookRequest.checkoutId) && + Objects.equals(this.permalinkUrl, orderEventWebhookRequest.permalinkUrl) && + Objects.equals(this.lineItems, orderEventWebhookRequest.lineItems) && + Objects.equals(this.fulfillment, orderEventWebhookRequest.fulfillment) && + Objects.equals(this.adjustments, orderEventWebhookRequest.adjustments) && + Objects.equals(this.totals, orderEventWebhookRequest.totals) && + Objects.equals(this.eventId, orderEventWebhookRequest.eventId) && + Objects.equals(this.createdTime, orderEventWebhookRequest.createdTime) && + Objects.equals(this.additionalProperties, orderEventWebhookRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(ucp, id, checkoutId, permalinkUrl, lineItems, fulfillment, adjustments, totals, eventId, createdTime, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderEventWebhookRequest {\n"); + sb.append(" ucp: ").append(toIndentedString(ucp)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" checkoutId: ").append(toIndentedString(checkoutId)).append("\n"); + sb.append(" permalinkUrl: ").append(toIndentedString(permalinkUrl)).append("\n"); + sb.append(" lineItems: ").append(toIndentedString(lineItems)).append("\n"); + sb.append(" fulfillment: ").append(toIndentedString(fulfillment)).append("\n"); + sb.append(" adjustments: ").append(toIndentedString(adjustments)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + sb.append(" eventId: ").append(toIndentedString(eventId)).append("\n"); + sb.append(" createdTime: ").append(toIndentedString(createdTime)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderEventWebhookRequest instance; + + public Builder() { + this(new OrderEventWebhookRequest()); + } + + protected Builder(OrderEventWebhookRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderEventWebhookRequest value) { + this.instance.setUcp(value.ucp); + this.instance.setId(value.id); + this.instance.setCheckoutId(value.checkoutId); + this.instance.setPermalinkUrl(value.permalinkUrl); + this.instance.setLineItems(value.lineItems); + this.instance.setFulfillment(value.fulfillment); + this.instance.setAdjustments(value.adjustments); + this.instance.setTotals(value.totals); + this.instance.setEventId(value.eventId); + this.instance.setCreatedTime(value.createdTime); + return this; + } + + public OrderEventWebhookRequest.Builder ucp(UCPOrderResponse ucp) { + this.instance.ucp(ucp); + return this; + } + + public OrderEventWebhookRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + public OrderEventWebhookRequest.Builder checkoutId(String checkoutId) { + this.instance.checkoutId(checkoutId); + return this; + } + + public OrderEventWebhookRequest.Builder permalinkUrl(URI permalinkUrl) { + this.instance.permalinkUrl(permalinkUrl); + return this; + } + + public OrderEventWebhookRequest.Builder lineItems(List lineItems) { + this.instance.lineItems(lineItems); + return this; + } + + public OrderEventWebhookRequest.Builder fulfillment(OrderFulfillment fulfillment) { + this.instance.fulfillment(fulfillment); + return this; + } + + public OrderEventWebhookRequest.Builder adjustments(List adjustments) { + this.instance.adjustments(adjustments); + return this; + } + + public OrderEventWebhookRequest.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + public OrderEventWebhookRequest.Builder eventId(String eventId) { + this.instance.eventId(eventId); + return this; + } + + public OrderEventWebhookRequest.Builder createdTime(OffsetDateTime createdTime) { + this.instance.createdTime(createdTime); + return this; + } + + public OrderEventWebhookRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built OrderEventWebhookRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderEventWebhookRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderEventWebhookRequest.Builder builder() { + return new OrderEventWebhookRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderEventWebhookRequest.Builder toBuilder() { + OrderEventWebhookRequest.Builder builder = new OrderEventWebhookRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderFulfillment.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderFulfillment.java new file mode 100644 index 0000000..ada92c0 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderFulfillment.java @@ -0,0 +1,213 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.Expectation; +import com.dev.ucp.service.shopping.model.FulfillmentEvent; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Fulfillment data: buyer expectations and what actually happened. + */ + +@Schema(name = "order_fulfillment", description = "Fulfillment data: buyer expectations and what actually happened.") +@JsonTypeName("order_fulfillment") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderFulfillment { + + @Valid + private List<@Valid Expectation> expectations = new ArrayList<>(); + + @Valid + private List<@Valid FulfillmentEvent> events = new ArrayList<>(); + + public OrderFulfillment expectations(List<@Valid Expectation> expectations) { + this.expectations = expectations; + return this; + } + + public OrderFulfillment addExpectationsItem(Expectation expectationsItem) { + if (this.expectations == null) { + this.expectations = new ArrayList<>(); + } + this.expectations.add(expectationsItem); + return this; + } + + /** + * Buyer-facing groups representing when/how items will be delivered. Can be split, merged, or adjusted post-order. + * @return expectations + */ + @Valid + @Schema(name = "expectations", description = "Buyer-facing groups representing when/how items will be delivered. Can be split, merged, or adjusted post-order.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("expectations") + public List<@Valid Expectation> getExpectations() { + return expectations; + } + + public void setExpectations(List<@Valid Expectation> expectations) { + this.expectations = expectations; + } + + public OrderFulfillment events(List<@Valid FulfillmentEvent> events) { + this.events = events; + return this; + } + + public OrderFulfillment addEventsItem(FulfillmentEvent eventsItem) { + if (this.events == null) { + this.events = new ArrayList<>(); + } + this.events.add(eventsItem); + return this; + } + + /** + * Append-only event log of actual shipments. Each event references line items by ID. + * @return events + */ + @Valid + @Schema(name = "events", description = "Append-only event log of actual shipments. Each event references line items by ID.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("events") + public List<@Valid FulfillmentEvent> getEvents() { + return events; + } + + public void setEvents(List<@Valid FulfillmentEvent> events) { + this.events = events; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderFulfillment orderFulfillment = (OrderFulfillment) o; + return Objects.equals(this.expectations, orderFulfillment.expectations) && + Objects.equals(this.events, orderFulfillment.events); + } + + @Override + public int hashCode() { + return Objects.hash(expectations, events); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderFulfillment {\n"); + sb.append(" expectations: ").append(toIndentedString(expectations)).append("\n"); + sb.append(" events: ").append(toIndentedString(events)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderFulfillment instance; + + public Builder() { + this(new OrderFulfillment()); + } + + protected Builder(OrderFulfillment instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderFulfillment value) { + this.instance.setExpectations(value.expectations); + this.instance.setEvents(value.events); + return this; + } + + public OrderFulfillment.Builder expectations(List expectations) { + this.instance.expectations(expectations); + return this; + } + + public OrderFulfillment.Builder events(List events) { + this.instance.events(events); + return this; + } + + /** + * returns a built OrderFulfillment instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderFulfillment build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderFulfillment.Builder builder() { + return new OrderFulfillment.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderFulfillment.Builder toBuilder() { + OrderFulfillment.Builder builder = new OrderFulfillment.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItem.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItem.java new file mode 100644 index 0000000..5995e4d --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItem.java @@ -0,0 +1,377 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.ItemResponse; +import com.dev.ucp.service.shopping.model.OrderLineItemQuantity; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * OrderLineItem + */ + +@JsonTypeName("Order_Line_Item") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderLineItem { + + private String id; + + private ItemResponse item; + + private OrderLineItemQuantity quantity; + + @Valid + private List<@Valid TotalResponse> totals = new ArrayList<>(); + + /** + * Derived status: fulfilled if quantity.fulfilled == quantity.total, partial if quantity.fulfilled > 0, otherwise processing. + */ + public enum StatusEnum { + PROCESSING("processing"), + + PARTIAL("partial"), + + FULFILLED("fulfilled"); + + private final String value; + + StatusEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static StatusEnum fromValue(String value) { + for (StatusEnum b : StatusEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private StatusEnum status; + + private @Nullable String parentId; + + public OrderLineItem() { + super(); + } + + /** + * Constructor with only required parameters + */ + public OrderLineItem(String id, ItemResponse item, OrderLineItemQuantity quantity, List<@Valid TotalResponse> totals, StatusEnum status) { + this.id = id; + this.item = item; + this.quantity = quantity; + this.totals = totals; + this.status = status; + } + + public OrderLineItem id(String id) { + this.id = id; + return this; + } + + /** + * Line item identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Line item identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public OrderLineItem item(ItemResponse item) { + this.item = item; + return this; + } + + /** + * Get item + * @return item + */ + @NotNull @Valid + @Schema(name = "item", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("item") + public ItemResponse getItem() { + return item; + } + + public void setItem(ItemResponse item) { + this.item = item; + } + + public OrderLineItem quantity(OrderLineItemQuantity quantity) { + this.quantity = quantity; + return this; + } + + /** + * Get quantity + * @return quantity + */ + @NotNull @Valid + @Schema(name = "quantity", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("quantity") + public OrderLineItemQuantity getQuantity() { + return quantity; + } + + public void setQuantity(OrderLineItemQuantity quantity) { + this.quantity = quantity; + } + + public OrderLineItem totals(List<@Valid TotalResponse> totals) { + this.totals = totals; + return this; + } + + public OrderLineItem addTotalsItem(TotalResponse totalsItem) { + if (this.totals == null) { + this.totals = new ArrayList<>(); + } + this.totals.add(totalsItem); + return this; + } + + /** + * Line item totals breakdown. + * @return totals + */ + @NotNull @Valid + @Schema(name = "totals", description = "Line item totals breakdown.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("totals") + public List<@Valid TotalResponse> getTotals() { + return totals; + } + + public void setTotals(List<@Valid TotalResponse> totals) { + this.totals = totals; + } + + public OrderLineItem status(StatusEnum status) { + this.status = status; + return this; + } + + /** + * Derived status: fulfilled if quantity.fulfilled == quantity.total, partial if quantity.fulfilled > 0, otherwise processing. + * @return status + */ + @NotNull + @Schema(name = "status", description = "Derived status: fulfilled if quantity.fulfilled == quantity.total, partial if quantity.fulfilled > 0, otherwise processing.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("status") + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public OrderLineItem parentId(@Nullable String parentId) { + this.parentId = parentId; + return this; + } + + /** + * Parent line item identifier for any nested structures. + * @return parentId + */ + + @Schema(name = "parent_id", description = "Parent line item identifier for any nested structures.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("parent_id") + public @Nullable String getParentId() { + return parentId; + } + + public void setParentId(@Nullable String parentId) { + this.parentId = parentId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderLineItem orderLineItem = (OrderLineItem) o; + return Objects.equals(this.id, orderLineItem.id) && + Objects.equals(this.item, orderLineItem.item) && + Objects.equals(this.quantity, orderLineItem.quantity) && + Objects.equals(this.totals, orderLineItem.totals) && + Objects.equals(this.status, orderLineItem.status) && + Objects.equals(this.parentId, orderLineItem.parentId); + } + + @Override + public int hashCode() { + return Objects.hash(id, item, quantity, totals, status, parentId); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderLineItem {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" item: ").append(toIndentedString(item)).append("\n"); + sb.append(" quantity: ").append(toIndentedString(quantity)).append("\n"); + sb.append(" totals: ").append(toIndentedString(totals)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" parentId: ").append(toIndentedString(parentId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderLineItem instance; + + public Builder() { + this(new OrderLineItem()); + } + + protected Builder(OrderLineItem instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderLineItem value) { + this.instance.setId(value.id); + this.instance.setItem(value.item); + this.instance.setQuantity(value.quantity); + this.instance.setTotals(value.totals); + this.instance.setStatus(value.status); + this.instance.setParentId(value.parentId); + return this; + } + + public OrderLineItem.Builder id(String id) { + this.instance.id(id); + return this; + } + + public OrderLineItem.Builder item(ItemResponse item) { + this.instance.item(item); + return this; + } + + public OrderLineItem.Builder quantity(OrderLineItemQuantity quantity) { + this.instance.quantity(quantity); + return this; + } + + public OrderLineItem.Builder totals(List totals) { + this.instance.totals(totals); + return this; + } + + public OrderLineItem.Builder status(StatusEnum status) { + this.instance.status(status); + return this; + } + + public OrderLineItem.Builder parentId(String parentId) { + this.instance.parentId(parentId); + return this; + } + + /** + * returns a built OrderLineItem instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderLineItem build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderLineItem.Builder builder() { + return new OrderLineItem.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderLineItem.Builder toBuilder() { + OrderLineItem.Builder builder = new OrderLineItem.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItemQuantity.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItemQuantity.java new file mode 100644 index 0000000..b7163e2 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/OrderLineItemQuantity.java @@ -0,0 +1,204 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Quantity tracking. Both total and fulfilled are derived from events. + */ + +@Schema(name = "Order_Line_Item_quantity", description = "Quantity tracking. Both total and fulfilled are derived from events.") +@JsonTypeName("Order_Line_Item_quantity") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class OrderLineItemQuantity { + + private Integer total; + + private Integer fulfilled; + + public OrderLineItemQuantity() { + super(); + } + + /** + * Constructor with only required parameters + */ + public OrderLineItemQuantity(Integer total, Integer fulfilled) { + this.total = total; + this.fulfilled = fulfilled; + } + + public OrderLineItemQuantity total(Integer total) { + this.total = total; + return this; + } + + /** + * Current total quantity. + * minimum: 0 + * @return total + */ + @NotNull @Min(value = 0) + @Schema(name = "total", description = "Current total quantity.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("total") + public Integer getTotal() { + return total; + } + + public void setTotal(Integer total) { + this.total = total; + } + + public OrderLineItemQuantity fulfilled(Integer fulfilled) { + this.fulfilled = fulfilled; + return this; + } + + /** + * Quantity fulfilled (sum from fulfillment events). + * minimum: 0 + * @return fulfilled + */ + @NotNull @Min(value = 0) + @Schema(name = "fulfilled", description = "Quantity fulfilled (sum from fulfillment events).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("fulfilled") + public Integer getFulfilled() { + return fulfilled; + } + + public void setFulfilled(Integer fulfilled) { + this.fulfilled = fulfilled; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderLineItemQuantity orderLineItemQuantity = (OrderLineItemQuantity) o; + return Objects.equals(this.total, orderLineItemQuantity.total) && + Objects.equals(this.fulfilled, orderLineItemQuantity.fulfilled); + } + + @Override + public int hashCode() { + return Objects.hash(total, fulfilled); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class OrderLineItemQuantity {\n"); + sb.append(" total: ").append(toIndentedString(total)).append("\n"); + sb.append(" fulfilled: ").append(toIndentedString(fulfilled)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private OrderLineItemQuantity instance; + + public Builder() { + this(new OrderLineItemQuantity()); + } + + protected Builder(OrderLineItemQuantity instance) { + this.instance = instance; + } + + protected Builder copyOf(OrderLineItemQuantity value) { + this.instance.setTotal(value.total); + this.instance.setFulfilled(value.fulfilled); + return this; + } + + public OrderLineItemQuantity.Builder total(Integer total) { + this.instance.total(total); + return this; + } + + public OrderLineItemQuantity.Builder fulfilled(Integer fulfilled) { + this.instance.fulfilled(fulfilled); + return this; + } + + /** + * returns a built OrderLineItemQuantity instance. + * + * The builder is not reusable (NullPointerException) + */ + public OrderLineItemQuantity build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static OrderLineItemQuantity.Builder builder() { + return new OrderLineItemQuantity.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public OrderLineItemQuantity.Builder toBuilder() { + OrderLineItemQuantity.Builder builder = new OrderLineItemQuantity.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCreateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCreateRequest.java new file mode 100644 index 0000000..c31890e --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCreateRequest.java @@ -0,0 +1,203 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Payment configuration containing handlers. + */ + +@Schema(name = "Payment_Create_Request", description = "Payment configuration containing handlers.") +@JsonTypeName("Payment_Create_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PaymentCreateRequest { + + private @Nullable String selectedInstrumentId; + + @Valid + private List instruments = new ArrayList<>(); + + public PaymentCreateRequest selectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + return this; + } + + /** + * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state. + * @return selectedInstrumentId + */ + + @Schema(name = "selected_instrument_id", description = "The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_instrument_id") + public @Nullable String getSelectedInstrumentId() { + return selectedInstrumentId; + } + + public void setSelectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + } + + public PaymentCreateRequest instruments(List instruments) { + this.instruments = instruments; + return this; + } + + public PaymentCreateRequest addInstrumentsItem(CardPaymentInstrument instrumentsItem) { + if (this.instruments == null) { + this.instruments = new ArrayList<>(); + } + this.instruments.add(instrumentsItem); + return this; + } + + /** + * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields. + * @return instruments + */ + @Valid + @Schema(name = "instruments", description = "The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("instruments") + public List getInstruments() { + return instruments; + } + + public void setInstruments(List instruments) { + this.instruments = instruments; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PaymentCreateRequest paymentCreateRequest = (PaymentCreateRequest) o; + return Objects.equals(this.selectedInstrumentId, paymentCreateRequest.selectedInstrumentId) && + Objects.equals(this.instruments, paymentCreateRequest.instruments); + } + + @Override + public int hashCode() { + return Objects.hash(selectedInstrumentId, instruments); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PaymentCreateRequest {\n"); + sb.append(" selectedInstrumentId: ").append(toIndentedString(selectedInstrumentId)).append("\n"); + sb.append(" instruments: ").append(toIndentedString(instruments)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PaymentCreateRequest instance; + + public Builder() { + this(new PaymentCreateRequest()); + } + + protected Builder(PaymentCreateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(PaymentCreateRequest value) { + this.instance.setSelectedInstrumentId(value.selectedInstrumentId); + this.instance.setInstruments(value.instruments); + return this; + } + + public PaymentCreateRequest.Builder selectedInstrumentId(String selectedInstrumentId) { + this.instance.selectedInstrumentId(selectedInstrumentId); + return this; + } + + public PaymentCreateRequest.Builder instruments(List instruments) { + this.instance.instruments(instruments); + return this; + } + + /** + * returns a built PaymentCreateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public PaymentCreateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PaymentCreateRequest.Builder builder() { + return new PaymentCreateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PaymentCreateRequest.Builder toBuilder() { + PaymentCreateRequest.Builder builder = new PaymentCreateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCredential.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCredential.java new file mode 100644 index 0000000..1bebcbe --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentCredential.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardCredential; +import com.dev.ucp.service.shopping.model.TokenCredentialResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public interface PaymentCredential { +} diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentData.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentData.java new file mode 100644 index 0000000..7cade88 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentData.java @@ -0,0 +1,172 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * The data that will used to submit payment to the merchant. + */ + +@Schema(name = "payment_data", description = "The data that will used to submit payment to the merchant.") +@JsonTypeName("payment_data") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PaymentData { + + private CardPaymentInstrument paymentData; + + public PaymentData() { + super(); + } + + /** + * Constructor with only required parameters + */ + public PaymentData(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + } + + public PaymentData paymentData(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + return this; + } + + /** + * Get paymentData + * @return paymentData + */ + @NotNull @Valid + @Schema(name = "payment_data", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("payment_data") + public CardPaymentInstrument getPaymentData() { + return paymentData; + } + + public void setPaymentData(CardPaymentInstrument paymentData) { + this.paymentData = paymentData; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PaymentData paymentData = (PaymentData) o; + return Objects.equals(this.paymentData, paymentData.paymentData); + } + + @Override + public int hashCode() { + return Objects.hash(paymentData); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PaymentData {\n"); + sb.append(" paymentData: ").append(toIndentedString(paymentData)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PaymentData instance; + + public Builder() { + this(new PaymentData()); + } + + protected Builder(PaymentData instance) { + this.instance = instance; + } + + protected Builder copyOf(PaymentData value) { + this.instance.setPaymentData(value.paymentData); + return this; + } + + public PaymentData.Builder paymentData(CardPaymentInstrument paymentData) { + this.instance.paymentData(paymentData); + return this; + } + + /** + * returns a built PaymentData instance. + * + * The builder is not reusable (NullPointerException) + */ + public PaymentData build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PaymentData.Builder builder() { + return new PaymentData.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PaymentData.Builder toBuilder() { + PaymentData.Builder builder = new PaymentData.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentHandlerResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentHandlerResponse.java new file mode 100644 index 0000000..c3d595e --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentHandlerResponse.java @@ -0,0 +1,381 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * PaymentHandlerResponse + */ + +@JsonTypeName("Payment_Handler_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PaymentHandlerResponse { + + private String id; + + private String name; + + private String version; + + private URI spec; + + private URI configSchema; + + @Valid + private List instrumentSchemas = new ArrayList<>(); + + @Valid + private Map config = new HashMap<>(); + + public PaymentHandlerResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public PaymentHandlerResponse(String id, String name, String version, URI spec, URI configSchema, List instrumentSchemas, Map config) { + this.id = id; + this.name = name; + this.version = version; + this.spec = spec; + this.configSchema = configSchema; + this.instrumentSchemas = instrumentSchemas; + this.config = config; + } + + public PaymentHandlerResponse id(String id) { + this.id = id; + return this; + } + + /** + * The unique identifier for this handler instance within the payment.handlers. Used by payment instruments to reference which handler produced them. + * @return id + */ + @NotNull + @Schema(name = "id", description = "The unique identifier for this handler instance within the payment.handlers. Used by payment instruments to reference which handler produced them.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public PaymentHandlerResponse name(String name) { + this.name = name; + return this; + } + + /** + * The specification name using reverse-DNS format. For example, dev.ucp.delegate_payment. + * @return name + */ + @NotNull + @Schema(name = "name", description = "The specification name using reverse-DNS format. For example, dev.ucp.delegate_payment.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public PaymentHandlerResponse version(String version) { + this.version = version; + return this; + } + + /** + * Handler version in YYYY-MM-DD format. + * @return version + */ + @NotNull @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$") + @Schema(name = "version", description = "Handler version in YYYY-MM-DD format.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("version") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public PaymentHandlerResponse spec(URI spec) { + this.spec = spec; + return this; + } + + /** + * A URI pointing to the technical specification or schema that defines how this handler operates. + * @return spec + */ + @NotNull @Valid + @Schema(name = "spec", description = "A URI pointing to the technical specification or schema that defines how this handler operates.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("spec") + public URI getSpec() { + return spec; + } + + public void setSpec(URI spec) { + this.spec = spec; + } + + public PaymentHandlerResponse configSchema(URI configSchema) { + this.configSchema = configSchema; + return this; + } + + /** + * A URI pointing to a JSON Schema used to validate the structure of the config object. + * @return configSchema + */ + @NotNull @Valid + @Schema(name = "config_schema", description = "A URI pointing to a JSON Schema used to validate the structure of the config object.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("config_schema") + public URI getConfigSchema() { + return configSchema; + } + + public void setConfigSchema(URI configSchema) { + this.configSchema = configSchema; + } + + public PaymentHandlerResponse instrumentSchemas(List instrumentSchemas) { + this.instrumentSchemas = instrumentSchemas; + return this; + } + + public PaymentHandlerResponse addInstrumentSchemasItem(URI instrumentSchemasItem) { + if (this.instrumentSchemas == null) { + this.instrumentSchemas = new ArrayList<>(); + } + this.instrumentSchemas.add(instrumentSchemasItem); + return this; + } + + /** + * Get instrumentSchemas + * @return instrumentSchemas + */ + @NotNull @Valid + @Schema(name = "instrument_schemas", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("instrument_schemas") + public List getInstrumentSchemas() { + return instrumentSchemas; + } + + public void setInstrumentSchemas(List instrumentSchemas) { + this.instrumentSchemas = instrumentSchemas; + } + + public PaymentHandlerResponse config(Map config) { + this.config = config; + return this; + } + + public PaymentHandlerResponse putConfigItem(String key, JsonNode configItem) { + if (this.config == null) { + this.config = new HashMap<>(); + } + this.config.put(key, configItem); + return this; + } + + /** + * A dictionary containing provider-specific configuration details, such as merchant IDs, supported networks, or gateway credentials. + * @return config + */ + @NotNull @Valid + @Schema(name = "config", description = "A dictionary containing provider-specific configuration details, such as merchant IDs, supported networks, or gateway credentials.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("config") + public Map getConfig() { + return config; + } + + public void setConfig(Map config) { + this.config = config; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PaymentHandlerResponse paymentHandlerResponse = (PaymentHandlerResponse) o; + return Objects.equals(this.id, paymentHandlerResponse.id) && + Objects.equals(this.name, paymentHandlerResponse.name) && + Objects.equals(this.version, paymentHandlerResponse.version) && + Objects.equals(this.spec, paymentHandlerResponse.spec) && + Objects.equals(this.configSchema, paymentHandlerResponse.configSchema) && + Objects.equals(this.instrumentSchemas, paymentHandlerResponse.instrumentSchemas) && + Objects.equals(this.config, paymentHandlerResponse.config); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, version, spec, configSchema, instrumentSchemas, config); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PaymentHandlerResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" spec: ").append(toIndentedString(spec)).append("\n"); + sb.append(" configSchema: ").append(toIndentedString(configSchema)).append("\n"); + sb.append(" instrumentSchemas: ").append(toIndentedString(instrumentSchemas)).append("\n"); + sb.append(" config: ").append(toIndentedString(config)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PaymentHandlerResponse instance; + + public Builder() { + this(new PaymentHandlerResponse()); + } + + protected Builder(PaymentHandlerResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(PaymentHandlerResponse value) { + this.instance.setId(value.id); + this.instance.setName(value.name); + this.instance.setVersion(value.version); + this.instance.setSpec(value.spec); + this.instance.setConfigSchema(value.configSchema); + this.instance.setInstrumentSchemas(value.instrumentSchemas); + this.instance.setConfig(value.config); + return this; + } + + public PaymentHandlerResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public PaymentHandlerResponse.Builder name(String name) { + this.instance.name(name); + return this; + } + + public PaymentHandlerResponse.Builder version(String version) { + this.instance.version(version); + return this; + } + + public PaymentHandlerResponse.Builder spec(URI spec) { + this.instance.spec(spec); + return this; + } + + public PaymentHandlerResponse.Builder configSchema(URI configSchema) { + this.instance.configSchema(configSchema); + return this; + } + + public PaymentHandlerResponse.Builder instrumentSchemas(List instrumentSchemas) { + this.instance.instrumentSchemas(instrumentSchemas); + return this; + } + + public PaymentHandlerResponse.Builder config(Map config) { + this.instance.config(config); + return this; + } + + /** + * returns a built PaymentHandlerResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public PaymentHandlerResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PaymentHandlerResponse.Builder builder() { + return new PaymentHandlerResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PaymentHandlerResponse.Builder toBuilder() { + PaymentHandlerResponse.Builder builder = new PaymentHandlerResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentResponse.java new file mode 100644 index 0000000..2c05183 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentResponse.java @@ -0,0 +1,254 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.PaymentHandlerResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Payment configuration containing handlers. + */ + +@Schema(name = "Payment_Response", description = "Payment configuration containing handlers.") +@JsonTypeName("Payment_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PaymentResponse { + + @Valid + private List<@Valid PaymentHandlerResponse> handlers = new ArrayList<>(); + + private @Nullable String selectedInstrumentId; + + @Valid + private List instruments = new ArrayList<>(); + + public PaymentResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public PaymentResponse(List<@Valid PaymentHandlerResponse> handlers) { + this.handlers = handlers; + } + + public PaymentResponse handlers(List<@Valid PaymentHandlerResponse> handlers) { + this.handlers = handlers; + return this; + } + + public PaymentResponse addHandlersItem(PaymentHandlerResponse handlersItem) { + if (this.handlers == null) { + this.handlers = new ArrayList<>(); + } + this.handlers.add(handlersItem); + return this; + } + + /** + * Processing configurations that define how payment instruments can be collected. Each handler specifies a tokenization or payment collection strategy. + * @return handlers + */ + @NotNull @Valid + @Schema(name = "handlers", description = "Processing configurations that define how payment instruments can be collected. Each handler specifies a tokenization or payment collection strategy.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("handlers") + public List<@Valid PaymentHandlerResponse> getHandlers() { + return handlers; + } + + public void setHandlers(List<@Valid PaymentHandlerResponse> handlers) { + this.handlers = handlers; + } + + public PaymentResponse selectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + return this; + } + + /** + * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state. + * @return selectedInstrumentId + */ + + @Schema(name = "selected_instrument_id", description = "The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_instrument_id") + public @Nullable String getSelectedInstrumentId() { + return selectedInstrumentId; + } + + public void setSelectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + } + + public PaymentResponse instruments(List instruments) { + this.instruments = instruments; + return this; + } + + public PaymentResponse addInstrumentsItem(CardPaymentInstrument instrumentsItem) { + if (this.instruments == null) { + this.instruments = new ArrayList<>(); + } + this.instruments.add(instrumentsItem); + return this; + } + + /** + * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields. + * @return instruments + */ + @Valid + @Schema(name = "instruments", description = "The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("instruments") + public List getInstruments() { + return instruments; + } + + public void setInstruments(List instruments) { + this.instruments = instruments; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PaymentResponse paymentResponse = (PaymentResponse) o; + return Objects.equals(this.handlers, paymentResponse.handlers) && + Objects.equals(this.selectedInstrumentId, paymentResponse.selectedInstrumentId) && + Objects.equals(this.instruments, paymentResponse.instruments); + } + + @Override + public int hashCode() { + return Objects.hash(handlers, selectedInstrumentId, instruments); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PaymentResponse {\n"); + sb.append(" handlers: ").append(toIndentedString(handlers)).append("\n"); + sb.append(" selectedInstrumentId: ").append(toIndentedString(selectedInstrumentId)).append("\n"); + sb.append(" instruments: ").append(toIndentedString(instruments)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PaymentResponse instance; + + public Builder() { + this(new PaymentResponse()); + } + + protected Builder(PaymentResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(PaymentResponse value) { + this.instance.setHandlers(value.handlers); + this.instance.setSelectedInstrumentId(value.selectedInstrumentId); + this.instance.setInstruments(value.instruments); + return this; + } + + public PaymentResponse.Builder handlers(List handlers) { + this.instance.handlers(handlers); + return this; + } + + public PaymentResponse.Builder selectedInstrumentId(String selectedInstrumentId) { + this.instance.selectedInstrumentId(selectedInstrumentId); + return this; + } + + public PaymentResponse.Builder instruments(List instruments) { + this.instance.instruments(instruments); + return this; + } + + /** + * returns a built PaymentResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public PaymentResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PaymentResponse.Builder builder() { + return new PaymentResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PaymentResponse.Builder toBuilder() { + PaymentResponse.Builder builder = new PaymentResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentUpdateRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentUpdateRequest.java new file mode 100644 index 0000000..cef0c46 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PaymentUpdateRequest.java @@ -0,0 +1,203 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Payment configuration containing handlers. + */ + +@Schema(name = "Payment_Update_Request", description = "Payment configuration containing handlers.") +@JsonTypeName("Payment_Update_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PaymentUpdateRequest { + + private @Nullable String selectedInstrumentId; + + @Valid + private List instruments = new ArrayList<>(); + + public PaymentUpdateRequest selectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + return this; + } + + /** + * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state. + * @return selectedInstrumentId + */ + + @Schema(name = "selected_instrument_id", description = "The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("selected_instrument_id") + public @Nullable String getSelectedInstrumentId() { + return selectedInstrumentId; + } + + public void setSelectedInstrumentId(@Nullable String selectedInstrumentId) { + this.selectedInstrumentId = selectedInstrumentId; + } + + public PaymentUpdateRequest instruments(List instruments) { + this.instruments = instruments; + return this; + } + + public PaymentUpdateRequest addInstrumentsItem(CardPaymentInstrument instrumentsItem) { + if (this.instruments == null) { + this.instruments = new ArrayList<>(); + } + this.instruments.add(instrumentsItem); + return this; + } + + /** + * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields. + * @return instruments + */ + @Valid + @Schema(name = "instruments", description = "The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("instruments") + public List getInstruments() { + return instruments; + } + + public void setInstruments(List instruments) { + this.instruments = instruments; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PaymentUpdateRequest paymentUpdateRequest = (PaymentUpdateRequest) o; + return Objects.equals(this.selectedInstrumentId, paymentUpdateRequest.selectedInstrumentId) && + Objects.equals(this.instruments, paymentUpdateRequest.instruments); + } + + @Override + public int hashCode() { + return Objects.hash(selectedInstrumentId, instruments); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PaymentUpdateRequest {\n"); + sb.append(" selectedInstrumentId: ").append(toIndentedString(selectedInstrumentId)).append("\n"); + sb.append(" instruments: ").append(toIndentedString(instruments)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PaymentUpdateRequest instance; + + public Builder() { + this(new PaymentUpdateRequest()); + } + + protected Builder(PaymentUpdateRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(PaymentUpdateRequest value) { + this.instance.setSelectedInstrumentId(value.selectedInstrumentId); + this.instance.setInstruments(value.instruments); + return this; + } + + public PaymentUpdateRequest.Builder selectedInstrumentId(String selectedInstrumentId) { + this.instance.selectedInstrumentId(selectedInstrumentId); + return this; + } + + public PaymentUpdateRequest.Builder instruments(List instruments) { + this.instance.instruments(instruments); + return this; + } + + /** + * returns a built PaymentUpdateRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public PaymentUpdateRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PaymentUpdateRequest.Builder builder() { + return new PaymentUpdateRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PaymentUpdateRequest.Builder toBuilder() { + PaymentUpdateRequest.Builder builder = new PaymentUpdateRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PostalAddress.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PostalAddress.java new file mode 100644 index 0000000..9dadc99 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/PostalAddress.java @@ -0,0 +1,430 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Physical address of the location. + */ + +@Schema(name = "Postal_Address", description = "Physical address of the location.") +@JsonTypeName("Postal_Address") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class PostalAddress { + + private @Nullable String extendedAddress; + + private @Nullable String streetAddress; + + private @Nullable String addressLocality; + + private @Nullable String addressRegion; + + private @Nullable String addressCountry; + + private @Nullable String postalCode; + + private @Nullable String firstName; + + private @Nullable String lastName; + + private @Nullable String fullName; + + private @Nullable String phoneNumber; + + public PostalAddress extendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + return this; + } + + /** + * An address extension such as an apartment number, C/O or alternative name. + * @return extendedAddress + */ + + @Schema(name = "extended_address", description = "An address extension such as an apartment number, C/O or alternative name.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("extended_address") + public @Nullable String getExtendedAddress() { + return extendedAddress; + } + + public void setExtendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + } + + public PostalAddress streetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + return this; + } + + /** + * The street address. + * @return streetAddress + */ + + @Schema(name = "street_address", description = "The street address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("street_address") + public @Nullable String getStreetAddress() { + return streetAddress; + } + + public void setStreetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + } + + public PostalAddress addressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + return this; + } + + /** + * The locality in which the street address is, and which is in the region. For example, Mountain View. + * @return addressLocality + */ + + @Schema(name = "address_locality", description = "The locality in which the street address is, and which is in the region. For example, Mountain View.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_locality") + public @Nullable String getAddressLocality() { + return addressLocality; + } + + public void setAddressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + } + + public PostalAddress addressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + return this; + } + + /** + * The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. + * @return addressRegion + */ + + @Schema(name = "address_region", description = "The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_region") + public @Nullable String getAddressRegion() { + return addressRegion; + } + + public void setAddressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + } + + public PostalAddress addressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + return this; + } + + /** + * The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used. + * @return addressCountry + */ + + @Schema(name = "address_country", description = "The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_country") + public @Nullable String getAddressCountry() { + return addressCountry; + } + + public void setAddressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + } + + public PostalAddress postalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + return this; + } + + /** + * The postal code. For example, 94043. + * @return postalCode + */ + + @Schema(name = "postal_code", description = "The postal code. For example, 94043.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("postal_code") + public @Nullable String getPostalCode() { + return postalCode; + } + + public void setPostalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + } + + public PostalAddress firstName(@Nullable String firstName) { + this.firstName = firstName; + return this; + } + + /** + * Optional. First name of the contact associated with the address. + * @return firstName + */ + + @Schema(name = "first_name", description = "Optional. First name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("first_name") + public @Nullable String getFirstName() { + return firstName; + } + + public void setFirstName(@Nullable String firstName) { + this.firstName = firstName; + } + + public PostalAddress lastName(@Nullable String lastName) { + this.lastName = lastName; + return this; + } + + /** + * Optional. Last name of the contact associated with the address. + * @return lastName + */ + + @Schema(name = "last_name", description = "Optional. Last name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("last_name") + public @Nullable String getLastName() { + return lastName; + } + + public void setLastName(@Nullable String lastName) { + this.lastName = lastName; + } + + public PostalAddress fullName(@Nullable String fullName) { + this.fullName = fullName; + return this; + } + + /** + * Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence). + * @return fullName + */ + + @Schema(name = "full_name", description = "Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("full_name") + public @Nullable String getFullName() { + return fullName; + } + + public void setFullName(@Nullable String fullName) { + this.fullName = fullName; + } + + public PostalAddress phoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + /** + * Optional. Phone number of the contact associated with the address. + * @return phoneNumber + */ + + @Schema(name = "phone_number", description = "Optional. Phone number of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("phone_number") + public @Nullable String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PostalAddress postalAddress = (PostalAddress) o; + return Objects.equals(this.extendedAddress, postalAddress.extendedAddress) && + Objects.equals(this.streetAddress, postalAddress.streetAddress) && + Objects.equals(this.addressLocality, postalAddress.addressLocality) && + Objects.equals(this.addressRegion, postalAddress.addressRegion) && + Objects.equals(this.addressCountry, postalAddress.addressCountry) && + Objects.equals(this.postalCode, postalAddress.postalCode) && + Objects.equals(this.firstName, postalAddress.firstName) && + Objects.equals(this.lastName, postalAddress.lastName) && + Objects.equals(this.fullName, postalAddress.fullName) && + Objects.equals(this.phoneNumber, postalAddress.phoneNumber); + } + + @Override + public int hashCode() { + return Objects.hash(extendedAddress, streetAddress, addressLocality, addressRegion, addressCountry, postalCode, firstName, lastName, fullName, phoneNumber); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PostalAddress {\n"); + sb.append(" extendedAddress: ").append(toIndentedString(extendedAddress)).append("\n"); + sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); + sb.append(" addressLocality: ").append(toIndentedString(addressLocality)).append("\n"); + sb.append(" addressRegion: ").append(toIndentedString(addressRegion)).append("\n"); + sb.append(" addressCountry: ").append(toIndentedString(addressCountry)).append("\n"); + sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); + sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); + sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); + sb.append(" fullName: ").append(toIndentedString(fullName)).append("\n"); + sb.append(" phoneNumber: ").append(toIndentedString(phoneNumber)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private PostalAddress instance; + + public Builder() { + this(new PostalAddress()); + } + + protected Builder(PostalAddress instance) { + this.instance = instance; + } + + protected Builder copyOf(PostalAddress value) { + this.instance.setExtendedAddress(value.extendedAddress); + this.instance.setStreetAddress(value.streetAddress); + this.instance.setAddressLocality(value.addressLocality); + this.instance.setAddressRegion(value.addressRegion); + this.instance.setAddressCountry(value.addressCountry); + this.instance.setPostalCode(value.postalCode); + this.instance.setFirstName(value.firstName); + this.instance.setLastName(value.lastName); + this.instance.setFullName(value.fullName); + this.instance.setPhoneNumber(value.phoneNumber); + return this; + } + + public PostalAddress.Builder extendedAddress(String extendedAddress) { + this.instance.extendedAddress(extendedAddress); + return this; + } + + public PostalAddress.Builder streetAddress(String streetAddress) { + this.instance.streetAddress(streetAddress); + return this; + } + + public PostalAddress.Builder addressLocality(String addressLocality) { + this.instance.addressLocality(addressLocality); + return this; + } + + public PostalAddress.Builder addressRegion(String addressRegion) { + this.instance.addressRegion(addressRegion); + return this; + } + + public PostalAddress.Builder addressCountry(String addressCountry) { + this.instance.addressCountry(addressCountry); + return this; + } + + public PostalAddress.Builder postalCode(String postalCode) { + this.instance.postalCode(postalCode); + return this; + } + + public PostalAddress.Builder firstName(String firstName) { + this.instance.firstName(firstName); + return this; + } + + public PostalAddress.Builder lastName(String lastName) { + this.instance.lastName(lastName); + return this; + } + + public PostalAddress.Builder fullName(String fullName) { + this.instance.fullName(fullName); + return this; + } + + public PostalAddress.Builder phoneNumber(String phoneNumber) { + this.instance.phoneNumber(phoneNumber); + return this; + } + + /** + * returns a built PostalAddress instance. + * + * The builder is not reusable (NullPointerException) + */ + public PostalAddress build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static PostalAddress.Builder builder() { + return new PostalAddress.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public PostalAddress.Builder toBuilder() { + PostalAddress.Builder builder = new PostalAddress.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationRequest.java new file mode 100644 index 0000000..5faec43 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationRequest.java @@ -0,0 +1,252 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A pickup location (retail store, locker, etc.). + */ + +@Schema(name = "Retail_Location_Request", description = "A pickup location (retail store, locker, etc.).") +@JsonTypeName("Retail_Location_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class RetailLocationRequest implements FulfillmentDestinationRequest { + + private String name; + + private @Nullable PostalAddress address; + + public RetailLocationRequest() { + super(); + } + + /** + * Constructor with only required parameters + */ + public RetailLocationRequest(String name) { + this.name = name; + } + + public RetailLocationRequest name(String name) { + this.name = name; + return this; + } + + /** + * Location name (e.g., store name). + * @return name + */ + @NotNull + @Schema(name = "name", description = "Location name (e.g., store name).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RetailLocationRequest address(@Nullable PostalAddress address) { + this.address = address; + return this; + } + + /** + * Get address + * @return address + */ + @Valid + @Schema(name = "address", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address") + public @Nullable PostalAddress getAddress() { + return address; + } + + public void setAddress(@Nullable PostalAddress address) { + this.address = address; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public RetailLocationRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RetailLocationRequest retailLocationRequest = (RetailLocationRequest) o; + return Objects.equals(this.name, retailLocationRequest.name) && + Objects.equals(this.address, retailLocationRequest.address) && + Objects.equals(this.additionalProperties, retailLocationRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(name, address, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class RetailLocationRequest {\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" address: ").append(toIndentedString(address)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private RetailLocationRequest instance; + + public Builder() { + this(new RetailLocationRequest()); + } + + protected Builder(RetailLocationRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(RetailLocationRequest value) { + this.instance.setName(value.name); + this.instance.setAddress(value.address); + return this; + } + + public RetailLocationRequest.Builder name(String name) { + this.instance.name(name); + return this; + } + + public RetailLocationRequest.Builder address(PostalAddress address) { + this.instance.address(address); + return this; + } + + public RetailLocationRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built RetailLocationRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public RetailLocationRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static RetailLocationRequest.Builder builder() { + return new RetailLocationRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public RetailLocationRequest.Builder toBuilder() { + RetailLocationRequest.Builder builder = new RetailLocationRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationResponse.java new file mode 100644 index 0000000..7dd7614 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/RetailLocationResponse.java @@ -0,0 +1,283 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * A pickup location (retail store, locker, etc.). + */ + +@Schema(name = "Retail_Location_Response", description = "A pickup location (retail store, locker, etc.).") +@JsonTypeName("Retail_Location_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class RetailLocationResponse implements FulfillmentDestinationResponse { + + private String id; + + private String name; + + private @Nullable PostalAddress address; + + public RetailLocationResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public RetailLocationResponse(String id, String name) { + this.id = id; + this.name = name; + } + + public RetailLocationResponse id(String id) { + this.id = id; + return this; + } + + /** + * Unique location identifier. + * @return id + */ + @NotNull + @Schema(name = "id", description = "Unique location identifier.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public RetailLocationResponse name(String name) { + this.name = name; + return this; + } + + /** + * Location name (e.g., store name). + * @return name + */ + @NotNull + @Schema(name = "name", description = "Location name (e.g., store name).", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RetailLocationResponse address(@Nullable PostalAddress address) { + this.address = address; + return this; + } + + /** + * Get address + * @return address + */ + @Valid + @Schema(name = "address", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address") + public @Nullable PostalAddress getAddress() { + return address; + } + + public void setAddress(@Nullable PostalAddress address) { + this.address = address; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public RetailLocationResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + RetailLocationResponse retailLocationResponse = (RetailLocationResponse) o; + return Objects.equals(this.id, retailLocationResponse.id) && + Objects.equals(this.name, retailLocationResponse.name) && + Objects.equals(this.address, retailLocationResponse.address) && + Objects.equals(this.additionalProperties, retailLocationResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, address, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class RetailLocationResponse {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" address: ").append(toIndentedString(address)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private RetailLocationResponse instance; + + public Builder() { + this(new RetailLocationResponse()); + } + + protected Builder(RetailLocationResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(RetailLocationResponse value) { + this.instance.setId(value.id); + this.instance.setName(value.name); + this.instance.setAddress(value.address); + return this; + } + + public RetailLocationResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public RetailLocationResponse.Builder name(String name) { + this.instance.name(name); + return this; + } + + public RetailLocationResponse.Builder address(PostalAddress address) { + this.instance.address(address); + return this; + } + + public RetailLocationResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built RetailLocationResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public RetailLocationResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static RetailLocationResponse.Builder builder() { + return new RetailLocationResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public RetailLocationResponse.Builder toBuilder() { + RetailLocationResponse.Builder builder = new RetailLocationResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationRequest.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationRequest.java new file mode 100644 index 0000000..55c2174 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationRequest.java @@ -0,0 +1,510 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Shipping destination. + */ + +@Schema(name = "Shipping_Destination_Request", description = "Shipping destination.") +@JsonTypeName("Shipping_Destination_Request") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ShippingDestinationRequest implements FulfillmentDestinationRequest { + + private @Nullable String extendedAddress; + + private @Nullable String streetAddress; + + private @Nullable String addressLocality; + + private @Nullable String addressRegion; + + private @Nullable String addressCountry; + + private @Nullable String postalCode; + + private @Nullable String firstName; + + private @Nullable String lastName; + + private @Nullable String fullName; + + private @Nullable String phoneNumber; + + private @Nullable String id; + + public ShippingDestinationRequest extendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + return this; + } + + /** + * An address extension such as an apartment number, C/O or alternative name. + * @return extendedAddress + */ + + @Schema(name = "extended_address", description = "An address extension such as an apartment number, C/O or alternative name.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("extended_address") + public @Nullable String getExtendedAddress() { + return extendedAddress; + } + + public void setExtendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + } + + public ShippingDestinationRequest streetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + return this; + } + + /** + * The street address. + * @return streetAddress + */ + + @Schema(name = "street_address", description = "The street address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("street_address") + public @Nullable String getStreetAddress() { + return streetAddress; + } + + public void setStreetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + } + + public ShippingDestinationRequest addressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + return this; + } + + /** + * The locality in which the street address is, and which is in the region. For example, Mountain View. + * @return addressLocality + */ + + @Schema(name = "address_locality", description = "The locality in which the street address is, and which is in the region. For example, Mountain View.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_locality") + public @Nullable String getAddressLocality() { + return addressLocality; + } + + public void setAddressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + } + + public ShippingDestinationRequest addressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + return this; + } + + /** + * The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. + * @return addressRegion + */ + + @Schema(name = "address_region", description = "The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_region") + public @Nullable String getAddressRegion() { + return addressRegion; + } + + public void setAddressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + } + + public ShippingDestinationRequest addressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + return this; + } + + /** + * The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used. + * @return addressCountry + */ + + @Schema(name = "address_country", description = "The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_country") + public @Nullable String getAddressCountry() { + return addressCountry; + } + + public void setAddressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + } + + public ShippingDestinationRequest postalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + return this; + } + + /** + * The postal code. For example, 94043. + * @return postalCode + */ + + @Schema(name = "postal_code", description = "The postal code. For example, 94043.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("postal_code") + public @Nullable String getPostalCode() { + return postalCode; + } + + public void setPostalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + } + + public ShippingDestinationRequest firstName(@Nullable String firstName) { + this.firstName = firstName; + return this; + } + + /** + * Optional. First name of the contact associated with the address. + * @return firstName + */ + + @Schema(name = "first_name", description = "Optional. First name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("first_name") + public @Nullable String getFirstName() { + return firstName; + } + + public void setFirstName(@Nullable String firstName) { + this.firstName = firstName; + } + + public ShippingDestinationRequest lastName(@Nullable String lastName) { + this.lastName = lastName; + return this; + } + + /** + * Optional. Last name of the contact associated with the address. + * @return lastName + */ + + @Schema(name = "last_name", description = "Optional. Last name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("last_name") + public @Nullable String getLastName() { + return lastName; + } + + public void setLastName(@Nullable String lastName) { + this.lastName = lastName; + } + + public ShippingDestinationRequest fullName(@Nullable String fullName) { + this.fullName = fullName; + return this; + } + + /** + * Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence). + * @return fullName + */ + + @Schema(name = "full_name", description = "Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("full_name") + public @Nullable String getFullName() { + return fullName; + } + + public void setFullName(@Nullable String fullName) { + this.fullName = fullName; + } + + public ShippingDestinationRequest phoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + /** + * Optional. Phone number of the contact associated with the address. + * @return phoneNumber + */ + + @Schema(name = "phone_number", description = "Optional. Phone number of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("phone_number") + public @Nullable String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public ShippingDestinationRequest id(@Nullable String id) { + this.id = id; + return this; + } + + /** + * ID specific to this shipping destination. + * @return id + */ + + @Schema(name = "id", description = "ID specific to this shipping destination.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("id") + public @Nullable String getId() { + return id; + } + + public void setId(@Nullable String id) { + this.id = id; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public ShippingDestinationRequest putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ShippingDestinationRequest shippingDestinationRequest = (ShippingDestinationRequest) o; + return Objects.equals(this.extendedAddress, shippingDestinationRequest.extendedAddress) && + Objects.equals(this.streetAddress, shippingDestinationRequest.streetAddress) && + Objects.equals(this.addressLocality, shippingDestinationRequest.addressLocality) && + Objects.equals(this.addressRegion, shippingDestinationRequest.addressRegion) && + Objects.equals(this.addressCountry, shippingDestinationRequest.addressCountry) && + Objects.equals(this.postalCode, shippingDestinationRequest.postalCode) && + Objects.equals(this.firstName, shippingDestinationRequest.firstName) && + Objects.equals(this.lastName, shippingDestinationRequest.lastName) && + Objects.equals(this.fullName, shippingDestinationRequest.fullName) && + Objects.equals(this.phoneNumber, shippingDestinationRequest.phoneNumber) && + Objects.equals(this.id, shippingDestinationRequest.id) && + Objects.equals(this.additionalProperties, shippingDestinationRequest.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(extendedAddress, streetAddress, addressLocality, addressRegion, addressCountry, postalCode, firstName, lastName, fullName, phoneNumber, id, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ShippingDestinationRequest {\n"); + sb.append(" extendedAddress: ").append(toIndentedString(extendedAddress)).append("\n"); + sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); + sb.append(" addressLocality: ").append(toIndentedString(addressLocality)).append("\n"); + sb.append(" addressRegion: ").append(toIndentedString(addressRegion)).append("\n"); + sb.append(" addressCountry: ").append(toIndentedString(addressCountry)).append("\n"); + sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); + sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); + sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); + sb.append(" fullName: ").append(toIndentedString(fullName)).append("\n"); + sb.append(" phoneNumber: ").append(toIndentedString(phoneNumber)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ShippingDestinationRequest instance; + + public Builder() { + this(new ShippingDestinationRequest()); + } + + protected Builder(ShippingDestinationRequest instance) { + this.instance = instance; + } + + protected Builder copyOf(ShippingDestinationRequest value) { + this.instance.setExtendedAddress(value.extendedAddress); + this.instance.setStreetAddress(value.streetAddress); + this.instance.setAddressLocality(value.addressLocality); + this.instance.setAddressRegion(value.addressRegion); + this.instance.setAddressCountry(value.addressCountry); + this.instance.setPostalCode(value.postalCode); + this.instance.setFirstName(value.firstName); + this.instance.setLastName(value.lastName); + this.instance.setFullName(value.fullName); + this.instance.setPhoneNumber(value.phoneNumber); + this.instance.setId(value.id); + return this; + } + + public ShippingDestinationRequest.Builder extendedAddress(String extendedAddress) { + this.instance.extendedAddress(extendedAddress); + return this; + } + + public ShippingDestinationRequest.Builder streetAddress(String streetAddress) { + this.instance.streetAddress(streetAddress); + return this; + } + + public ShippingDestinationRequest.Builder addressLocality(String addressLocality) { + this.instance.addressLocality(addressLocality); + return this; + } + + public ShippingDestinationRequest.Builder addressRegion(String addressRegion) { + this.instance.addressRegion(addressRegion); + return this; + } + + public ShippingDestinationRequest.Builder addressCountry(String addressCountry) { + this.instance.addressCountry(addressCountry); + return this; + } + + public ShippingDestinationRequest.Builder postalCode(String postalCode) { + this.instance.postalCode(postalCode); + return this; + } + + public ShippingDestinationRequest.Builder firstName(String firstName) { + this.instance.firstName(firstName); + return this; + } + + public ShippingDestinationRequest.Builder lastName(String lastName) { + this.instance.lastName(lastName); + return this; + } + + public ShippingDestinationRequest.Builder fullName(String fullName) { + this.instance.fullName(fullName); + return this; + } + + public ShippingDestinationRequest.Builder phoneNumber(String phoneNumber) { + this.instance.phoneNumber(phoneNumber); + return this; + } + + public ShippingDestinationRequest.Builder id(String id) { + this.instance.id(id); + return this; + } + + public ShippingDestinationRequest.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built ShippingDestinationRequest instance. + * + * The builder is not reusable (NullPointerException) + */ + public ShippingDestinationRequest build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ShippingDestinationRequest.Builder builder() { + return new ShippingDestinationRequest.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ShippingDestinationRequest.Builder toBuilder() { + ShippingDestinationRequest.Builder builder = new ShippingDestinationRequest.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationResponse.java new file mode 100644 index 0000000..a171e24 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/ShippingDestinationResponse.java @@ -0,0 +1,521 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Shipping destination. + */ + +@Schema(name = "Shipping_Destination_Response", description = "Shipping destination.") +@JsonTypeName("Shipping_Destination_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class ShippingDestinationResponse implements FulfillmentDestinationResponse { + + private @Nullable String extendedAddress; + + private @Nullable String streetAddress; + + private @Nullable String addressLocality; + + private @Nullable String addressRegion; + + private @Nullable String addressCountry; + + private @Nullable String postalCode; + + private @Nullable String firstName; + + private @Nullable String lastName; + + private @Nullable String fullName; + + private @Nullable String phoneNumber; + + private String id; + + public ShippingDestinationResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ShippingDestinationResponse(String id) { + this.id = id; + } + + public ShippingDestinationResponse extendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + return this; + } + + /** + * An address extension such as an apartment number, C/O or alternative name. + * @return extendedAddress + */ + + @Schema(name = "extended_address", description = "An address extension such as an apartment number, C/O or alternative name.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("extended_address") + public @Nullable String getExtendedAddress() { + return extendedAddress; + } + + public void setExtendedAddress(@Nullable String extendedAddress) { + this.extendedAddress = extendedAddress; + } + + public ShippingDestinationResponse streetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + return this; + } + + /** + * The street address. + * @return streetAddress + */ + + @Schema(name = "street_address", description = "The street address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("street_address") + public @Nullable String getStreetAddress() { + return streetAddress; + } + + public void setStreetAddress(@Nullable String streetAddress) { + this.streetAddress = streetAddress; + } + + public ShippingDestinationResponse addressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + return this; + } + + /** + * The locality in which the street address is, and which is in the region. For example, Mountain View. + * @return addressLocality + */ + + @Schema(name = "address_locality", description = "The locality in which the street address is, and which is in the region. For example, Mountain View.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_locality") + public @Nullable String getAddressLocality() { + return addressLocality; + } + + public void setAddressLocality(@Nullable String addressLocality) { + this.addressLocality = addressLocality; + } + + public ShippingDestinationResponse addressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + return this; + } + + /** + * The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division. + * @return addressRegion + */ + + @Schema(name = "address_region", description = "The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_region") + public @Nullable String getAddressRegion() { + return addressRegion; + } + + public void setAddressRegion(@Nullable String addressRegion) { + this.addressRegion = addressRegion; + } + + public ShippingDestinationResponse addressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + return this; + } + + /** + * The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used. + * @return addressCountry + */ + + @Schema(name = "address_country", description = "The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example \"US\". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as \"SGP\" or a full country name such as \"Singapore\" can also be used.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("address_country") + public @Nullable String getAddressCountry() { + return addressCountry; + } + + public void setAddressCountry(@Nullable String addressCountry) { + this.addressCountry = addressCountry; + } + + public ShippingDestinationResponse postalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + return this; + } + + /** + * The postal code. For example, 94043. + * @return postalCode + */ + + @Schema(name = "postal_code", description = "The postal code. For example, 94043.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("postal_code") + public @Nullable String getPostalCode() { + return postalCode; + } + + public void setPostalCode(@Nullable String postalCode) { + this.postalCode = postalCode; + } + + public ShippingDestinationResponse firstName(@Nullable String firstName) { + this.firstName = firstName; + return this; + } + + /** + * Optional. First name of the contact associated with the address. + * @return firstName + */ + + @Schema(name = "first_name", description = "Optional. First name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("first_name") + public @Nullable String getFirstName() { + return firstName; + } + + public void setFirstName(@Nullable String firstName) { + this.firstName = firstName; + } + + public ShippingDestinationResponse lastName(@Nullable String lastName) { + this.lastName = lastName; + return this; + } + + /** + * Optional. Last name of the contact associated with the address. + * @return lastName + */ + + @Schema(name = "last_name", description = "Optional. Last name of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("last_name") + public @Nullable String getLastName() { + return lastName; + } + + public void setLastName(@Nullable String lastName) { + this.lastName = lastName; + } + + public ShippingDestinationResponse fullName(@Nullable String fullName) { + this.fullName = fullName; + return this; + } + + /** + * Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence). + * @return fullName + */ + + @Schema(name = "full_name", description = "Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence).", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("full_name") + public @Nullable String getFullName() { + return fullName; + } + + public void setFullName(@Nullable String fullName) { + this.fullName = fullName; + } + + public ShippingDestinationResponse phoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + return this; + } + + /** + * Optional. Phone number of the contact associated with the address. + * @return phoneNumber + */ + + @Schema(name = "phone_number", description = "Optional. Phone number of the contact associated with the address.", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("phone_number") + public @Nullable String getPhoneNumber() { + return phoneNumber; + } + + public void setPhoneNumber(@Nullable String phoneNumber) { + this.phoneNumber = phoneNumber; + } + + public ShippingDestinationResponse id(String id) { + this.id = id; + return this; + } + + /** + * ID specific to this shipping destination. + * @return id + */ + @NotNull + @Schema(name = "id", description = "ID specific to this shipping destination.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public ShippingDestinationResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ShippingDestinationResponse shippingDestinationResponse = (ShippingDestinationResponse) o; + return Objects.equals(this.extendedAddress, shippingDestinationResponse.extendedAddress) && + Objects.equals(this.streetAddress, shippingDestinationResponse.streetAddress) && + Objects.equals(this.addressLocality, shippingDestinationResponse.addressLocality) && + Objects.equals(this.addressRegion, shippingDestinationResponse.addressRegion) && + Objects.equals(this.addressCountry, shippingDestinationResponse.addressCountry) && + Objects.equals(this.postalCode, shippingDestinationResponse.postalCode) && + Objects.equals(this.firstName, shippingDestinationResponse.firstName) && + Objects.equals(this.lastName, shippingDestinationResponse.lastName) && + Objects.equals(this.fullName, shippingDestinationResponse.fullName) && + Objects.equals(this.phoneNumber, shippingDestinationResponse.phoneNumber) && + Objects.equals(this.id, shippingDestinationResponse.id) && + Objects.equals(this.additionalProperties, shippingDestinationResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(extendedAddress, streetAddress, addressLocality, addressRegion, addressCountry, postalCode, firstName, lastName, fullName, phoneNumber, id, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ShippingDestinationResponse {\n"); + sb.append(" extendedAddress: ").append(toIndentedString(extendedAddress)).append("\n"); + sb.append(" streetAddress: ").append(toIndentedString(streetAddress)).append("\n"); + sb.append(" addressLocality: ").append(toIndentedString(addressLocality)).append("\n"); + sb.append(" addressRegion: ").append(toIndentedString(addressRegion)).append("\n"); + sb.append(" addressCountry: ").append(toIndentedString(addressCountry)).append("\n"); + sb.append(" postalCode: ").append(toIndentedString(postalCode)).append("\n"); + sb.append(" firstName: ").append(toIndentedString(firstName)).append("\n"); + sb.append(" lastName: ").append(toIndentedString(lastName)).append("\n"); + sb.append(" fullName: ").append(toIndentedString(fullName)).append("\n"); + sb.append(" phoneNumber: ").append(toIndentedString(phoneNumber)).append("\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private ShippingDestinationResponse instance; + + public Builder() { + this(new ShippingDestinationResponse()); + } + + protected Builder(ShippingDestinationResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(ShippingDestinationResponse value) { + this.instance.setExtendedAddress(value.extendedAddress); + this.instance.setStreetAddress(value.streetAddress); + this.instance.setAddressLocality(value.addressLocality); + this.instance.setAddressRegion(value.addressRegion); + this.instance.setAddressCountry(value.addressCountry); + this.instance.setPostalCode(value.postalCode); + this.instance.setFirstName(value.firstName); + this.instance.setLastName(value.lastName); + this.instance.setFullName(value.fullName); + this.instance.setPhoneNumber(value.phoneNumber); + this.instance.setId(value.id); + return this; + } + + public ShippingDestinationResponse.Builder extendedAddress(String extendedAddress) { + this.instance.extendedAddress(extendedAddress); + return this; + } + + public ShippingDestinationResponse.Builder streetAddress(String streetAddress) { + this.instance.streetAddress(streetAddress); + return this; + } + + public ShippingDestinationResponse.Builder addressLocality(String addressLocality) { + this.instance.addressLocality(addressLocality); + return this; + } + + public ShippingDestinationResponse.Builder addressRegion(String addressRegion) { + this.instance.addressRegion(addressRegion); + return this; + } + + public ShippingDestinationResponse.Builder addressCountry(String addressCountry) { + this.instance.addressCountry(addressCountry); + return this; + } + + public ShippingDestinationResponse.Builder postalCode(String postalCode) { + this.instance.postalCode(postalCode); + return this; + } + + public ShippingDestinationResponse.Builder firstName(String firstName) { + this.instance.firstName(firstName); + return this; + } + + public ShippingDestinationResponse.Builder lastName(String lastName) { + this.instance.lastName(lastName); + return this; + } + + public ShippingDestinationResponse.Builder fullName(String fullName) { + this.instance.fullName(fullName); + return this; + } + + public ShippingDestinationResponse.Builder phoneNumber(String phoneNumber) { + this.instance.phoneNumber(phoneNumber); + return this; + } + + public ShippingDestinationResponse.Builder id(String id) { + this.instance.id(id); + return this; + } + + public ShippingDestinationResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built ShippingDestinationResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public ShippingDestinationResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static ShippingDestinationResponse.Builder builder() { + return new ShippingDestinationResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public ShippingDestinationResponse.Builder toBuilder() { + ShippingDestinationResponse.Builder builder = new ShippingDestinationResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TokenCredentialResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TokenCredentialResponse.java new file mode 100644 index 0000000..fc88b47 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TokenCredentialResponse.java @@ -0,0 +1,222 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.JsonNode; +import org.springframework.lang.Nullable; +import com.fasterxml.jackson.annotation.JsonValue; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +import java.util.Map; +import java.util.HashMap; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +/** + * Base token credential schema. Concrete payment handlers may extend this schema with additional fields and define their own constraints. + */ + +@Schema(name = "Token_Credential_Response", description = "Base token credential schema. Concrete payment handlers may extend this schema with additional fields and define their own constraints.") +@JsonTypeName("Token_Credential_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class TokenCredentialResponse implements PaymentCredential { + + private String type; + + public TokenCredentialResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public TokenCredentialResponse(String type) { + this.type = type; + } + + public TokenCredentialResponse type(String type) { + this.type = type; + return this; + } + + /** + * The specific type of token produced by the handler (e.g., 'stripe_token'). + * @return type + */ + @NotNull + @Schema(name = "type", description = "The specific type of token produced by the handler (e.g., 'stripe_token').", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + */ + @JsonAnySetter + public TokenCredentialResponse putAdditionalProperty(String key, JsonNode value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + */ + @JsonAnyGetter + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + */ + public JsonNode getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TokenCredentialResponse tokenCredentialResponse = (TokenCredentialResponse) o; + return Objects.equals(this.type, tokenCredentialResponse.type) && + Objects.equals(this.additionalProperties, tokenCredentialResponse.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(type, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TokenCredentialResponse {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private TokenCredentialResponse instance; + + public Builder() { + this(new TokenCredentialResponse()); + } + + protected Builder(TokenCredentialResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(TokenCredentialResponse value) { + this.instance.setType(value.type); + return this; + } + + public TokenCredentialResponse.Builder type(String type) { + this.instance.type(type); + return this; + } + + public TokenCredentialResponse.Builder additionalProperties(Map additionalProperties) { + this.instance.additionalProperties = additionalProperties; + return this; + } + + /** + * returns a built TokenCredentialResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public TokenCredentialResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static TokenCredentialResponse.Builder builder() { + return new TokenCredentialResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public TokenCredentialResponse.Builder toBuilder() { + TokenCredentialResponse.Builder builder = new TokenCredentialResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TotalResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TotalResponse.java new file mode 100644 index 0000000..c50bae7 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/TotalResponse.java @@ -0,0 +1,278 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonValue; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * TotalResponse + */ + +@JsonTypeName("Total_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:22.638361369Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class TotalResponse { + + /** + * Type of total categorization. + */ + public enum TypeEnum { + ITEMS_DISCOUNT("items_discount"), + + SUBTOTAL("subtotal"), + + DISCOUNT("discount"), + + FULFILLMENT("fulfillment"), + + TAX("tax"), + + FEE("fee"), + + TOTAL("total"); + + private final String value; + + TypeEnum(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static TypeEnum fromValue(String value) { + for (TypeEnum b : TypeEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + } + + private TypeEnum type; + + private @Nullable String displayText; + + private Integer amount; + + public TotalResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public TotalResponse(TypeEnum type, Integer amount) { + this.type = type; + this.amount = amount; + } + + public TotalResponse type(TypeEnum type) { + this.type = type; + return this; + } + + /** + * Type of total categorization. + * @return type + */ + @NotNull + @Schema(name = "type", description = "Type of total categorization.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("type") + public TypeEnum getType() { + return type; + } + + public void setType(TypeEnum type) { + this.type = type; + } + + public TotalResponse displayText(@Nullable String displayText) { + this.displayText = displayText; + return this; + } + + /** + * Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery'). + * @return displayText + */ + + @Schema(name = "display_text", description = "Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery').", requiredMode = Schema.RequiredMode.NOT_REQUIRED) + @JsonProperty("display_text") + public @Nullable String getDisplayText() { + return displayText; + } + + public void setDisplayText(@Nullable String displayText) { + this.displayText = displayText; + } + + public TotalResponse amount(Integer amount) { + this.amount = amount; + return this; + } + + /** + * If type == total, sums subtotal - discount + fulfillment + tax + fee. Should be >= 0. Amount in minor (cents) currency units. + * minimum: 0 + * @return amount + */ + @NotNull @Min(value = 0) + @Schema(name = "amount", description = "If type == total, sums subtotal - discount + fulfillment + tax + fee. Should be >= 0. Amount in minor (cents) currency units.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("amount") + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TotalResponse totalResponse = (TotalResponse) o; + return Objects.equals(this.type, totalResponse.type) && + Objects.equals(this.displayText, totalResponse.displayText) && + Objects.equals(this.amount, totalResponse.amount); + } + + @Override + public int hashCode() { + return Objects.hash(type, displayText, amount); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class TotalResponse {\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" displayText: ").append(toIndentedString(displayText)).append("\n"); + sb.append(" amount: ").append(toIndentedString(amount)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private TotalResponse instance; + + public Builder() { + this(new TotalResponse()); + } + + protected Builder(TotalResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(TotalResponse value) { + this.instance.setType(value.type); + this.instance.setDisplayText(value.displayText); + this.instance.setAmount(value.amount); + return this; + } + + public TotalResponse.Builder type(TypeEnum type) { + this.instance.type(type); + return this; + } + + public TotalResponse.Builder displayText(String displayText) { + this.instance.displayText(displayText); + return this; + } + + public TotalResponse.Builder amount(Integer amount) { + this.instance.amount(amount); + return this; + } + + /** + * returns a built TotalResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public TotalResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static TotalResponse.Builder builder() { + return new TotalResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public TotalResponse.Builder toBuilder() { + TotalResponse.Builder builder = new TotalResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPCheckoutResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPCheckoutResponse.java new file mode 100644 index 0000000..c12fcab --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPCheckoutResponse.java @@ -0,0 +1,215 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CapabilityResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * UCP metadata for checkout responses. + */ + +@Schema(name = "UCP_Checkout_Response", description = "UCP metadata for checkout responses.") +@JsonTypeName("UCP_Checkout_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class UCPCheckoutResponse { + + private String version; + + @Valid + private List capabilities = new ArrayList<>(); + + public UCPCheckoutResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public UCPCheckoutResponse(String version, List capabilities) { + this.version = version; + this.capabilities = capabilities; + } + + public UCPCheckoutResponse version(String version) { + this.version = version; + return this; + } + + /** + * Handler version in YYYY-MM-DD format. + * @return version + */ + @NotNull @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$") + @Schema(name = "version", description = "Handler version in YYYY-MM-DD format.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("version") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public UCPCheckoutResponse capabilities(List capabilities) { + this.capabilities = capabilities; + return this; + } + + public UCPCheckoutResponse addCapabilitiesItem(CapabilityResponse capabilitiesItem) { + if (this.capabilities == null) { + this.capabilities = new ArrayList<>(); + } + this.capabilities.add(capabilitiesItem); + return this; + } + + /** + * Active capabilities for this response. + * @return capabilities + */ + @NotNull @Valid + @Schema(name = "capabilities", description = "Active capabilities for this response.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("capabilities") + public List getCapabilities() { + return capabilities; + } + + public void setCapabilities(List capabilities) { + this.capabilities = capabilities; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UCPCheckoutResponse ucPCheckoutResponse = (UCPCheckoutResponse) o; + return Objects.equals(this.version, ucPCheckoutResponse.version) && + Objects.equals(this.capabilities, ucPCheckoutResponse.capabilities); + } + + @Override + public int hashCode() { + return Objects.hash(version, capabilities); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class UCPCheckoutResponse {\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" capabilities: ").append(toIndentedString(capabilities)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private UCPCheckoutResponse instance; + + public Builder() { + this(new UCPCheckoutResponse()); + } + + protected Builder(UCPCheckoutResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(UCPCheckoutResponse value) { + this.instance.setVersion(value.version); + this.instance.setCapabilities(value.capabilities); + return this; + } + + public UCPCheckoutResponse.Builder version(String version) { + this.instance.version(version); + return this; + } + + public UCPCheckoutResponse.Builder capabilities(List capabilities) { + this.instance.capabilities(capabilities); + return this; + } + + /** + * returns a built UCPCheckoutResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public UCPCheckoutResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static UCPCheckoutResponse.Builder builder() { + return new UCPCheckoutResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public UCPCheckoutResponse.Builder toBuilder() { + UCPCheckoutResponse.Builder builder = new UCPCheckoutResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPOrderResponse.java b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPOrderResponse.java new file mode 100644 index 0000000..7f5c899 --- /dev/null +++ b/rest/java-spring/generated/src/main/java/com/dev/ucp/service/shopping/model/UCPOrderResponse.java @@ -0,0 +1,215 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.dev.ucp.service.shopping.model; + +import java.net.URI; +import java.util.Objects; +import com.dev.ucp.service.shopping.model.CapabilityResponse; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeName; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.springframework.lang.Nullable; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * UCP metadata for order responses. No payment handlers needed post-purchase. + */ + +@Schema(name = "UCP_Order_Response", description = "UCP metadata for order responses. No payment handlers needed post-purchase.") +@JsonTypeName("UCP_Order_Response") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2026-01-26T17:13:20.046324970Z[Etc/UTC]", comments = "Generator version: 7.17.0") +public class UCPOrderResponse { + + private String version; + + @Valid + private List capabilities = new ArrayList<>(); + + public UCPOrderResponse() { + super(); + } + + /** + * Constructor with only required parameters + */ + public UCPOrderResponse(String version, List capabilities) { + this.version = version; + this.capabilities = capabilities; + } + + public UCPOrderResponse version(String version) { + this.version = version; + return this; + } + + /** + * Handler version in YYYY-MM-DD format. + * @return version + */ + @NotNull @Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$") + @Schema(name = "version", description = "Handler version in YYYY-MM-DD format.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("version") + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public UCPOrderResponse capabilities(List capabilities) { + this.capabilities = capabilities; + return this; + } + + public UCPOrderResponse addCapabilitiesItem(CapabilityResponse capabilitiesItem) { + if (this.capabilities == null) { + this.capabilities = new ArrayList<>(); + } + this.capabilities.add(capabilitiesItem); + return this; + } + + /** + * Active capabilities for this response. + * @return capabilities + */ + @NotNull @Valid + @Schema(name = "capabilities", description = "Active capabilities for this response.", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("capabilities") + public List getCapabilities() { + return capabilities; + } + + public void setCapabilities(List capabilities) { + this.capabilities = capabilities; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + UCPOrderResponse ucPOrderResponse = (UCPOrderResponse) o; + return Objects.equals(this.version, ucPOrderResponse.version) && + Objects.equals(this.capabilities, ucPOrderResponse.capabilities); + } + + @Override + public int hashCode() { + return Objects.hash(version, capabilities); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class UCPOrderResponse {\n"); + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" capabilities: ").append(toIndentedString(capabilities)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + public static class Builder { + + private UCPOrderResponse instance; + + public Builder() { + this(new UCPOrderResponse()); + } + + protected Builder(UCPOrderResponse instance) { + this.instance = instance; + } + + protected Builder copyOf(UCPOrderResponse value) { + this.instance.setVersion(value.version); + this.instance.setCapabilities(value.capabilities); + return this; + } + + public UCPOrderResponse.Builder version(String version) { + this.instance.version(version); + return this; + } + + public UCPOrderResponse.Builder capabilities(List capabilities) { + this.instance.capabilities(capabilities); + return this; + } + + /** + * returns a built UCPOrderResponse instance. + * + * The builder is not reusable (NullPointerException) + */ + public UCPOrderResponse build() { + try { + return this.instance; + } finally { + // ensure that this.instance is not reused + this.instance = null; + } + } + + @Override + public String toString() { + return getClass() + "=(" + instance + ")"; + } + } + + /** + * Create a builder with no initialized field (except for the default values). + */ + public static UCPOrderResponse.Builder builder() { + return new UCPOrderResponse.Builder(); + } + + /** + * Create a builder with a shallow copy of this instance. + */ + public UCPOrderResponse.Builder toBuilder() { + UCPOrderResponse.Builder builder = new UCPOrderResponse.Builder(); + return builder.copyOf(this); + } + +} + diff --git a/rest/java-spring/license_template.txt b/rest/java-spring/license_template.txt new file mode 100644 index 0000000..1347dff --- /dev/null +++ b/rest/java-spring/license_template.txt @@ -0,0 +1,13 @@ +Copyright 2026 UCP Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/rest/java-spring/pom.xml b/rest/java-spring/pom.xml new file mode 100644 index 0000000..7f87a97 --- /dev/null +++ b/rest/java-spring/pom.xml @@ -0,0 +1,190 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.5.9 + + + com.dev.ucp + spring-demo + 2026.1.11-1-SNAPSHOT + ucp-spring-demo + Demo project for UCP Spring Boot integration + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + + + + + + + 25 + + + + + org.springframework.boot + spring-boot-starter-parent + 3.5.9 + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-validation + + + javax.validation + validation-api + 2.0.1.Final + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.14 + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + javax.annotation + javax.annotation-api + 1.3.2 + + + + com.h2database + h2 + runtime + + + org.springframework.boot + spring-boot-devtools + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.codehaus.mojo + build-helper-maven-plugin + 3.6.1 + + + add-source + generate-sources + + add-source + + + + ${basedir}/generated/src/main/java + + + + + + + com.mycila + license-maven-plugin + 4.6 + +

${project.basedir}/license_template.txt
+ + SLASHSTAR_STYLE + SCRIPT_STYLE + + + src/main/java/**/*.java + src/test/java/**/*.java + conformance/src/main/java/**/*.java + *.sh + + true + + + + add-license-headers + process-sources + + format + + + + + + + + + + conformance + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-conformance-source + generate-sources + + add-source + + + + ${basedir}/conformance/src/main/java + + + + + + + + + + diff --git a/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java b/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java new file mode 100644 index 0000000..f4b6907 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java @@ -0,0 +1,143 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.exceptions.ExtensionValidationException; +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.dev.ucp.service.shopping.model.DiscountRequest; +import com.dev.ucp.service.shopping.model.DiscountResponse; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validator; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Utility class for managing UCP extension fields in request and response objects. + * + *

This class provides methods for accessing and modifying the fulfillment and discounts + * extensions within the checkout request and response objects. It allows for business logic to be + * decoupled from json serialization details. + */ +@Component +public class ExtensionsHelper { + + private static final String FULFILLMENT_PROPERTY = "fulfillment"; + private static final String DISCOUNTS_PROPERTY = "discounts"; + + private static final Predicate> IGNORE_ID_FILTER = + v -> !v.getPropertyPath().toString().endsWith(".id") && !v.getPropertyPath().toString().equals("id"); + + @Autowired private ObjectMapper objectMapper; + @Autowired private Validator validator; + + public ExtensionsHelper() {} + + public FulfillmentResponse getFulfillmentRequest(CheckoutCreateRequest request) { + // Parse request as response since this part of schema is unstable and there are proposed fixes in flight. + // Create request is missing id fields, reponse has all fields needed. + // TODO: Make proper conversion later. + return getAdditionalProperty( + request::getAdditionalProperty, + FULFILLMENT_PROPERTY, + FulfillmentResponse.class, + IGNORE_ID_FILTER); + } + + public FulfillmentResponse getFulfillmentRequest(CheckoutUpdateRequest request) { + // Parse request as response since this part of schema is unstable and there are proposed fixes in flight. + // Create request is missing id fields, reponse has all fields needed. + // TODO: Make proper conversion later. + return getAdditionalProperty( + request::getAdditionalProperty, + FULFILLMENT_PROPERTY, + FulfillmentResponse.class, + IGNORE_ID_FILTER); + } + + public FulfillmentResponse getFulfillmentResponse(CheckoutResponse checkout) { + return getAdditionalProperty( + checkout::getAdditionalProperty, + FULFILLMENT_PROPERTY, + FulfillmentResponse.class, + IGNORE_ID_FILTER); + } + + public DiscountRequest getDiscount(CheckoutCreateRequest request) { + return getAdditionalProperty( + request::getAdditionalProperty, DISCOUNTS_PROPERTY, DiscountRequest.class, v -> true); + } + + public DiscountRequest getDiscount(CheckoutUpdateRequest request) { + return getAdditionalProperty( + request::getAdditionalProperty, DISCOUNTS_PROPERTY, DiscountRequest.class, v -> true); + } + + public void setFulfillment(CheckoutResponse checkout, FulfillmentResponse fulfillmentResponse) { + setAdditionalProperty(checkout::putAdditionalProperty, FULFILLMENT_PROPERTY, fulfillmentResponse); + } + + public void setDiscount(CheckoutResponse checkout, DiscountResponse discount) { + setAdditionalProperty(checkout::putAdditionalProperty, DISCOUNTS_PROPERTY, discount); + } + + private T getAdditionalProperty( + Function getter, + String propertyName, + Class valueType, + Predicate> violationFilter) { + try { + JsonNode node = getter.apply(propertyName); + if (node == null) { + return null; + } + T value = objectMapper.treeToValue(node, valueType); + + Set> violations = + validator.validate(value).stream() + .filter(violationFilter) + .collect(Collectors.toSet()); + + if (!violations.isEmpty()) { + throw new ExtensionValidationException((Set>) (Set) violations); + } + + return value; + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing " + propertyName + " extension", e); + } + } + + private void setAdditionalProperty(BiConsumer setter, String propertyName, T value) { + try { + JsonNode node = objectMapper.valueToTree(value); + setter.accept(propertyName, node); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Error setting " + propertyName + " extension", e); + } + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java.orig new file mode 100644 index 0000000..70ce300 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/ExtensionsHelper.java.orig @@ -0,0 +1,40 @@ +package com.dev.ucp; + +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.dev.ucp.service.shopping.model.FulfillmentRequest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; + +public class ExtensionsHelper { + + @Autowired private ObjectMapper objectMapper; + + public ExtensionsHelper() {} + + public FulfillmentRequest getFulfillmentRequest(CheckoutCreateRequest request) { + try { + JsonNode fulfillmentNode = request.getAdditionalProperty("fulfillment"); + if (fulfillmentNode == null) { + return null; + } + return objectMapper.treeToValue(fulfillmentNode, FulfillmentRequest.class); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing fulfillment extension", e); + } + } + + public FulfillmentRequest getFulfillmentRequest(CheckoutUpdateRequest request) { + try { + JsonNode fulfillmentNode = request.getAdditionalProperty("fulfillment"); + if (fulfillmentNode == null) { + return null; + } + return objectMapper.treeToValue(fulfillmentNode, FulfillmentRequest.class); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing fulfillment extension", e); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentManager.java b/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentManager.java new file mode 100644 index 0000000..2525880 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentManager.java @@ -0,0 +1,338 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.entities.AddressEntity; +import com.dev.ucp.entities.PromotionEntity; +import com.dev.ucp.entities.ShippingRateEntity; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.FulfillmentAvailableMethodResponse; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationResponse; +import com.dev.ucp.service.shopping.model.FulfillmentGroupResponse; +import com.dev.ucp.service.shopping.model.FulfillmentMethodResponse; +import com.dev.ucp.service.shopping.model.FulfillmentOptionResponse; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.ShippingDestinationResponse; +import com.dev.ucp.service.shopping.model.TotalResponse; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Manages fulfillment logic for checkout sessions. + * + *

Responsible for identifying available shipping destinations, calculating shipping rates, and + * validating fulfillment selections. + */ +@Component +public class FulfillmentManager { + + @Autowired private AddressEntity.Repository addressRepository; + @Autowired private ShippingRateEntity.Repository shippingRateRepository; + @Autowired private PromotionEntity.Repository promotionRepository; + @Autowired private ExtensionsHelper extensionsHelper; + + public void handleFulfillment(CheckoutResponse checkout, FulfillmentResponse fulfillment) { + if (fulfillment == null) { + fulfillment = new FulfillmentResponse(); + } + + addDefaultsAndValidateRequestedFulfillment(checkout, fulfillment); + + FulfillmentMethodResponse fulfillmentMethod = fulfillment.getMethods().get(0); + + long currentGrandTotal = + checkout.getTotals().stream() + .filter(total -> total.getType() == TotalResponse.TypeEnum.SUBTOTAL) + .mapToLong(TotalResponse::getAmount) + .findFirst() + .orElse(0L); + + populateMethodDetails(fulfillmentMethod, currentGrandTotal, checkout.getLineItems(), checkout); + + extensionsHelper.setFulfillment(checkout, fulfillment); + } + + public long recalculateFulfillmentTotals(CheckoutResponse checkout) { + long fulfillmentTotal = 0; + FulfillmentResponse fulfillment = extensionsHelper.getFulfillmentResponse(checkout); + if (fulfillment != null && fulfillment.getMethods() != null) { + for (FulfillmentMethodResponse fulfillmentMethod : fulfillment.getMethods()) { + if (fulfillmentMethod.getGroups() != null) { + for (FulfillmentGroupResponse group : fulfillmentMethod.getGroups()) { + if (group.getSelectedOptionId() != null + && group.getSelectedOptionId().isPresent() + && group.getOptions() != null) { + String selectedOptionId = group.getSelectedOptionId().get(); + for (FulfillmentOptionResponse option : group.getOptions()) { + if (option.getId().equals(selectedOptionId) && option.getTotals() != null) { + for (TotalResponse total : option.getTotals()) { + if (total.getType() == TotalResponse.TypeEnum.TOTAL) { + fulfillmentTotal += total.getAmount(); + } + } + } + } + } + } + } + } + } + return fulfillmentTotal; + } + + private void addDefaultsAndValidateRequestedFulfillment( + CheckoutResponse checkout, FulfillmentResponse fulfillment) { + List allLineItemIds = + checkout.getLineItems().stream().map(LineItemResponse::getId).collect(Collectors.toList()); + + // Initialize available methods + FulfillmentAvailableMethodResponse availableMethod = new FulfillmentAvailableMethodResponse(); + availableMethod.setType(FulfillmentAvailableMethodResponse.TypeEnum.SHIPPING); + availableMethod.setLineItemIds(allLineItemIds); + fulfillment.setAvailableMethods(List.of(availableMethod)); + + // Resolve known addresses + String buyerEmail = (checkout.getBuyer() != null) ? checkout.getBuyer().getEmail() : null; + List knownAddresses = + (buyerEmail != null) ? getKnownAddressesForBuyer(buyerEmail) : new ArrayList<>(); + + // Suggested method if none provided + if (fulfillment.getMethods() == null || fulfillment.getMethods().isEmpty()) { + FulfillmentMethodResponse defaultMethod = new FulfillmentMethodResponse(); + defaultMethod.setType(FulfillmentMethodResponse.TypeEnum.SHIPPING); + defaultMethod.setLineItemIds(allLineItemIds); + fulfillment.setMethods(new ArrayList<>(List.of(defaultMethod))); + } + + // Merge destinations and assign line items + for (FulfillmentMethodResponse fulfillmentMethod : fulfillment.getMethods()) { + // Cast list elements to the interface type for processing + List requestedDestinations = new ArrayList<>(); + if (fulfillmentMethod.getDestinations() != null) { + requestedDestinations.addAll(fulfillmentMethod.getDestinations()); + } + + List merged = + mergeDestinationsPolymorphic(knownAddresses, requestedDestinations); + fulfillmentMethod.setDestinations( + merged.stream().map(d -> (ShippingDestinationResponse) d).collect(Collectors.toList())); + + if (fulfillmentMethod.getId() == null) { + fulfillmentMethod.setId(UUID.randomUUID().toString()); + } + + if (fulfillmentMethod.getDestinations() == null + || fulfillmentMethod.getDestinations().isEmpty()) { + checkout.addMessagesItem( + new MessageError() + .severity(MessageError.SeverityEnum.RECOVERABLE) + .type(MessageError.TypeEnum.ERROR) + .code("no_fulfillment_destinations") + .path( + "$.fulfillment.methods[?(@.id=='" + + fulfillmentMethod.getId() + + "')].destinations") + .content("No fulfillment destinations found for the buyer.")); + } + + if (fulfillmentMethod.getGroups() == null || fulfillmentMethod.getGroups().isEmpty()) { + FulfillmentGroupResponse defaultGroup = new FulfillmentGroupResponse(); + defaultGroup.setLineItemIds(fulfillmentMethod.getLineItemIds()); + fulfillmentMethod.setGroups(new ArrayList<>(List.of(defaultGroup))); + } + + for (FulfillmentGroupResponse group : fulfillmentMethod.getGroups()) { + if (group.getId() == null) { + group.setId(UUID.randomUUID().toString()); + } + } + } + + // Validation + if (fulfillment.getMethods().size() > 1) { + checkout.addMessagesItem( + new MessageError() + .severity(MessageError.SeverityEnum.RECOVERABLE) + .type(MessageError.TypeEnum.ERROR) + .code("multiple_fulfillment_methods_not_supported") + .path("$.fulfillment.methods") + .content("Multiple fulfillment methods are not supported.")); + } else if (fulfillment.getMethods().get(0).getType() + != FulfillmentMethodResponse.TypeEnum.SHIPPING) { + checkout.addMessagesItem( + new MessageError() + .severity(MessageError.SeverityEnum.RECOVERABLE) + .type(MessageError.TypeEnum.ERROR) + .code("unsupported_fulfillment_method_type") + .path("$.fulfillment.methods[0]") + .content("Unsupported fulfillment method type.")); + } + } + + private List getAvailableShippingOptions( + String countryCode, long subtotal, List lineItems) { + List dbRates = + shippingRateRepository.findByCountryCodeOrCountryCode("default", countryCode); + List options = new ArrayList<>(); + + boolean isFreeShipping = false; + if (lineItems != null) { + for (LineItemResponse lineItem : lineItems) { + if ("bouquet_roses".equals(lineItem.getItem().getId())) { + isFreeShipping = true; + break; + } + } + } + + if (!isFreeShipping) { + List promotions = promotionRepository.findAll(); + for (PromotionEntity promo : promotions) { + if ("free_shipping".equals(promo.getType()) && subtotal >= promo.getSubtotalThreshold()) { + isFreeShipping = true; + break; + } + } + } + + for (ShippingRateEntity rate : dbRates) { + long price = rate.getPrice(); + String title = rate.getTitle(); + if (isFreeShipping && "standard".equalsIgnoreCase(rate.getServiceLevel())) { + price = 0; + title += " (Free)"; + } + FulfillmentOptionResponse option = + new FulfillmentOptionResponse().id(rate.getId()).title(title); + option.addTotalsItem( + new TotalResponse().type(TotalResponse.TypeEnum.SUBTOTAL).amount(Math.toIntExact(price))); + option.addTotalsItem( + new TotalResponse().type(TotalResponse.TypeEnum.TOTAL).amount(Math.toIntExact(price))); + options.add(option); + } + return options; + } + + private List getKnownAddressesForBuyer(String email) { + List addressEntities = addressRepository.findByCustomerEmail(email); + return addressEntities.stream() + .map(this::convertToShippingDestinationResponse) + .collect(Collectors.toList()); + } + + private ShippingDestinationResponse convertToShippingDestinationResponse(AddressEntity entity) { + return new ShippingDestinationResponse() + .id(entity.getId()) + .streetAddress(entity.getStreetAddress()) + .addressLocality(entity.getCity()) + .addressRegion(entity.getState()) + .postalCode(entity.getPostalCode()) + .addressCountry(entity.getCountry()); + } + + private List mergeDestinationsPolymorphic( + List knownAddresses, + List requested) { + Map merged = new LinkedHashMap<>(); + if (knownAddresses != null) { + knownAddresses.forEach(k -> merged.put(k.getId(), k)); + } + + if (requested != null) { + for (FulfillmentDestinationResponse req : requested) { + String id = FulfillmentUtils.getId(req); + if (id != null) { + merged.put(id, req); + continue; + } + + // Try to match flat addresses + if (req instanceof ShippingDestinationResponse sdr) { + ShippingDestinationResponse matched = + knownAddresses.stream().filter(k -> isSameAddress(sdr, k)).findFirst().orElse(null); + + if (matched != null) { + sdr.setId(matched.getId()); + } else { + sdr.setId(UUID.randomUUID().toString()); + } + merged.put(sdr.getId(), sdr); + } else { + // For other types, generate ID if missing + // Note: RetailLocationResponse would need cast/set ID if we supported it here + merged.put(UUID.randomUUID().toString(), req); + } + } + } + + return new ArrayList<>(merged.values()); + } + + private boolean isSameAddress(ShippingDestinationResponse a, ShippingDestinationResponse b) { + return Objects.equals(a.getStreetAddress(), b.getStreetAddress()) + && Objects.equals(a.getAddressLocality(), b.getAddressLocality()) + && Objects.equals(a.getAddressRegion(), b.getAddressRegion()) + && Objects.equals(a.getPostalCode(), b.getPostalCode()) + && Objects.equals(a.getAddressCountry(), b.getAddressCountry()); + } + + private List getOptionsForGroup( + FulfillmentMethodResponse fulfillmentMethod, + long currentGrandTotal, + List lineItems, + FulfillmentGroupResponse fulfillmentGroup) { + + String selectedId = + fulfillmentMethod.getSelectedDestinationId() != null + ? fulfillmentMethod.getSelectedDestinationId().orElse(null) + : null; + + if (selectedId != null && fulfillmentMethod.getDestinations() != null) { + return fulfillmentMethod.getDestinations().stream() + .filter(dest -> FulfillmentUtils.getId(dest).equals(selectedId)) + .findFirst() + .map( + dest -> + getAvailableShippingOptions( + FulfillmentUtils.getCountryCode(dest), currentGrandTotal, lineItems)) + .orElseGet(() -> getAvailableShippingOptions("default", currentGrandTotal, lineItems)); + } + + return getAvailableShippingOptions("default", currentGrandTotal, lineItems); + } + + private void populateMethodDetails( + FulfillmentMethodResponse fulfillmentMethod, + long currentGrandTotal, + List lineItems, + CheckoutResponse checkout) { + + for (FulfillmentGroupResponse fulfillmentGroup : fulfillmentMethod.getGroups()) { + fulfillmentGroup.setOptions( + getOptionsForGroup(fulfillmentMethod, currentGrandTotal, lineItems, fulfillmentGroup)); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentUtils.java b/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentUtils.java new file mode 100644 index 0000000..5310d2d --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/FulfillmentUtils.java @@ -0,0 +1,107 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.service.shopping.model.FulfillmentDestinationRequest; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationResponse; +import com.dev.ucp.service.shopping.model.PostalAddress; +import com.dev.ucp.service.shopping.model.RetailLocationResponse; +import com.dev.ucp.service.shopping.model.ShippingDestinationRequest; +import com.dev.ucp.service.shopping.model.ShippingDestinationResponse; + +/** + * Utility class for handling polymorphic UCP fulfillment destinations. + * + *

UCP destinations can be either flat shipping addresses or composed retail locations. This + * utility provides a unified way to extract common data (IDs, addresses, countries) using type + * discrimination. + */ +public class FulfillmentUtils { + + /** + * Extracts the unique identifier from a fulfillment destination. + * + * @param dest the destination response object. + * @return the destination ID, or {@code null} if the type is unknown. + */ + public static String getId(FulfillmentDestinationResponse dest) { + if (dest instanceof ShippingDestinationResponse sdr) { + return sdr.getId(); + } else if (dest instanceof RetailLocationResponse rlr) { + return rlr.getId(); + } + return null; + } + + /** + * Extracts the country code from a fulfillment destination. + * + * @param dest the destination response object. + * @return the ISO country code, or "default" if not specified. + */ + public static String getCountryCode(FulfillmentDestinationResponse dest) { + if (dest instanceof ShippingDestinationResponse sdr) { + return sdr.getAddressCountry() != null ? sdr.getAddressCountry() : "default"; + } else if (dest instanceof RetailLocationResponse rlr && rlr.getAddress() != null) { + return rlr.getAddress().getAddressCountry() != null + ? rlr.getAddress().getAddressCountry() + : "default"; + } + return "default"; + } + + /** + * Extracts or constructs a {@link PostalAddress} from a fulfillment destination. + * + * @param dest the destination response object. + * @return a {@link PostalAddress} representation, or {@code null} if not applicable. + */ + public static PostalAddress getPostalAddress(FulfillmentDestinationResponse dest) { + if (dest instanceof RetailLocationResponse rlr) { + return rlr.getAddress(); + } else if (dest instanceof ShippingDestinationResponse sdr) { + PostalAddress pa = new PostalAddress(); + pa.setStreetAddress(sdr.getStreetAddress()); + pa.setExtendedAddress(sdr.getExtendedAddress()); + pa.setAddressLocality(sdr.getAddressLocality()); + pa.setAddressRegion(sdr.getAddressRegion()); + pa.setAddressCountry(sdr.getAddressCountry()); + pa.setPostalCode(sdr.getPostalCode()); + pa.setFirstName(sdr.getFirstName()); + pa.setLastName(sdr.getLastName()); + pa.setFullName(sdr.getFullName()); + pa.setPhoneNumber(sdr.getPhoneNumber()); + return pa; + } + return null; + } + + /** + * Extracts the unique identifier from a fulfillment destination request. + * + * @param dest the destination request object. + * @return the destination ID, or {@code null} if not provided. + */ + public static String getId(FulfillmentDestinationRequest dest) { + if (dest instanceof ShippingDestinationRequest sdr) { + return sdr.getId(); + } + // RetailLocationRequest usually does not have an ID on create/update, + // it's selected via name or external reference in the spec. + return null; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/HttpDebugLoggingFilter.java b/rest/java-spring/src/main/java/com/dev/ucp/HttpDebugLoggingFilter.java new file mode 100644 index 0000000..15250bf --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/HttpDebugLoggingFilter.java @@ -0,0 +1,98 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +/** + * A debug filter that logs incoming HTTP requests and outgoing responses. + * + *

This class captures the request and response bodies (after they have been processed/converted + * to JSON) and outputs them to the log, allowing developers to inspect the raw data being exchanged + * with the server. + */ +@Component +public class HttpDebugLoggingFilter extends OncePerRequestFilter { + + private static final Logger logger = LoggerFactory.getLogger(HttpDebugLoggingFilter.class); + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request); + ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response); + + try { + filterChain.doFilter(requestWrapper, responseWrapper); + } finally { + // Log after filter chain execution so wrappers are populated + String requestBody = new String(requestWrapper.getContentAsByteArray()); + String responseBody = new String(responseWrapper.getContentAsByteArray()); + + logger.debug("Request Endpoint: {}", requestWrapper.getRequestURI()); + StringBuilder requestHeaders = new StringBuilder(); + java.util.Collections.list(requestWrapper.getHeaderNames()) + .forEach( + headerName -> + java.util.Collections.list(requestWrapper.getHeaders(headerName)) + .forEach( + headerValue -> + requestHeaders + .append(headerName) + .append(": ") + .append(headerValue) + .append("\n"))); + if (requestHeaders.length() > 0) { + logger.debug("Request Headers:\n{}", requestHeaders); + } + logger.debug("Request Body: {}", requestBody); + + StringBuilder responseHeaders = new StringBuilder(); + responseWrapper + .getHeaderNames() + .forEach( + headerName -> + responseWrapper + .getHeaders(headerName) + .forEach( + headerValue -> + responseHeaders + .append(headerName) + .append(": ") + .append(headerValue) + .append("\n"))); + if (responseHeaders.length() > 0) { + logger.debug("Response Headers:\n{}", responseHeaders); + } + logger.debug("Response Body: {}", responseBody); + + // IMPORTANT: Copy content back to original response + responseWrapper.copyBodyToResponse(); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/InventoryManager.java b/rest/java-spring/src/main/java/com/dev/ucp/InventoryManager.java new file mode 100644 index 0000000..3d5640f --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/InventoryManager.java @@ -0,0 +1,112 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.entities.InventoryEntity; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.MessageError; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Manages all inventory-related operations, including validation and stock reservation. This class + * is responsible for checking if items are in stock and for reserving them when a checkout is + * completed. + */ +@Component +public class InventoryManager { + + @Autowired private InventoryEntity.Repository inventoryRepository; + + /** + * Validates the inventory for all line items in the given checkout response. This method checks + * for two main conditions: 1. If inventory information is found for the product. 2. If there is + * sufficient stock for the requested quantity. If any issues are found, appropriate {@link + * MessageError} objects are added to the checkout's messages list. + * + * @param checkout The {@link CheckoutResponse} containing the line items to validate. + */ + public void validate(CheckoutResponse checkout) { + if (checkout.getLineItems() == null) { + return; + } + for (LineItemResponse lineItem : checkout.getLineItems()) { + // It's possible the product wasn't found in a previous step, so the item is null. + if (lineItem.getItem() == null) { + continue; + } + String productId = lineItem.getItem().getId(); + int requestedQuantity = lineItem.getQuantity(); + + Optional inventoryOpt = inventoryRepository.findById(productId); + + if (inventoryOpt.isEmpty()) { + MessageError error = + new MessageError() + .type(MessageError.TypeEnum.ERROR) + .severity(MessageError.SeverityEnum.RECOVERABLE) + .code("inventory_not_found") + .content("Inventory not found for product: " + lineItem.getItem().getTitle()) + .path("$.line_items[?(@.id=='" + lineItem.getId() + "')]"); + checkout.addMessagesItem(error); + continue; + } + + InventoryEntity inventory = inventoryOpt.get(); + if (inventory.getQuantity() < requestedQuantity) { + MessageError error = + new MessageError() + .type(MessageError.TypeEnum.ERROR) + .severity(MessageError.SeverityEnum.RECOVERABLE) + .code("insufficient_stock") + .content("Insufficient stock for item " + lineItem.getItem().getTitle()) + .path("$.line_items[?(@.id=='" + lineItem.getId() + "')]"); + checkout.addMessagesItem(error); + } + } + } + + /** + * Reserves inventory for all line items in the given checkout response. This method decrements + * the stock quantity for each item in the repository. It assumes that inventory has already been + * validated prior to this operation. If a product's stock becomes insufficient during the + * reservation process (which should ideally be prevented by prior validation), an {@link + * IllegalStateException} is thrown. + * + * @param checkout The {@link CheckoutResponse} containing the line items for which to reserve + * inventory. + * @throws IllegalStateException if there is insufficient stock for an item during reservation, + * which indicates a logical error if validation was performed correctly. + */ + public void reserveInventory(CheckoutResponse checkout) { + for (LineItemResponse lineItem : checkout.getLineItems()) { + inventoryRepository + .findById(lineItem.getItem().getId()) + .ifPresent( + inventoryEntity -> { + if (inventoryEntity.getQuantity() < lineItem.getQuantity()) { + throw new IllegalStateException( + "Insufficient stock for item " + lineItem.getItem().getId()); + } + inventoryEntity.setQuantity(inventoryEntity.getQuantity() - lineItem.getQuantity()); + inventoryRepository.save(inventoryEntity); + }); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/JacksonConfig.java b/rest/java-spring/src/main/java/com/dev/ucp/JacksonConfig.java new file mode 100644 index 0000000..4cafdad --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/JacksonConfig.java @@ -0,0 +1,166 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.service.shopping.model.CardCredential; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationRequest; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationResponse; +import com.dev.ucp.service.shopping.model.Message; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.MessageInfo; +import com.dev.ucp.service.shopping.model.MessageWarning; +import com.dev.ucp.service.shopping.model.PaymentCredential; +import com.dev.ucp.service.shopping.model.RetailLocationRequest; +import com.dev.ucp.service.shopping.model.RetailLocationResponse; +import com.dev.ucp.service.shopping.model.ShippingDestinationRequest; +import com.dev.ucp.service.shopping.model.ShippingDestinationResponse; +import com.dev.ucp.service.shopping.model.TokenCredentialResponse; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.module.SimpleModule; +import java.io.IOException; +import org.openapitools.jackson.nullable.JsonNullableModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configures Jackson Object Mapper behaviors for the UCP Spring Demo. + * + *

This configuration handles specialized JSON processing requirements such as: + * + *

+ */ +@Configuration +public class JacksonConfig { + + /** + * Supports the {@code JsonNullable} wrapper used by OpenAPI-generated models. + * + *

This module allows Jackson to distinguish between a field being absent (undefined), + * explicitly set to null, or containing a value. + * + * @return a configured {@link JsonNullableModule}. + */ + @Bean + public Module jsonNullableModule() { + return new JsonNullableModule(); + } + + /** + * Provides a unified module for all custom polymorphic deserializers required by the UCP + * specification. + * + *

Standard Jackson polymorphic handling (using discriminators) is often overridden by the + * generated models or is insufficient for certain UCP union types. This module registers manual + * discrimination logic for: + * + *

+ * + * @return a {@link SimpleModule} containing all UCP polymorphic deserializers. + */ + @Bean + public Module ucpPolymorphicModule() { + SimpleModule module = new SimpleModule(); + + // 1. PaymentCredential Deserializer + module.addDeserializer( + PaymentCredential.class, + new JsonDeserializer() { + @Override + public PaymentCredential deserialize(JsonParser p, DeserializationContext ctxt) + throws IOException { + JsonNode node = p.readValueAsTree(); + if (node.has("card_number_type")) { + try { + return p.getCodec().treeToValue(node, CardCredential.class); + } catch (Exception ignored) { + // Fallback to token + } + } + return p.getCodec().treeToValue(node, TokenCredentialResponse.class); + } + }); + + // 2. Message Deserializer + module.addDeserializer( + Message.class, + new JsonDeserializer() { + @Override + public Message deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.readValueAsTree(); + JsonNode typeNode = node.get("type"); + if (typeNode == null) { + return null; + } + String type = typeNode.asText(); + return switch (type) { + case "error" -> p.getCodec().treeToValue(node, MessageError.class); + case "warning" -> p.getCodec().treeToValue(node, MessageWarning.class); + case "info" -> p.getCodec().treeToValue(node, MessageInfo.class); + default -> throw new IOException("Unknown message type: " + type); + }; + } + }); + + // 3. FulfillmentDestinationResponse Deserializer + module.addDeserializer( + FulfillmentDestinationResponse.class, + new JsonDeserializer() { + @Override + public FulfillmentDestinationResponse deserialize( + JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.readValueAsTree(); + if (node.has("name")) { + return p.getCodec().treeToValue(node, RetailLocationResponse.class); + } + return p.getCodec().treeToValue(node, ShippingDestinationResponse.class); + } + }); + + // 4. FulfillmentDestinationRequest Deserializer + module.addDeserializer( + FulfillmentDestinationRequest.class, + new JsonDeserializer() { + @Override + public FulfillmentDestinationRequest deserialize( + JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.readValueAsTree(); + if (node.has("name")) { + return p.getCodec().treeToValue(node, RetailLocationRequest.class); + } + return p.getCodec().treeToValue(node, ShippingDestinationRequest.class); + } + }); + + return module; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/LineItemConverter.java b/rest/java-spring/src/main/java/com/dev/ucp/LineItemConverter.java new file mode 100644 index 0000000..b5f5fcf --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/LineItemConverter.java @@ -0,0 +1,184 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.entities.ProductEntity; +import com.dev.ucp.exceptions.InvalidRequestException; +import com.dev.ucp.service.shopping.model.ItemCreateRequest; +import com.dev.ucp.service.shopping.model.ItemResponse; +import com.dev.ucp.service.shopping.model.ItemUpdateRequest; +import com.dev.ucp.service.shopping.model.LineItemCreateRequest; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.LineItemUpdateRequest; +import com.dev.ucp.service.shopping.model.TotalResponse; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * A converter class responsible for transforming LineItem request objects into LineItemResponse + * objects. This includes fetching authoritative product data from the database to populate fields + * like price and title. + */ +@Component +public class LineItemConverter { + + @Autowired private ProductEntity.Repository productRepository; + + /** + * Converts a list of {@link LineItemCreateRequest} objects into a list of {@link + * LineItemResponse} objects, ensuring that a new unique ID is generated for each line item. + * + *

This method processes each line item individually, converting its associated item data by + * fetching authoritative product information. + * + * @param lineItems The list of {@link LineItemCreateRequest} objects to convert. + * @return A list of fully populated {@link LineItemResponse} objects. + */ + public List convertFromCreateRequest(List lineItems) { + return convertLineItems( + lineItems, + r -> null, // Always generate a new ID for create requests + LineItemCreateRequest::getQuantity, + LineItemCreateRequest::getItem, + ItemCreateRequest::getId); + } + + /** + * Converts a list of {@link LineItemUpdateRequest} objects into a list of {@link + * LineItemResponse} objects, preserving the existing line item ID if provided. + * + * @param lineItems The list of {@link LineItemUpdateRequest} objects to convert. + * @return A list of fully populated {@link LineItemResponse} objects. + */ + public List convertFromUpdateRequest(List lineItems) { + return convertLineItems( + lineItems, + LineItemUpdateRequest::getId, // Use existing ID if present + LineItemUpdateRequest::getQuantity, + LineItemUpdateRequest::getItem, + ItemUpdateRequest::getId); + } + + /** + * Converts a list of generic line item request objects into a list of {@link LineItemResponse} + * objects. This method processes each line item individually, converting its associated item data + * by fetching authoritative product information. + * + * @param lineItems The list of generic line item request objects. + * @param lineItemIdExtractor A function to extract the line item ID from the request object. + * @param quantityExtractor A function to extract the quantity from the request object. + * @param itemExtractor A function to extract the item request object from the line item request. + * @param itemIdExtractor A function to extract the item ID from the item request object. + * @param

  • The type of the line item request object. + * @param The type of the item request object within the line item request. + * @return A list of {@link LineItemResponse} objects, fully populated with product details. + * @throws RuntimeException if a {@link URISyntaxException} occurs during item conversion + * (internal error). + */ + private List convertLineItems( + List
  • lineItems, + Function lineItemIdExtractor, + Function quantityExtractor, + Function itemExtractor, + Function itemIdExtractor) { + if (lineItems == null) { + return new ArrayList<>(); + } + return lineItems.stream() + .map( + item -> { + try { + return convertLineItem( + item, lineItemIdExtractor, quantityExtractor, itemExtractor, itemIdExtractor); + } catch (URISyntaxException e) { + // This is an internal error, so we can still throw a runtime exception + throw new RuntimeException("Error converting line item", e); + } + }) + .collect(Collectors.toList()); + } + + /** + * Converts a generic line item request object into a {@link LineItemResponse} object. It extracts + * details from the request and populates the response, including fetching authoritative product + * details for the associated item. + * + * @param req The generic line item request object. + * @param lineItemIdExtractor A function to extract the line item ID from the request object. + * @param quantityExtractor A function to extract the quantity from the request object. + * @param itemExtractor A function to extract the item request object from the line item request. + * @param itemIdExtractor A function to extract the item ID from the item request object. + * @param
  • The type of the line item request object. + * @param The type of the item request object within the line item request. + * @return A {@link LineItemResponse} object, populated with details from the request and product + * data. + * @throws URISyntaxException if a URI for the item image is invalid. + * @throws InvalidRequestException if the product associated with the item ID is not found. + */ + private LineItemResponse convertLineItem( + LI req, + Function lineItemIdExtractor, + Function quantityExtractor, + Function itemExtractor, + Function itemIdExtractor) + throws URISyntaxException { + LineItemResponse resp = new LineItemResponse(); + String lineItemId = lineItemIdExtractor.apply(req); + if (lineItemId == null) { + lineItemId = UUID.randomUUID().toString(); + } + resp.setId(lineItemId); + resp.setQuantity(quantityExtractor.apply(req)); + I item = itemExtractor.apply(req); + String itemId = itemIdExtractor.apply(item); + + Optional productOpt = productRepository.findById(itemId); + if (productOpt.isEmpty()) { + // TODO: This should be a MessageError instead. But current spec marks the title and price as + // required, so we have to throw an exception. + throw new InvalidRequestException("Product not found: " + itemId); + } + ProductEntity product = productOpt.get(); + + ItemResponse itemResponse = new ItemResponse(); + itemResponse.setId(product.getId()); + itemResponse.setTitle(product.getTitle()); + itemResponse.setPrice(product.getPrice()); + itemResponse.setImageUrl(new URI("https://example.com/image.png")); // Dummy image URL + resp.setItem(itemResponse); + + long baseAmount = (long) itemResponse.getPrice() * resp.getQuantity(); + List totals = new ArrayList<>(); + totals.add( + new TotalResponse() + .type(TotalResponse.TypeEnum.SUBTOTAL) + .amount(Math.toIntExact(baseAmount))); + totals.add( + new TotalResponse().type(TotalResponse.TypeEnum.TOTAL).amount(Math.toIntExact(baseAmount))); + resp.setTotals(totals); + + return resp; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/OrderWebHookNotifier.java b/rest/java-spring/src/main/java/com/dev/ucp/OrderWebHookNotifier.java new file mode 100644 index 0000000..8936378 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/OrderWebHookNotifier.java @@ -0,0 +1,259 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.Expectation; +import com.dev.ucp.service.shopping.model.ExpectationLineItemsInner; +import com.dev.ucp.service.shopping.model.FulfillmentEvent; +import com.dev.ucp.service.shopping.model.FulfillmentEventLineItemsInner; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.dev.ucp.service.shopping.model.OrderEventWebhookRequest; +import com.dev.ucp.service.shopping.model.OrderFulfillment; +import com.dev.ucp.service.shopping.model.OrderLineItem; +import com.dev.ucp.service.shopping.model.OrderLineItemQuantity; +import com.dev.ucp.service.shopping.model.UCPOrderResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.TextNode; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; + +/** + * Handles asynchronous webhook notifications for order-related events. + * + *

    This component is responsible for orchestrating the simulated order lifecycle after a checkout + * is completed. It sends an initial 'order_placed' event and then waits for an external signal (via + * {@link #triggerShipping(String)}) to send a subsequent 'order_shipped' event. + * + *

    Synchronization between the asynchronous notification thread and the simulation trigger is + * managed using a {@link ConcurrentHashMap} of {@link CountDownLatch} objects. + */ +@Component +public class OrderWebHookNotifier { + private static final Logger logger = Logger.getLogger(OrderWebHookNotifier.class.getName()); + + private final RestClient restClient; + @Autowired private ObjectMapper objectMapper; + @Autowired private ExtensionsHelper extensionsHelper; + + private final Map shipmentLatches = new ConcurrentHashMap<>(); + + public OrderWebHookNotifier(RestClient.Builder restClientBuilder) { + this.restClient = restClientBuilder.build(); + } + + /** + * Initiates the order notification lifecycle for a completed checkout. + * + *

    This method sends an 'order_placed' webhook immediately and then enters a waiting state + * until a shipping trigger is received or a timeout occurs (60 seconds). Once triggered, it sends + * an 'order_shipped' webhook with mock tracking details. + * + * @param webhookUrl the destination URL for the webhook events. + * @param checkout the completed checkout session data. + */ + @Async + public void notify(String webhookUrl, CheckoutResponse checkout) { + if (webhookUrl == null || webhookUrl.isEmpty()) { + logger.warning("No webhook URL provided, skipping notification."); + return; + } + + String orderId = checkout.getOrder().getId(); + OrderEventWebhookRequest request = createBaseRequest(checkout); + + // 1. Send order_placed + request.putAdditionalProperty("event_type", new TextNode("order_placed")); + sendWebhook(webhookUrl, request); + + // 2. Wait for trigger for order_shipped + CountDownLatch latch = shipmentLatches.computeIfAbsent(orderId, k -> new CountDownLatch(1)); + + try { + logger.info("Waiting for shipping trigger for order: " + orderId); + // Wait up to 60 seconds for external trigger + if (!latch.await(60, TimeUnit.SECONDS)) { + logger.warning("Timed out waiting for shipping trigger for order: " + orderId); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warning("Interrupted while waiting for shipping trigger."); + } finally { + shipmentLatches.remove(orderId); + } + + // 3. Send order_shipped + request.setEventId(UUID.randomUUID().toString()); + request.setCreatedTime(OffsetDateTime.now()); + request.putAdditionalProperty("event_type", new TextNode("order_shipped")); + addMockShippingEvent(request); + sendWebhook(webhookUrl, request); + } + + /** + * Signal the notification thread to proceed with sending the 'order_shipped' event. + * + * @param orderId the unique identifier of the order to be shipped. + */ + public void triggerShipping(String orderId) { + CountDownLatch latch = shipmentLatches.computeIfAbsent(orderId, k -> new CountDownLatch(1)); + logger.info("Triggering shipping for order: " + orderId); + latch.countDown(); + } + + private void sendWebhook(String url, OrderEventWebhookRequest payload) { + try { + String jsonBody = objectMapper.writeValueAsString(payload); + logger.info("Sending webhook to " + url + " with body: " + jsonBody); + restClient + .post() + .uri(url) + .contentType(MediaType.APPLICATION_JSON) + .body(jsonBody) + .retrieve() + .toBodilessEntity(); + logger.info("Webhook delivered successfully."); + } catch (Exception e) { + logger.severe("Failed to deliver webhook to " + url + ": " + e.getMessage()); + } + } + + private void addMockShippingEvent(OrderEventWebhookRequest request) { + if (request.getLineItems() == null) { + return; + } + + for (OrderLineItem oli : request.getLineItems()) { + oli.setStatus(OrderLineItem.StatusEnum.FULFILLED); + if (oli.getQuantity() != null) { + oli.getQuantity().setFulfilled(oli.getQuantity().getTotal()); + } + } + + FulfillmentEvent event = new FulfillmentEvent(); + event.setId(UUID.randomUUID().toString()); + event.setOccurredAt(OffsetDateTime.now()); + event.setType("shipped"); + event.setCarrier("MockCarrier"); + event.setTrackingNumber("MOCK123456789"); + try { + event.setTrackingUrl(new URI("https://example.com/track/MOCK123456789")); + } catch (Exception ignored) {} + + List fulfillmentLineItems = new ArrayList<>(); + for (OrderLineItem oli : request.getLineItems()) { + FulfillmentEventLineItemsInner feli = new FulfillmentEventLineItemsInner(); + feli.setId(oli.getId()); + if (oli.getQuantity() != null) { + feli.setQuantity(oli.getQuantity().getTotal()); + } + fulfillmentLineItems.add(feli); + } + event.setLineItems(fulfillmentLineItems); + + if (request.getFulfillment() == null) { + request.setFulfillment(new OrderFulfillment()); + } + request.getFulfillment().addEventsItem(event); + } + + private OrderEventWebhookRequest createBaseRequest(CheckoutResponse checkout) { + OrderEventWebhookRequest request = new OrderEventWebhookRequest(); + + UCPOrderResponse ucp = new UCPOrderResponse(); + ucp.setVersion(checkout.getUcp().getVersion()); + request.setUcp(ucp); + + request.setId(checkout.getOrder().getId()); + request.setCheckoutId(checkout.getId()); + request.setPermalinkUrl(checkout.getOrder().getPermalinkUrl()); + + List orderLineItems = new ArrayList<>(); + if (checkout.getLineItems() != null) { + for (var li : checkout.getLineItems()) { + OrderLineItem oli = new OrderLineItem(); + oli.setId(li.getId()); + oli.setItem(li.getItem()); + + OrderLineItemQuantity qty = new OrderLineItemQuantity(); + qty.setTotal(li.getQuantity()); + qty.setFulfilled(0); + oli.setQuantity(qty); + + oli.setStatus(OrderLineItem.StatusEnum.PROCESSING); + oli.setTotals(li.getTotals()); + orderLineItems.add(oli); + } + } + request.setLineItems(orderLineItems); + + OrderFulfillment fulfillment = new OrderFulfillment(); + FulfillmentResponse checkoutFulfillment = extensionsHelper.getFulfillmentResponse(checkout); + if (checkoutFulfillment != null && checkoutFulfillment.getMethods() != null) { + for (var method : checkoutFulfillment.getMethods()) { + Expectation expectation = new Expectation(); + expectation.setId(UUID.randomUUID().toString()); + expectation.setMethodType( + Expectation.MethodTypeEnum.fromValue(method.getType().getValue())); + + // Find selected destination + if (method.getSelectedDestinationId() != null + && method.getSelectedDestinationId().isPresent() + && method.getDestinations() != null) { + String selectedId = method.getSelectedDestinationId().get(); + for (var dest : method.getDestinations()) { + if (selectedId.equals(FulfillmentUtils.getId(dest))) { + expectation.setDestination(FulfillmentUtils.getPostalAddress(dest)); + break; + } + } + } + // Map line items to expectation + List expectationLineItems = new ArrayList<>(); + if (checkout.getLineItems() != null) { + for (var li : checkout.getLineItems()) { + ExpectationLineItemsInner eli = new ExpectationLineItemsInner(); + eli.setId(li.getId()); + eli.setQuantity(li.getQuantity()); + expectationLineItems.add(eli); + } + } + expectation.setLineItems(expectationLineItems); + fulfillment.addExpectationsItem(expectation); + } + } + request.setFulfillment(fulfillment); + request.setTotals(checkout.getTotals()); + request.setEventId(UUID.randomUUID().toString()); + request.setCreatedTime(OffsetDateTime.now()); + + return request; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/PlatformProfileResolver.java b/rest/java-spring/src/main/java/com/dev/ucp/PlatformProfileResolver.java new file mode 100644 index 0000000..25650c8 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/PlatformProfileResolver.java @@ -0,0 +1,77 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import com.fasterxml.jackson.databind.JsonNode; +import java.util.Optional; +import java.util.logging.Logger; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; + +/** + * Utility for resolving platform UCP profiles. + * + *

    Retrieves the discovery profile from the platform to determine capability endpoints and + * configuration. + */ +@Component +public class PlatformProfileResolver { + private static final Logger logger = Logger.getLogger(PlatformProfileResolver.class.getName()); + private final RestClient restClient; + + public PlatformProfileResolver(RestClient.Builder restClientBuilder) { + this.restClient = restClientBuilder.build(); + } + + /** + * Fetches the platform's UCP profile and extracts the webhook URL for the order capability. + * + * @param profileUri The URI of the platform's profile. + * @return An Optional containing the webhook URL if found. + */ + public Optional resolveWebhookUrl(String profileUri) { + if (profileUri == null || profileUri.isEmpty()) { + return Optional.empty(); + } + + try { + logger.info("Fetching platform profile from: " + profileUri); + JsonNode profile = restClient.get().uri(profileUri).retrieve().body(JsonNode.class); + + if (profile == null) { + return Optional.empty(); + } + + JsonNode capabilities = profile.at("/ucp/capabilities"); + if (capabilities.isArray()) { + for (JsonNode cap : capabilities) { + if ("dev.ucp.shopping.order".equals(cap.path("name").asText())) { + String url = cap.at("/config/webhook_url").asText(); + if (url != null && !url.isEmpty()) { + logger.info("Resolved webhook URL: " + url); + return Optional.of(url); + } + } + } + } + } catch (Exception e) { + logger.warning( + "Failed to resolve platform profile from " + profileUri + ": " + e.getMessage()); + } + return Optional.empty(); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/SpringDemoApp.java b/rest/java-spring/src/main/java/com/dev/ucp/SpringDemoApp.java new file mode 100644 index 0000000..8101cdb --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/SpringDemoApp.java @@ -0,0 +1,42 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.web.client.RestClient; + +/** Main entry point for the Spring Demo application. */ +@SpringBootApplication +@EnableAsync +@EnableJpaRepositories(considerNestedRepositories = true) +public class SpringDemoApp { + + public static void main(String[] args) { + SpringApplication.run(SpringDemoApp.class, args); + } + + @Bean + public RestClient.Builder restClientBuilder() { + return RestClient.builder().requestFactory(new SimpleClientHttpRequestFactory()); + } + +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseAction.java new file mode 100644 index 0000000..4da82c6 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseAction.java @@ -0,0 +1,116 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.entities.IdempotencyRecordEntity; +import com.dev.ucp.exceptions.IdempotencyConflictException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +/** + * Base class for all checkout-related actions, providing common functionality for idempotency + * handling and request hashing. + * + * @param The type of the request payload. + * @param The type of the response payload. + */ +@Transactional +public abstract class BaseAction { + + protected final Logger logger = Logger.getLogger(this.getClass().getName()); + + @Autowired protected ObjectMapper objectMapper; + + @Autowired private IdempotencyRecordEntity.Repository idempotencyRecordRepository; + + protected abstract R executeAction(T request); + + public R execute(UUID idempotencyKey, T request) { + if (idempotencyKey == null) { + return executeAction(request); + } + try { + String requestPayload = objectMapper.writeValueAsString(request); + String requestHash = computeHash(requestPayload); + + Optional existingRecordOpt = + idempotencyRecordRepository.findById(idempotencyKey.toString()); + + if (existingRecordOpt.isPresent()) { + IdempotencyRecordEntity existingRecord = existingRecordOpt.get(); + if (!existingRecord.getRequestHash().equals(requestHash)) { + throw new IdempotencyConflictException( + "Idempotency key reused with different parameters"); + } + logger.info( + String.format( + "Returning cached response for idempotency key: %s, request hash: %s", + idempotencyKey.toString(), requestHash)); + return objectMapper.readValue(existingRecord.getResponseBody(), getResponseType()); + } + + R response = executeAction(request); + + String responseBody = objectMapper.writeValueAsString(response); + + saveIdempotencyRecord(idempotencyKey.toString(), requestHash, 201, responseBody); + + return response; + } catch (JsonProcessingException | NoSuchAlgorithmException e) { + throw new RuntimeException("Error processing request", e); + } + } + + private String computeHash(String data) throws NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8)); + StringBuilder hexString = new StringBuilder(); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + private void saveIdempotencyRecord( + String key, String requestHash, int responseStatus, String responseBody) { + IdempotencyRecordEntity record = new IdempotencyRecordEntity(); + record.setKey(key); + record.setRequestHash(requestHash); + record.setResponseStatus(responseStatus); + record.setResponseBody(responseBody); + record.setCreatedAt(OffsetDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)); + idempotencyRecordRepository.save(record); + } + + protected abstract String getEndpoint(); + + protected abstract Class getResponseType(); +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseCheckoutAction.java new file mode 100644 index 0000000..761c439 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/BaseCheckoutAction.java @@ -0,0 +1,281 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.ExtensionsHelper; +import com.dev.ucp.FulfillmentManager; +import com.dev.ucp.LineItemConverter; +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.entities.DiscountEntity; +import com.dev.ucp.exceptions.CheckoutNotModifiableException; +import com.dev.ucp.exceptions.ResourceNotFoundException; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.DiscountRequest; +import com.dev.ucp.service.shopping.model.DiscountRequestAppliedInner; +import com.dev.ucp.service.shopping.model.DiscountResponse; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.Message; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.PaymentHandlerResponse; +import com.dev.ucp.service.shopping.model.PaymentResponse; +import com.dev.ucp.service.shopping.model.TotalResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; + +/** + * Base class for actions specifically targeting the UCP checkout session lifecycle. + * + *

    Provides centralized logic for loading/saving checkout sessions, validating session state, + * recalculating totals, and managing checkout status based on validation errors. + * + * @param The type of the request payload. + */ +public abstract class BaseCheckoutAction extends BaseAction { + + @Autowired protected CheckoutSessionEntity.Repository checkoutSessionRepository; + @Autowired protected DiscountEntity.Repository discountRepository; + @Autowired protected ExtensionsHelper extensionsHelper; + @Autowired protected LineItemConverter lineItemConverter; + @Autowired protected FulfillmentManager fulfillmentManager; + + @Override + protected Class getResponseType() { + return CheckoutResponse.class; + } + + protected void ensureModifiable(CheckoutResponse checkout) { + if (checkout.getStatus() == CheckoutResponse.StatusEnum.COMPLETED + || checkout.getStatus() == CheckoutResponse.StatusEnum.CANCELED) { + throw new CheckoutNotModifiableException( + "Cannot update checkout in state " + checkout.getStatus()); + } + } + + protected CheckoutResponse loadCheckoutSession(String checkoutId) { + CheckoutSessionEntity entity = + checkoutSessionRepository + .findById(checkoutId) + .orElseThrow(() -> new ResourceNotFoundException("Checkout session not found")); + try { + return objectMapper.readValue(entity.getData(), CheckoutResponse.class); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing checkout data", e); + } + } + + protected void saveCheckoutSession(String id, String status, CheckoutResponse checkoutResponse) { + CheckoutSessionEntity entity = + checkoutSessionRepository.findById(id).orElse(new CheckoutSessionEntity()); + entity.setId(id); + entity.setStatus(status); + try { + entity.setData(objectMapper.writeValueAsString(checkoutResponse)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing checkout response", e); + } + checkoutSessionRepository.save(entity); + } + + protected PaymentResponse convertPayment( + CheckoutResponse checkout, + List instruments, + String selectedInstrumentId) { + PaymentResponse resp = new PaymentResponse(); + resp.setSelectedInstrumentId(selectedInstrumentId); + + // Load handlers from discovery_profile.json + try (InputStream inputStream = + new ClassPathResource("discovery_profile.json").getInputStream()) { + JsonNode discoveryProfile = objectMapper.readTree(inputStream); + JsonNode paymentNode = discoveryProfile.get("payment"); + if (paymentNode != null && paymentNode.has("handlers")) { + List handlers = new ArrayList<>(); + for (JsonNode handlerNode : paymentNode.get("handlers")) { + handlers.add(objectMapper.treeToValue(handlerNode, PaymentHandlerResponse.class)); + } + resp.setHandlers(handlers); + } + } catch (IOException e) { + throw new RuntimeException("Error reading discovery_profile.json for payment handlers", e); + } + + resp.setInstruments(instruments); + + return resp; + } + + /** + * Initializes the totals for the checkout session with a subtotal calculated from the line items. + * + * @param checkout The {@link CheckoutResponse} object to initialize totals for. + */ + protected void initTotals(CheckoutResponse checkout) { + long subtotal = 0; + if (checkout.getLineItems() != null) { + subtotal = + checkout.getLineItems().stream() + .filter(li -> li.getTotals() != null) + .flatMap(li -> li.getTotals().stream()) + .filter(t -> t.getType() == TotalResponse.TypeEnum.SUBTOTAL) + .mapToLong(TotalResponse::getAmount) + .sum(); + } + List totals = new ArrayList<>(); + totals.add( + new TotalResponse() + .type(TotalResponse.TypeEnum.SUBTOTAL) + .amount(Math.toIntExact(subtotal))); + checkout.setTotals(totals); + } + + /** + * Recalculates the totals for the checkout session, including line item subtotals, grand total, + * and applying discounts. This method relies on the {@link LineItemResponse} objects in the + * checkout already having authoritative pricing and title information. + * + * @param checkout The {@link CheckoutResponse} object to recalculate totals for. + * @param discountRequest The {@link DiscountRequest} containing any discount codes to apply. + */ + protected void recalculateTotals(CheckoutResponse checkout, DiscountRequest discountRequest) { + long currentGrandTotal = 0; + for (LineItemResponse lineItem : checkout.getLineItems()) { + if (lineItem.getTotals() != null) { + for (TotalResponse total : lineItem.getTotals()) { + if (total.getType() == TotalResponse.TypeEnum.SUBTOTAL) { + currentGrandTotal += total.getAmount(); + } + } + } + } + + checkout.setTotals(new ArrayList<>()); + checkout.addTotalsItem( + new TotalResponse() + .type(TotalResponse.TypeEnum.SUBTOTAL) + .amount(Math.toIntExact(currentGrandTotal))); + + DiscountResponse discountResponse = new DiscountResponse(); + + if (discountRequest != null && discountRequest.getCodes() != null) { + for (String code : discountRequest.getCodes()) { + Optional discountEntityOpt = discountRepository.findById(code); + if (discountEntityOpt.isPresent()) { + DiscountEntity discountEntity = discountEntityOpt.get(); + long discountAmount = 0; + if ("percentage".equals(discountEntity.getType())) { + discountAmount = currentGrandTotal * discountEntity.getValue() / 100; + } else if ("fixed_amount".equals(discountEntity.getType())) { + discountAmount = discountEntity.getValue(); + } + + if (discountAmount > 0) { + currentGrandTotal -= discountAmount; + checkout.addTotalsItem( + new TotalResponse() + .type(TotalResponse.TypeEnum.DISCOUNT) + .amount(Math.toIntExact(discountAmount))); + if (discountResponse.getApplied() == null) { + discountResponse.setApplied(new ArrayList<>()); + } + discountResponse + .getApplied() + .add( + new DiscountRequestAppliedInner() + .code(code) + .title(discountEntity.getDescription()) + .amount(Math.toIntExact(discountAmount))); + } + } + } + } + + long fulfillmentCost = fulfillmentManager.recalculateFulfillmentTotals(checkout); + if (fulfillmentCost > 0) { + checkout.addTotalsItem( + new TotalResponse() + .type(TotalResponse.TypeEnum.FULFILLMENT) + .amount(Math.toIntExact(fulfillmentCost))); + currentGrandTotal += fulfillmentCost; + } + + checkout.addTotalsItem( + new TotalResponse() + .type(TotalResponse.TypeEnum.TOTAL) + .amount(Math.toIntExact(currentGrandTotal))); + extensionsHelper.setDiscount(checkout, discountResponse); + } + + /** + * Updates the checkout session's status based on the messages present in the checkout response. + * The status is determined by the highest severity message: + * + *

    - If any message has a severity of `REQUIRES_BUYER_INPUT` or `REQUIRES_BUYER_REVIEW`, the + * status is set to `REQUIRES_ESCALATION`. + * + *

    - Otherwise, if any message is an `ERROR` with `RECOVERABLE` severity, the status is set to + * `INCOMPLETE`. + * + *

    - If no error messages are present (only informational or warning messages, or no messages + * at all), the status defaults to `READY_FOR_COMPLETE`. + * + * @param checkout The {@link CheckoutResponse} object whose status is to be updated. + */ + protected void updateStatus(CheckoutResponse checkout) { + // Default to ready, this will be the status if no errors are found. + checkout.setStatus(CheckoutResponse.StatusEnum.READY_FOR_COMPLETE); + + if (checkout.getMessages() == null || checkout.getMessages().isEmpty()) { + return; + } + + for (Message message : checkout.getMessages()) { + if (message instanceof MessageError error) { + + // Escalation errors take the highest precedence. + if (requiresEscalation(error)) { + checkout.setStatus(CheckoutResponse.StatusEnum.REQUIRES_ESCALATION); + return; // Exit immediately, as this is the highest priority status. + } + + // Any other error makes the checkout incomplete. + checkout.setStatus(CheckoutResponse.StatusEnum.INCOMPLETE); + } + } + } + + /** + * Checks if a given {@link MessageError} indicates a requirement for buyer escalation. This is + * true if the error's severity is either `REQUIRES_BUYER_INPUT` or `REQUIRES_BUYER_REVIEW`. + * + * @param error The {@link MessageError} to check. + * @return `true` if the error requires buyer escalation, `false` otherwise. + */ + private boolean requiresEscalation(MessageError error) { + return error.getSeverity() == MessageError.SeverityEnum.REQUIRES_BUYER_INPUT + || error.getSeverity() == MessageError.SeverityEnum.REQUIRES_BUYER_REVIEW; + } + + +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/CancelCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/CancelCheckoutAction.java new file mode 100644 index 0000000..5248000 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/CancelCheckoutAction.java @@ -0,0 +1,44 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import java.util.UUID; +import org.springframework.stereotype.Service; + +/** Service for canceling a checkout session. */ +@Service +public class CancelCheckoutAction extends BaseCheckoutAction { + + public CheckoutResponse cancelCheckout(String checkoutId, UUID idempotencyKey) { + return execute(idempotencyKey, checkoutId); + } + + @Override + protected CheckoutResponse executeAction(String checkoutId) { + CheckoutResponse checkout = loadCheckoutSession(checkoutId); + ensureModifiable(checkout); + checkout.setStatus(CheckoutResponse.StatusEnum.CANCELED); + saveCheckoutSession(checkoutId, checkout.getStatus().toString(), checkout); + return checkout; + } + + @Override + protected String getEndpoint() { + return "/checkout-sessions/{id}/cancel"; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/CompleteCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/CompleteCheckoutAction.java new file mode 100644 index 0000000..8a08ff0 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/CompleteCheckoutAction.java @@ -0,0 +1,204 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.ExtensionsHelper; +import com.dev.ucp.InventoryManager; +import com.dev.ucp.OrderWebHookNotifier; +import com.dev.ucp.entities.OrderEntity; +import com.dev.ucp.exceptions.InvalidRequestException; +import com.dev.ucp.exceptions.PaymentFailedException; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CompleteCheckoutRequest; +import com.dev.ucp.service.shopping.model.FulfillmentGroupResponse; +import com.dev.ucp.service.shopping.model.FulfillmentMethodResponse; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.OrderConfirmation; +import com.dev.ucp.service.shopping.model.PaymentCredential; +import com.dev.ucp.service.shopping.model.TokenCredentialResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Service for completing a checkout session, finalizing payment and creating an order. */ +@Service +public class CompleteCheckoutAction + extends BaseCheckoutAction> { + + @Autowired private OrderEntity.Repository orderRepository; + @Autowired private ExtensionsHelper extensionsHelper; + @Autowired private InventoryManager inventoryManager; + @Autowired private OrderWebHookNotifier webhookNotifier; + + public CheckoutResponse completeCheckout( + String checkoutId, + UUID idempotencyKey, + CompleteCheckoutRequest completeCheckoutRequest, + String webhookUrl) { + CheckoutResponse checkout = + execute(idempotencyKey, Map.entry(checkoutId, completeCheckoutRequest)); + webhookNotifier.notify(webhookUrl, checkout); + return checkout; + } + + @Override + protected CheckoutResponse executeAction(Map.Entry params) { + String checkoutId = params.getKey(); + CompleteCheckoutRequest request = params.getValue(); + CheckoutResponse checkout = loadCheckoutSession(checkoutId); + ensureModifiable(checkout); + + if (checkout.getStatus() != CheckoutResponse.StatusEnum.READY_FOR_COMPLETE) { + throw new InvalidRequestException("Checkout is not in ready to complete."); + } + + processPayment(request); + inventoryManager.reserveInventory(checkout); + updateStatus(checkout); + + if (checkout.getStatus() != CheckoutResponse.StatusEnum.READY_FOR_COMPLETE) { + saveCheckoutSession(checkoutId, checkout.getStatus().toString(), checkout); + return checkout; + } + + checkout.setStatus(CheckoutResponse.StatusEnum.COMPLETED); + String orderId = UUID.randomUUID().toString(); + OrderConfirmation orderConfirmation = new OrderConfirmation(); + orderConfirmation.setId(orderId); + try { + orderConfirmation.setPermalinkUrl(new URI("https://example.com/orders/" + orderId)); + } catch (URISyntaxException e) { + throw new RuntimeException("Error creating permalink URL", e); + } + checkout.setOrder(orderConfirmation); + + saveCheckoutSession(checkoutId, checkout.getStatus().toString(), checkout); + saveOrder(orderId, checkout); + + return checkout; + } + + private void processPayment(CompleteCheckoutRequest request) { + CardPaymentInstrument instrument = request.getPaymentData(); + if (instrument == null) { + throw new InvalidRequestException("Missing payment data"); + } + + String handlerId = instrument.getHandlerId(); + PaymentCredential credential = instrument.getCredential(); + + if ("mock_payment_handler".equals(handlerId)) { + if (credential instanceof TokenCredentialResponse tokenCredential) { + JsonNode tokenNode = tokenCredential.getAdditionalProperty("token"); + if (tokenNode != null && "fail_token".equals(tokenNode.asText())) { + throw new PaymentFailedException("Payment Failed: Insufficient Funds (Mock)"); + } + } + } + } + + private void validateFulfillment(CheckoutResponse checkout) { + FulfillmentResponse fulfillment = extensionsHelper.getFulfillmentResponse(checkout); + boolean hasFulfillmentErrors = false; + + if (fulfillment == null + || fulfillment.getMethods() == null + || fulfillment.getMethods().isEmpty()) { + MessageError error = + new MessageError() + .type(MessageError.TypeEnum.ERROR) + .severity(MessageError.SeverityEnum.RECOVERABLE) + .code("missing_fulfillment_info") + .content("Fulfillment address and option must be selected") + .path("$.fulfillment"); + checkout.addMessagesItem(error); + hasFulfillmentErrors = true; + } else { + boolean optionSelected = false; + for (FulfillmentMethodResponse method : fulfillment.getMethods()) { + // If shipping type, check if selectedDestinationId is present + if ("shipping".equals(method.getType()) && method.getSelectedDestinationId() == null) { + // This is a potential error, but we want to collect all errors, so we don't return here. + // Instead, we will add an error message and continue. + MessageError error = + new MessageError() + .type(MessageError.TypeEnum.ERROR) + .severity(MessageError.SeverityEnum.RECOVERABLE) + .code("missing_shipping_destination") + .content("Shipping destination must be selected for method: " + method.getId()) + .path( + "$.fulfillment.methods[?(@.id=='" + + method.getId() + + "')].selected_destination_id"); + checkout.addMessagesItem(error); + hasFulfillmentErrors = true; + } + + // Check if an option is selected within any group for this method + if (method.getGroups() != null) { + for (FulfillmentGroupResponse group : method.getGroups()) { + if (group.getSelectedOptionId() != null) { + optionSelected = true; + break; + } + } + } + } + // If no option was selected across all methods/groups and no specific destination error was + // already flagged + if (!optionSelected && !hasFulfillmentErrors) { + MessageError error = + new MessageError() + .type(MessageError.TypeEnum.ERROR) + .severity(MessageError.SeverityEnum.RECOVERABLE) + .code("missing_fulfillment_option") + .content("At least one fulfillment option must be selected.") + .path("$.fulfillment.methods"); // General path if no specific method is missing + // options + checkout.addMessagesItem(error); + hasFulfillmentErrors = true; + } + } + + if (hasFulfillmentErrors) { + checkout.setStatus(CheckoutResponse.StatusEnum.INCOMPLETE); + } + } + + private void saveOrder(String id, CheckoutResponse checkoutResponse) { + OrderEntity entity = new OrderEntity(); + entity.setId(id); + try { + entity.setData(objectMapper.writeValueAsString(checkoutResponse)); + } catch (JsonProcessingException e) { + throw new RuntimeException("Error processing checkout response", e); + } + orderRepository.save(entity); + } + + @Override + protected String getEndpoint() { + return "/checkout-sessions/{id}/complete"; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/CreateCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/CreateCheckoutAction.java new file mode 100644 index 0000000..8bca652 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/CreateCheckoutAction.java @@ -0,0 +1,107 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.ExtensionsHelper; +import com.dev.ucp.InventoryManager; +import com.dev.ucp.LineItemConverter; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.PaymentCreateRequest; +import com.dev.ucp.service.shopping.model.PaymentResponse; +import com.dev.ucp.service.shopping.model.UCPCheckoutResponse; +import com.fasterxml.jackson.databind.JsonNode; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** Service for creating a new checkout session. */ +@Service +@Transactional +public class CreateCheckoutAction extends BaseCheckoutAction { + + @Autowired private ExtensionsHelper extensionsHelper; + @Autowired private InventoryManager inventoryManager; + @Autowired private LineItemConverter lineItemConverter; + + public CheckoutResponse createCheckoutSession( + UUID idempotencyKey, CheckoutCreateRequest request) { + return execute(idempotencyKey, request); + } + + @Override + protected CheckoutResponse executeAction(CheckoutCreateRequest request) { + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setUcp(createUcpCheckoutResponse()); + checkout.setId(UUID.randomUUID().toString()); // A unique ID for the checkout session itself + + checkout.setBuyer(request.getBuyer()); + checkout.setCurrency(request.getCurrency()); + checkout.setPayment(convertPayment(checkout, request.getPayment())); + checkout.setLineItems(lineItemConverter.convertFromCreateRequest(request.getLineItems())); + initTotals(checkout); + inventoryManager.validate(checkout); + + fulfillmentManager.handleFulfillment(checkout, extensionsHelper.getFulfillmentRequest(request)); + recalculateTotals(checkout, extensionsHelper.getDiscount(request)); + updateStatus(checkout); + + saveCheckoutSession(checkout.getId(), checkout.getStatus().toString(), checkout); + + return checkout; + } + + private PaymentResponse convertPayment( + CheckoutResponse checkout, PaymentCreateRequest paymentRequest) { + List instruments = new ArrayList<>(); + String selectedInstrumentId = null; + if (paymentRequest != null) { + selectedInstrumentId = paymentRequest.getSelectedInstrumentId(); + if (paymentRequest.getInstruments() != null) { + instruments.addAll(paymentRequest.getInstruments()); + } + } + + return super.convertPayment(checkout, instruments, selectedInstrumentId); + } + + private UCPCheckoutResponse createUcpCheckoutResponse() { + try (InputStream inputStream = + new ClassPathResource("discovery_profile.json").getInputStream()) { + JsonNode discoveryProfile = objectMapper.readTree(inputStream); + JsonNode ucpNode = discoveryProfile.get("ucp"); + if (ucpNode != null) { + return objectMapper.treeToValue(ucpNode, UCPCheckoutResponse.class); + } + return new UCPCheckoutResponse(); + } catch (IOException e) { + throw new RuntimeException("Error reading discovery_profile.json", e); + } + } + + @Override + protected String getEndpoint() { + return "/checkout-sessions/create"; + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/GetCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/GetCheckoutAction.java new file mode 100644 index 0000000..d529dd8 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/GetCheckoutAction.java @@ -0,0 +1,41 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** Service for retrieving an existing checkout session. */ +@Service +@Transactional +public class GetCheckoutAction extends BaseCheckoutAction { + + public CheckoutResponse getCheckout(String checkoutId) { + return execute(null, checkoutId); + } + + @Override + protected CheckoutResponse executeAction(String request) { + return loadCheckoutSession(request); + } + + @Override + protected String getEndpoint() { + return "/checkout-sessions/{id}"; + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/actions/UpdateCheckoutAction.java b/rest/java-spring/src/main/java/com/dev/ucp/actions/UpdateCheckoutAction.java new file mode 100644 index 0000000..7fef5af --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/actions/UpdateCheckoutAction.java @@ -0,0 +1,88 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import com.dev.ucp.ExtensionsHelper; +import com.dev.ucp.InventoryManager; +import com.dev.ucp.LineItemConverter; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.dev.ucp.service.shopping.model.PaymentResponse; +import com.dev.ucp.service.shopping.model.PaymentUpdateRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Service for updating an existing checkout session. */ +@Service +public class UpdateCheckoutAction extends BaseCheckoutAction { + + @Autowired private ExtensionsHelper extensionsHelper; + @Autowired private InventoryManager inventoryManager; + @Autowired private LineItemConverter lineItemConverter; + + public CheckoutResponse updateCheckout( + String checkoutId, UUID idempotencyKey, CheckoutUpdateRequest checkout) { + checkout.setId(checkoutId); // Ensure ID consistency + return execute(idempotencyKey, checkout); + } + + @Override + protected CheckoutResponse executeAction(CheckoutUpdateRequest request) { + CheckoutResponse checkout = loadCheckoutSession(request.getId()); + + ensureModifiable(checkout); + + checkout.setMessages(new ArrayList<>()); + checkout.setBuyer(request.getBuyer()); + checkout.setCurrency(request.getCurrency()); + checkout.setPayment(convertPayment(checkout, request.getPayment())); + checkout.setLineItems(lineItemConverter.convertFromUpdateRequest(request.getLineItems())); + initTotals(checkout); + inventoryManager.validate(checkout); + + fulfillmentManager.handleFulfillment(checkout, extensionsHelper.getFulfillmentRequest(request)); + recalculateTotals(checkout, extensionsHelper.getDiscount(request)); + updateStatus(checkout); + + saveCheckoutSession(checkout.getId(), checkout.getStatus().toString(), checkout); + + return checkout; + } + + private PaymentResponse convertPayment( + CheckoutResponse checkout, PaymentUpdateRequest paymentRequest) { + List instruments = new ArrayList<>(); + String selectedInstrumentId = null; + if (paymentRequest != null) { + selectedInstrumentId = paymentRequest.getSelectedInstrumentId(); + if (paymentRequest.getInstruments() != null) { + instruments.addAll(paymentRequest.getInstruments()); + } + } + + return super.convertPayment(checkout, instruments, selectedInstrumentId); + } + + @Override + protected String getEndpoint() { + return "/checkout-sessions/{id}"; + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/AddressEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/AddressEntity.java new file mode 100644 index 0000000..2fd3ef1 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/AddressEntity.java @@ -0,0 +1,137 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.List; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a customer's address. */ +@Entity +@Table(name = "addresses") +public class AddressEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository { + List findByCustomerEmail(String customerEmail); + } + + @Id private String id; + private String customerEmail; + private String streetAddress; + private String city; + private String state; + private String postalCode; + private String country; + + public AddressEntity() {} + + public AddressEntity( + String id, + String customerEmail, + String streetAddress, + String city, + String state, + String postalCode, + String country) { + this.id = id; + this.customerEmail = customerEmail; + this.streetAddress = streetAddress; + this.city = city; + this.state = state; + this.postalCode = postalCode; + this.country = country; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCustomerEmail() { + return customerEmail; + } + + public void setCustomerEmail(String customerEmail) { + this.customerEmail = customerEmail; + } + + public String getStreetAddress() { + return streetAddress; + } + + public void setStreetAddress(String streetAddress) { + this.streetAddress = streetAddress; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getPostalCode() { + return postalCode; + } + + public void setPostalCode(String postalCode) { + this.postalCode = postalCode; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AddressEntity that = (AddressEntity) o; + return Objects.equals(id, that.id) + && Objects.equals(customerEmail, that.customerEmail) + && Objects.equals(streetAddress, that.streetAddress) + && Objects.equals(city, that.city) + && Objects.equals(state, that.state) + && Objects.equals(postalCode, that.postalCode) + && Objects.equals(country, that.country); + } + + @Override + public int hashCode() { + return Objects.hash(id, customerEmail, streetAddress, city, state, postalCode, country); + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/CheckoutSessionEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/CheckoutSessionEntity.java new file mode 100644 index 0000000..00349f9 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/CheckoutSessionEntity.java @@ -0,0 +1,76 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a UCP checkout session. */ +@Entity +@Table(name = "checkouts") +public class CheckoutSessionEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String id; + + private String status; + + @Lob private String data; // Storing the full JSON blob + + // Getters and Setters + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CheckoutSessionEntity that = (CheckoutSessionEntity) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/DiscountEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/DiscountEntity.java new file mode 100644 index 0000000..2f427dd --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/DiscountEntity.java @@ -0,0 +1,97 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a discount code. */ +@Entity +@Table(name = "discounts") +public class DiscountEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String code; + private String description; + private String type; + + @Column(name = "discount_value") + private Integer discountValue; + + public DiscountEntity() {} + + public DiscountEntity(String code, String description, String type, Integer value) { + this.code = code; + this.description = description; + this.type = type; + this.discountValue = value; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Integer getValue() { + return discountValue; + } + + public void setValue(Integer value) { + this.discountValue = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DiscountEntity that = (DiscountEntity) o; + return Objects.equals(code, that.code) + && Objects.equals(description, that.description) + && Objects.equals(type, that.type) + && Objects.equals(discountValue, that.discountValue); + } + + @Override + public int hashCode() { + return Objects.hash(code, description, type, discountValue); + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/IdempotencyRecordEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/IdempotencyRecordEntity.java new file mode 100644 index 0000000..d9e81a4 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/IdempotencyRecordEntity.java @@ -0,0 +1,104 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing an idempotency record for request deduplication. */ +@Entity +@Table(name = "idempotency_records") +public class IdempotencyRecordEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id + @Column(name = "id") + private String key; + + @Column(name = "request_hash") + private String requestHash; + + @Column(name = "response_status") + private Integer responseStatus; + + @Lob + @Column(name = "response_body") + private String responseBody; + + @Column(name = "created_at") + private String createdAt; + + // Getters and Setters + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getRequestHash() { + return requestHash; + } + + public void setRequestHash(String requestHash) { + this.requestHash = requestHash; + } + + public Integer getResponseStatus() { + return responseStatus; + } + + public void setResponseStatus(Integer responseStatus) { + this.responseStatus = responseStatus; + } + + public String getResponseBody() { + return responseBody; + } + + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } + + public String getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IdempotencyRecordEntity that = (IdempotencyRecordEntity) o; + return key.equals(that.key); + } + + @Override + public int hashCode() { + return key.hashCode(); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/InventoryEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/InventoryEntity.java new file mode 100644 index 0000000..86d5e23 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/InventoryEntity.java @@ -0,0 +1,75 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing the inventory level of a product. */ +@Entity +@Table(name = "inventory") +public class InventoryEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String productId; + private int quantity; + + public InventoryEntity() {} + + public InventoryEntity(String productId, int quantity) { + this.productId = productId; + this.quantity = quantity; + } + + public String getProductId() { + return productId; + } + + public void setProductId(String productId) { + this.productId = productId; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + InventoryEntity that = (InventoryEntity) o; + return quantity == that.quantity && Objects.equals(productId, that.productId); + } + + @Override + public int hashCode() { + return Objects.hash(productId, quantity); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/OrderEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/OrderEntity.java new file mode 100644 index 0000000..cf9ff30 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/OrderEntity.java @@ -0,0 +1,66 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a finalized UCP order. */ +@Entity +@Table(name = "orders") +public class OrderEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String id; + + @Lob private String data; + + // Getters and Setters + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OrderEntity that = (OrderEntity) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/ProductEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/ProductEntity.java new file mode 100644 index 0000000..7968b19 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/ProductEntity.java @@ -0,0 +1,85 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a product in the shop. */ +@Entity +@Table(name = "products") +public class ProductEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String id; + private String title; + private int price; + + public ProductEntity() {} + + public ProductEntity(String id, String title, int price) { + this.id = id; + this.title = title; + this.price = price; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getPrice() { + return price; + } + + public void setPrice(int price) { + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductEntity that = (ProductEntity) o; + return price == that.price && Objects.equals(id, that.id) && Objects.equals(title, that.title); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, price); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/PromotionEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/PromotionEntity.java new file mode 100644 index 0000000..2e8e6de --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/PromotionEntity.java @@ -0,0 +1,98 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a shop promotion. */ +@Entity +@Table(name = "promotions") +public class PromotionEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository {} + + @Id private String id; + private String title; + private String type; + private Integer subtotalThreshold; + + public PromotionEntity() {} + + public PromotionEntity(String id, String title, String type, Integer subtotalThreshold) { + this.id = id; + this.title = title; + this.type = type; + this.subtotalThreshold = subtotalThreshold; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Integer getSubtotalThreshold() { + return subtotalThreshold; + } + + public void setSubtotalThreshold(Integer subtotalThreshold) { + this.subtotalThreshold = subtotalThreshold; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PromotionEntity that = (PromotionEntity) o; + return Objects.equals(id, that.id) + && Objects.equals(title, that.title) + && Objects.equals(type, that.type) + && Objects.equals(subtotalThreshold, that.subtotalThreshold); + } + + @Override + public int hashCode() { + return Objects.hash(id, title, type, subtotalThreshold); + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/java/com/dev/ucp/entities/ShippingRateEntity.java b/rest/java-spring/src/main/java/com/dev/ucp/entities/ShippingRateEntity.java new file mode 100644 index 0000000..88184dc --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/entities/ShippingRateEntity.java @@ -0,0 +1,109 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.entities; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.util.List; +import java.util.Objects; +import org.springframework.data.jpa.repository.JpaRepository; + +/** Entity representing a shipping rate for a country and service level. */ +@Entity +@Table(name = "shipping_rates") +public class ShippingRateEntity { + + @org.springframework.stereotype.Repository + public interface Repository extends JpaRepository { + List findByCountryCodeOrCountryCode(String code1, String code2); + } + + @Id private String id; + private String countryCode; + private String serviceLevel; + private String title; + private Integer price; + + public ShippingRateEntity() {} + + public ShippingRateEntity( + String id, String countryCode, String serviceLevel, String title, Integer price) { + this.id = id; + this.countryCode = countryCode; + this.serviceLevel = serviceLevel; + this.title = title; + this.price = price; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getServiceLevel() { + return serviceLevel; + } + + public void setServiceLevel(String serviceLevel) { + this.serviceLevel = serviceLevel; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Integer getPrice() { + return price; + } + + public void setPrice(Integer price) { + this.price = price; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ShippingRateEntity that = (ShippingRateEntity) o; + return Objects.equals(id, that.id) + && Objects.equals(countryCode, that.countryCode) + && Objects.equals(serviceLevel, that.serviceLevel) + && Objects.equals(title, that.title) + && Objects.equals(price, that.price); + } + + @Override + public int hashCode() { + return Objects.hash(id, countryCode, serviceLevel, title, price); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java new file mode 100644 index 0000000..d0de11e --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when an attempt is made to modify a checkout session that is in a terminal state + * (e.g., COMPLETED or CANCELED). + * + *

    This exception results in an HTTP 409 Conflict response. + * + *

    Reference: UCP Checkout + * REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.CONFLICT) +public class CheckoutNotModifiableException extends RuntimeException { + public CheckoutNotModifiableException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java.orig new file mode 100644 index 0000000..6bc19b2 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/CheckoutNotModifiableException.java.orig @@ -0,0 +1,19 @@ +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when an attempt is made to modify a checkout session that is in a terminal state + * (e.g., COMPLETED or CANCELED). + * + *

    This exception results in an HTTP 409 Conflict response. + * + *

    Reference: UCP Checkout REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.CONFLICT) +public class CheckoutNotModifiableException extends RuntimeException { + public CheckoutNotModifiableException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ExtensionValidationException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ExtensionValidationException.java new file mode 100644 index 0000000..515d4fe --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ExtensionValidationException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import jakarta.validation.ConstraintViolation; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when a UCP extension fails validation. + * + *

    This exception results in an HTTP 400 Bad Request response. + */ +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class ExtensionValidationException extends RuntimeException { + private final Set> violations; + + public ExtensionValidationException(Set> violations) { + super(formatMessage(violations)); + this.violations = violations; + } + + public Set> getViolations() { + return violations; + } + + private static String formatMessage(Set> violations) { + return violations.stream() + .map(v -> v.getPropertyPath() + ": " + v.getMessage()) + .collect(Collectors.joining(", ")); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java new file mode 100644 index 0000000..ce3dcde --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when an idempotency key is reused with a different request payload, indicating a + * conflict that prevents the server from processing the request. + * + *

    This exception results in an HTTP 409 Conflict response. + * + *

    Reference: UCP Checkout + * REST - Status Codes + */ +@ResponseStatus(HttpStatus.CONFLICT) +public class IdempotencyConflictException extends RuntimeException { + + public IdempotencyConflictException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java.orig new file mode 100644 index 0000000..ca8c9c0 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/IdempotencyConflictException.java.orig @@ -0,0 +1,20 @@ +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when an idempotency key is reused with a different request payload, indicating a + * conflict that prevents the server from processing the request. + * + *

    This exception results in an HTTP 409 Conflict response. + * + *

    Reference: UCP Checkout REST - Status Codes + */ +@ResponseStatus(HttpStatus.CONFLICT) +public class IdempotencyConflictException extends RuntimeException { + + public IdempotencyConflictException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java new file mode 100644 index 0000000..3135b55 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when a request is invalid due to business logic errors, such as a product not + * being found during checkout. + * + *

    This exception results in an HTTP 400 Bad Request response. + * + *

    Reference: UCP Checkout + * REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class InvalidRequestException extends RuntimeException { + public InvalidRequestException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java.orig new file mode 100644 index 0000000..1b287a7 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/InvalidRequestException.java.orig @@ -0,0 +1,19 @@ +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when a request is invalid due to business logic errors, such as a product not + * being found during checkout. + * + *

    This exception results in an HTTP 400 Bad Request response. + * + *

    Reference: UCP Checkout REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.BAD_REQUEST) +public class InvalidRequestException extends RuntimeException { + public InvalidRequestException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java new file mode 100644 index 0000000..00fc7f0 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when payment processing fails. + * + *

    This exception results in an HTTP 402 Payment Required response. + * + *

    Reference: UCP Checkout + * REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.PAYMENT_REQUIRED) +public class PaymentFailedException extends RuntimeException { + public PaymentFailedException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java.orig new file mode 100644 index 0000000..029e636 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/PaymentFailedException.java.orig @@ -0,0 +1,18 @@ +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when payment processing fails. + * + *

    This exception results in an HTTP 402 Payment Required response. + * + *

    Reference: UCP Checkout REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.PAYMENT_REQUIRED) +public class PaymentFailedException extends RuntimeException { + public PaymentFailedException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java new file mode 100644 index 0000000..6fc18eb --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when a requested resource, such as a checkout session, cannot be found in the + * system. + * + *

    This exception results in an HTTP 404 Not Found response. + * + *

    Reference: UCP Checkout + * REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java.orig b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java.orig new file mode 100644 index 0000000..ddbd27f --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/exceptions/ResourceNotFoundException.java.orig @@ -0,0 +1,19 @@ +package com.dev.ucp.exceptions; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Exception thrown when a requested resource, such as a checkout session, cannot be found in the + * system. + * + *

    This exception results in an HTTP 404 Not Found response. + * + *

    Reference: UCP Checkout REST - Status Codes + */ +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/service/DiscoveryController.java b/rest/java-spring/src/main/java/com/dev/ucp/service/DiscoveryController.java new file mode 100644 index 0000000..7dc000c --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/service/DiscoveryController.java @@ -0,0 +1,63 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Controller for the UCP discovery endpoint. + * + *

    Serves the discovery profile which describes the capabilities and service endpoints of this + * UCP implementation. + */ +@RestController +public class DiscoveryController { + + private static final String SHOP_ID = UUID.randomUUID().toString(); + + @Value("${server.port:8080}") + private int port; + + @Autowired private ResourceLoader resourceLoader; + + @Autowired private ObjectMapper objectMapper; + + @GetMapping("/.well-known/ucp") + public Object getDiscoveryProfile() throws IOException { + Resource resource = resourceLoader.getResource("classpath:discovery_profile.json"); + try (InputStream inputStream = resource.getInputStream()) { + String template = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + // For simplicity, we'll replace the endpoint with a placeholder. + // A more robust solution would dynamically determine the endpoint URL. + String profileJson = + template + .replace("{{ENDPOINT}}", "http://localhost:" + port + "/ucp") + .replace("{{SHOP_ID}}", SHOP_ID); + return objectMapper.readValue(profileJson, Object.class); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/service/UCPExceptionControllerAdvice.java b/rest/java-spring/src/main/java/com/dev/ucp/service/UCPExceptionControllerAdvice.java new file mode 100644 index 0000000..c070ec2 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/service/UCPExceptionControllerAdvice.java @@ -0,0 +1,146 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.service; + +import com.dev.ucp.service.shopping.CheckoutSessionsApiController; +import com.dev.ucp.service.shopping.model.Message; +import com.dev.ucp.service.shopping.model.MessageError; +import java.util.List; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +/** + * Centralized exception handling for UCP-compliant services. + * + *

    This advice ensures that all exceptions thrown within the targeted controllers are surfaced as + * structured UCP error responses. It automatically maps Java exceptions to protocol-standard error + * codes and ensures the response body adheres to the UCP ErrorResponse schema. + * + *

    Key features: + * + *

      + *
    • Dynamic Status Mapping: Extracts HTTP status codes from {@link ResponseStatus} + * annotations on custom exceptions. + *
    • Standardized Error Codes: Automatically converts exception class names to snake_case + * codes (e.g., PaymentFailedException -> payment_failed). + *
    • Escalation Signal: Sets the checkout status to 'requires_escalation' to notify the + * platform that buyer intervention is needed. + *
    + * + *

    Reference: UCP Checkout + * REST - Error Responses + */ +@ControllerAdvice( + assignableTypes = {CheckoutSessionsApiController.class, DiscoveryController.class}) +public class UCPExceptionControllerAdvice { + + private static final Logger logger = LoggerFactory.getLogger(UCPExceptionControllerAdvice.class); + + /** Simple record for UCP-compliant error responses. */ + public record ErrorResponse(String status, List messages) {} + + /** + * Catches all unhandled exceptions and transforms them into structured UCP error responses. + * + * @param ex the exception to handle. + * @return a {@link ResponseEntity} containing a UCP-compliant {@link ErrorResponse}. + */ + @ExceptionHandler(Exception.class) + public ResponseEntity handleAllExceptions(Exception ex) { + HttpStatus status = getResponseStatus(ex); + String errorCode = convertToErrorCode(ex); + + if (status.is5xxServerError()) { + logger.error("Internal Server Error: ", ex); + return createErrorResponse(status, "internal_error", "An unexpected error occurred."); + } + + logger.warn("{}: {}", ex.getClass().getSimpleName(), ex.getMessage()); + return createErrorResponse(status, errorCode, ex.getMessage()); + } + + /** + * Specifically handles {@link IllegalArgumentException} resulting from validation or header + * parsing errors. + * + * @param ex the exception to handle. + * @return a {@link ResponseEntity} with {@code 400 Bad Request} and structured error details. + */ + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgument(IllegalArgumentException ex) { + logger.warn("Illegal argument: {}", ex.getMessage()); + return createErrorResponse(HttpStatus.BAD_REQUEST, "invalid_parameters", ex.getMessage()); + } + + /** + * Handles {@link MethodArgumentNotValidException} which is thrown when {@code @Valid} validation + * fails on a controller method argument. + * + * @param ex the exception to handle. + * @return a {@link ResponseEntity} with {@code 400 Bad Request} and structured error details. + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex) { + String message = + ex.getBindingResult().getFieldErrors().stream() + .map(error -> error.getField() + ": " + error.getDefaultMessage()) + .collect(Collectors.joining(", ")); + + logger.warn("Validation failed: {}", message); + return createErrorResponse(HttpStatus.BAD_REQUEST, "validation_failed", message); + } + + private HttpStatus getResponseStatus(Exception ex) { + ResponseStatus annotation = + AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class); + if (annotation != null) { + return (HttpStatus) annotation.value(); + } + return HttpStatus.INTERNAL_SERVER_ERROR; + } + + private String convertToErrorCode(Exception ex) { + String name = ex.getClass().getSimpleName(); + if (name.endsWith("Exception")) { + name = name.substring(0, name.length() - "Exception".length()); + } + // Convert CamelCase to snake_case + return name.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase(); + } + + private ResponseEntity createErrorResponse( + HttpStatus status, String code, String message) { + MessageError error = new MessageError(); + error.setType(MessageError.TypeEnum.ERROR); + error.setCode(code); + error.setContent(message); + error.setSeverity(MessageError.SeverityEnum.REQUIRES_BUYER_INPUT); + + ErrorResponse response = new ErrorResponse("requires_escalation", List.of(error)); + + return new ResponseEntity<>(response, status); + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/ApiUtil.java b/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/ApiUtil.java new file mode 100644 index 0000000..82a3017 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/ApiUtil.java @@ -0,0 +1,36 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.service.shopping; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.web.context.request.NativeWebRequest; + +/** Utility class to append example responses. */ +public class ApiUtil { + + public static void setExampleResponse(NativeWebRequest req, String contentType, String example) { + try { + HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class); + res.setCharacterEncoding("UTF-8"); + res.addHeader("Content-Type", contentType); + res.getWriter().print(example); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApiController.java b/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApiController.java new file mode 100644 index 0000000..2ef3747 --- /dev/null +++ b/rest/java-spring/src/main/java/com/dev/ucp/service/shopping/CheckoutSessionsApiController.java @@ -0,0 +1,233 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.service.shopping; + +import com.dev.ucp.PlatformProfileResolver; +import com.dev.ucp.actions.CancelCheckoutAction; +import com.dev.ucp.actions.CompleteCheckoutAction; +import com.dev.ucp.actions.CreateCheckoutAction; +import com.dev.ucp.actions.GetCheckoutAction; +import com.dev.ucp.actions.UpdateCheckoutAction; +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.dev.ucp.service.shopping.model.CompleteCheckoutRequest; +import java.util.UUID; +import javax.annotation.Generated; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.request.NativeWebRequest; + +/** + * Implementation of the UCP Shopping API for managing checkout sessions. + * + *

    Delegates business logic to specialized Action classes. + */ +@Generated( + value = "org.openapitools.codegen.languages.SpringCodegen", + date = "2025-12-22T13:22:18.790633199Z[Etc/UTC]", + comments = "Generator version: 7.17.0") +@Controller +@RequestMapping("${openapi.uCPShoppingService.base-path:/ucp}") +public class CheckoutSessionsApiController implements CheckoutSessionsApi { + + private static final Logger logger = LoggerFactory.getLogger(CheckoutSessionsApiController.class); + + private final NativeWebRequest request; + + private final CreateCheckoutAction createCheckoutAction; + + private final PlatformProfileResolver platformProfileResolver; + + private final GetCheckoutAction getCheckoutAction; + + private final UpdateCheckoutAction updateCheckoutAction; + + private final CancelCheckoutAction cancelCheckoutAction; + + private final CompleteCheckoutAction completeCheckoutAction; + + private final boolean failOnNullVersion; + + private static class UcpAgentInfo { + String version; + String profile; + } + + @Autowired + public CheckoutSessionsApiController( + NativeWebRequest request, + CreateCheckoutAction createCheckoutAction, + GetCheckoutAction getCheckoutAction, + UpdateCheckoutAction updateCheckoutAction, + CancelCheckoutAction cancelCheckoutAction, + CompleteCheckoutAction completeCheckoutAction, + PlatformProfileResolver platformProfileResolver, + @Value("${dev.ucp.version_check.fail_on_null:true}") boolean failOnNullVersion) { + this.request = request; + this.createCheckoutAction = createCheckoutAction; + this.getCheckoutAction = getCheckoutAction; + this.updateCheckoutAction = updateCheckoutAction; + this.cancelCheckoutAction = cancelCheckoutAction; + this.completeCheckoutAction = completeCheckoutAction; + this.platformProfileResolver = platformProfileResolver; + this.failOnNullVersion = failOnNullVersion; + } + + private UcpAgentInfo parseAndValidateUcpAgentHeader(String ucPAgent) { + logger.info("UCP-Agent header: {}", ucPAgent); + if (ucPAgent == null) { + throw new IllegalArgumentException("UCP-Agent header is missing."); + } + UcpAgentInfo info = new UcpAgentInfo(); + for (String part : ucPAgent.split(";")) { + String[] kv = part.trim().split("=", 2); + if (kv.length == 2) { + String key = kv[0].trim(); + String val = kv[1].trim(); + if (val.length() > 1 && val.startsWith("\"") && val.endsWith("\"")) { + val = val.substring(1, val.length() - 1); + } + if ("version".equals(key)) { + info.version = val; + } else if ("profile".equals(key)) { + info.profile = val; + } + } + } + + String expectedVersion = "2026-01-11"; + if (!expectedVersion.equals(info.version)) { + // TODO: remove this once coformance tests are updated. + if (info.version == null && !failOnNullVersion) { + logger.warn( + "UCP-Agent header has missing 'version'. Continuing as" + + " dev.ucp.version_check.fail_on_null is false."); + } else { + logger.error( + "UCP-Agent header has invalid or missing 'version'. Expected {}. Found {}.", + expectedVersion, + info.version); + throw new IllegalArgumentException( + "UCP-Agent header has invalid or missing 'version'. Expected '2026-01-11'."); + } + } + return info; + } + + @Override + public ResponseEntity createCheckout( + String requestSignature, + UUID idempotencyKey, + UUID requestId, + String ucPAgent, + CheckoutCreateRequest checkoutCreateRequest, + String authorization, + String xAPIKey, + String userAgent, + String contentType, + String accept, + String acceptLanguage, + String acceptEncoding) { + parseAndValidateUcpAgentHeader(ucPAgent); + return ResponseEntity.status(HttpStatus.CREATED) + .body(createCheckoutAction.createCheckoutSession(idempotencyKey, checkoutCreateRequest)); + } + + public ResponseEntity completeCheckout( + String requestSignature, + UUID idempotencyKey, + UUID requestId, + String ucPAgent, + String id, + CompleteCheckoutRequest completeCheckoutRequest, + String authorization, + String xAPIKey, + String userAgent, + String contentType, + String accept, + String acceptLanguage, + String acceptEncoding) { + UcpAgentInfo agentInfo = parseAndValidateUcpAgentHeader(ucPAgent); + return ResponseEntity.ok( + completeCheckoutAction.completeCheckout( + id, + idempotencyKey, + completeCheckoutRequest, + platformProfileResolver.resolveWebhookUrl(agentInfo.profile).orElse(null))); + } + + @Override + public ResponseEntity cancelCheckout( + String requestSignature, + UUID idempotencyKey, + UUID requestId, + String ucPAgent, + String id, + String authorization, + String xAPIKey, + String userAgent, + String contentType, + String accept, + String acceptLanguage, + String acceptEncoding) { + parseAndValidateUcpAgentHeader(ucPAgent); + return ResponseEntity.ok(cancelCheckoutAction.cancelCheckout(id, idempotencyKey)); + } + + @Override + public ResponseEntity updateCheckout( + String requestSignature, + UUID idempotencyKey, + UUID requestId, + String ucPAgent, + String id, + CheckoutUpdateRequest checkout, + String authorization, + String xAPIKey, + String userAgent, + String contentType, + String accept, + String acceptLanguage, + String acceptEncoding) { + parseAndValidateUcpAgentHeader(ucPAgent); + return ResponseEntity.ok(updateCheckoutAction.updateCheckout(id, idempotencyKey, checkout)); + } + + @Override + public ResponseEntity getCheckout( + String requestSignature, + UUID requestId, + String ucPAgent, + String id, + String authorization, + String xAPIKey, + String userAgent, + String contentType, + String accept, + String acceptLanguage, + String acceptEncoding) { + parseAndValidateUcpAgentHeader(ucPAgent); + return ResponseEntity.ok(getCheckoutAction.getCheckout(id)); + } +} diff --git a/rest/java-spring/src/main/java/org/openapitools/RFC3339DateFormat.java b/rest/java-spring/src/main/java/org/openapitools/RFC3339DateFormat.java new file mode 100644 index 0000000..5fc73fc --- /dev/null +++ b/rest/java-spring/src/main/java/org/openapitools/RFC3339DateFormat.java @@ -0,0 +1,53 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openapitools; + +import com.fasterxml.jackson.databind.util.StdDateFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** Formatter for RFC 3339 dates, using UTC as the default time zone. */ +public class RFC3339DateFormat extends DateFormat { + private static final long serialVersionUID = 1L; + private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC"); + + private final StdDateFormat fmt = + new StdDateFormat().withTimeZone(TIMEZONE_Z).withColonInTimeZone(true); + + public RFC3339DateFormat() { + this.calendar = new GregorianCalendar(); + } + + @Override + public Date parse(String source, ParsePosition pos) { + return fmt.parse(source, pos); + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { + return fmt.format(date, toAppendTo, fieldPosition); + } + + @Override + public Object clone() { + return this; + } +} diff --git a/rest/java-spring/src/main/resources/application.properties b/rest/java-spring/src/main/resources/application.properties new file mode 100644 index 0000000..606d018 --- /dev/null +++ b/rest/java-spring/src/main/resources/application.properties @@ -0,0 +1,14 @@ +spring.application.name=ucp-spring-demo +spring.jackson.date-format=org.openapitools.RFC3339DateFormat +spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:h2:mem:testdb +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +spring.h2.console.enabled=true +spring.jpa.defer-datasource-initialization=true +spring.sql.init.mode=always +dev.ucp.version_check.fail_on_null=false +logging.level.web=DEBUG +logging.level.dev.ucp=DEBUG \ No newline at end of file diff --git a/rest/java-spring/src/main/resources/cleanup.sql b/rest/java-spring/src/main/resources/cleanup.sql new file mode 100644 index 0000000..d8b00fc --- /dev/null +++ b/rest/java-spring/src/main/resources/cleanup.sql @@ -0,0 +1,6 @@ +DELETE FROM products; +DELETE FROM inventory; +DELETE FROM promotions; +DELETE FROM shipping_rates; +DELETE FROM discounts; +DELETE FROM addresses; diff --git a/rest/java-spring/src/main/resources/data.sql b/rest/java-spring/src/main/resources/data.sql new file mode 100644 index 0000000..2e9dd81 --- /dev/null +++ b/rest/java-spring/src/main/resources/data.sql @@ -0,0 +1,41 @@ +DELETE FROM products; +DELETE FROM inventory; +DELETE FROM promotions; +DELETE FROM shipping_rates; +DELETE FROM discounts; +DELETE FROM addresses; + +INSERT INTO products (id, title, price) VALUES +('bouquet_roses', 'Bouquet of Red Roses', 3500), +('pot_ceramic', 'Ceramic Pot', 1500), +('bouquet_sunflowers', 'Sunflower Bundle', 2500), +('bouquet_tulips', 'Spring Tulips', 3000), +('orchid_white', 'White Orchid', 4500), +('gardenias', 'Gardenias', 2000); + +INSERT INTO inventory (product_id, quantity) VALUES +('bouquet_roses', 1000), +('pot_ceramic', 2000), +('bouquet_sunflowers', 500), +('bouquet_tulips', 1500), +('orchid_white', 800), +('gardenias', 0); + +INSERT INTO promotions (id, title, type, subtotal_threshold) VALUES +('promo_1', 'Free Shipping on orders over $100', 'free_shipping', 10000); +-- ('promo_2', 'Free Shipping on Rose Bouquets', 'free_shipping', , '["bouquet_roses"]'); -- eligible_item_ids not supported yet + +INSERT INTO shipping_rates (id, country_code, service_level, title, price) VALUES +('std-ship', 'default', 'standard', 'Standard Shipping', 500), +('exp-ship-us', 'US', 'express', 'Express Shipping (US)', 1500), +('exp-ship-intl', 'default', 'express', 'International Express', 2500); + +INSERT INTO discounts (code, type, discount_value, description) VALUES +('10OFF', 'percentage', 10, '10% Off'), +('WELCOME20', 'percentage', 20, '20% Off'), +('FIXED500', 'fixed_amount', 500, '$5.00 Off'); + +INSERT INTO addresses (id, customer_email, street_address, city, state, postal_code, country) VALUES +('addr_1', 'john.doe@example.com', '123 Main St', 'Springfield', 'IL', '62704', 'US'), +('addr_2', 'john.doe@example.com', '456 Oak Ave', 'Metropolis', 'NY', '10012', 'US'), +('addr_3', 'jane.smith@example.com', '789 Pine Ln', 'Smallville', 'KS', '66002', 'US'); diff --git a/rest/java-spring/src/main/resources/discovery_profile.json b/rest/java-spring/src/main/resources/discovery_profile.json new file mode 100644 index 0000000..d7d9766 --- /dev/null +++ b/rest/java-spring/src/main/resources/discovery_profile.json @@ -0,0 +1,63 @@ +{ + "ucp": { + "version": "2026-01-11", + "services": { + "dev.ucp.shopping": { + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping", + "rest": { + "schema": "https://ucp.dev/services/shopping/openapi.json", + "endpoint": "{{ENDPOINT}}" + } + } + }, + "capabilities": [ + { + "name": "dev.ucp.shopping.checkout", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/checkout", + "schema": "https://ucp.dev/schemas/shopping/checkout.json" + }, + { + "name": "dev.ucp.shopping.discount", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/discount", + "schema": "https://ucp.dev/schemas/shopping/discount.json", + "extends": "dev.ucp.shopping.checkout" + }, + { + "name": "dev.ucp.shopping.fulfillment", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/fulfillment", + "schema": "https://ucp.dev/schemas/shopping/fulfillment.json", + "extends": "dev.ucp.shopping.checkout" + }, + { + "name": "dev.ucp.shopping.order", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/shopping/order", + "schema": "https://ucp.dev/schemas/shopping/order.json" + } + ] + }, + "payment": { + "handlers": [ + { + "id": "mock_payment_handler", + "name": "dev.ucp.mock_payment", + "version": "2026-01-11", + "spec": "https://ucp.dev/specs/mock", + "config_schema": "https://ucp.dev/schemas/mock.json", + "instrument_schemas": [ + "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json" + ], + "config": { + "supported_tokens": [ + "success_token", + "fail_token" + ] + } + } + ] + } +} \ No newline at end of file diff --git a/rest/java-spring/src/main/resources/openapi.extensions.json b/rest/java-spring/src/main/resources/openapi.extensions.json new file mode 100644 index 0000000..a50da3e --- /dev/null +++ b/rest/java-spring/src/main/resources/openapi.extensions.json @@ -0,0 +1,24 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "UCP Shopping Service", + "version": "2026-01-11", + "description": "Extensions used by this instance of the UCP Shopping service." + }, + "components": { + "schemas": { + "fulfillment_response": { + "$ref": "../../../target/spec/schemas/shopping/types/fulfillment_resp.json" + }, + "fulfillment_request": { + "$ref": "../../../target/spec/schemas/shopping/types/fulfillment_req.json" + }, + "discount_request": { + "$ref": "../../../target/spec/schemas/shopping/discount.create_req.json#/$defs/discounts_object" + }, + "discount_response": { + "$ref": "../../../target/spec/schemas/shopping/discount_resp.json#/$defs/discounts_object" + } + } + } +} \ No newline at end of file diff --git a/rest/java-spring/src/test/java/com/dev/ucp/FulfillmentManagerTest.java b/rest/java-spring/src/test/java/com/dev/ucp/FulfillmentManagerTest.java new file mode 100644 index 0000000..ffbb5ca --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/FulfillmentManagerTest.java @@ -0,0 +1,193 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +import com.dev.ucp.entities.AddressEntity; +import com.dev.ucp.entities.PromotionEntity; +import com.dev.ucp.entities.ShippingRateEntity; +import com.dev.ucp.service.shopping.model.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +@SpringBootTest +public class FulfillmentManagerTest { + + @Autowired private FulfillmentManager fulfillmentManager; + @Autowired private ExtensionsHelper extensionsHelper; + + @MockBean private AddressEntity.Repository addressRepository; + @MockBean private ShippingRateEntity.Repository shippingRateRepository; + @MockBean private PromotionEntity.Repository promotionRepository; + + private CheckoutResponse checkout; + private final String buyerEmail = "test@example.com"; + + @BeforeEach + void setUp() { + checkout = new CheckoutResponse(); + checkout.setBuyer(new Buyer().email(buyerEmail)); + checkout.setLineItems(new ArrayList<>()); + checkout.addLineItemsItem( + new LineItemResponse() + .id("li_1") + .item(new ItemResponse().id("item_1").price(1000).title("Item 1")) + .quantity(1)); + checkout.setTotals(new ArrayList<>()); + checkout.addTotalsItem(new TotalResponse().type(TotalResponse.TypeEnum.SUBTOTAL).amount(1000)); + + when(promotionRepository.findAll()).thenReturn(Collections.emptyList()); + when(shippingRateRepository.findByCountryCodeOrCountryCode(anyString(), anyString())) + .thenReturn(Collections.emptyList()); + } + + @Test + void handleFulfillment_shouldProposeDefaultShipping_whenNoFulfillmentProvided() { + when(addressRepository.findByCustomerEmail(buyerEmail)).thenReturn(Collections.emptyList()); + when(shippingRateRepository.findByCountryCodeOrCountryCode("default", "default")) + .thenReturn( + List.of( + new ShippingRateEntity( + "std-ship", "default", "standard", "Standard Shipping", 500))); + + fulfillmentManager.handleFulfillment(checkout, null); + + FulfillmentResponse fr = extensionsHelper.getFulfillmentResponse(checkout); + assertNotNull(fr); + assertEquals(1, fr.getMethods().size()); + FulfillmentMethodResponse method = fr.getMethods().get(0); + assertEquals(FulfillmentMethodResponse.TypeEnum.SHIPPING, method.getType()); + assertTrue(method.getDestinations().isEmpty()); + assertEquals(1, method.getGroups().size()); + assertEquals(1, method.getGroups().get(0).getOptions().size()); + assertEquals("std-ship", method.getGroups().get(0).getOptions().get(0).getId()); + } + + @Test + void handleFulfillment_shouldMergeRequestedDestinationsWithKnownAddresses() { + AddressEntity knownAddr = + new AddressEntity("addr_1", buyerEmail, "123 Main St", "Springfield", "IL", "62704", "US"); + when(addressRepository.findByCustomerEmail(buyerEmail)).thenReturn(List.of(knownAddr)); + + FulfillmentResponse requested = new FulfillmentResponse(); + FulfillmentMethodResponse requestedMethod = new FulfillmentMethodResponse(); + requestedMethod.setType(FulfillmentMethodResponse.TypeEnum.SHIPPING); + ShippingDestinationResponse newDest = + new ShippingDestinationResponse() + .id("dest_new") + .streetAddress("456 New St") + .addressCountry("US") + .postalCode("12345"); + requestedMethod.addDestinationsItem(newDest); + requested.addMethodsItem(requestedMethod); + + fulfillmentManager.handleFulfillment(checkout, requested); + + FulfillmentResponse fr = extensionsHelper.getFulfillmentResponse(checkout); + FulfillmentMethodResponse method = fr.getMethods().get(0); + assertEquals(2, method.getDestinations().size()); + assertTrue( + method.getDestinations().stream() + .anyMatch(d -> "addr_1".equals(FulfillmentUtils.getId(d)))); + assertTrue( + method.getDestinations().stream() + .anyMatch(d -> "dest_new".equals(FulfillmentUtils.getId(d)))); + } + + @Test + void handleFulfillment_shouldAddError_whenNoDestinationsFound() { + when(addressRepository.findByCustomerEmail(buyerEmail)).thenReturn(Collections.emptyList()); + + fulfillmentManager.handleFulfillment(checkout, null); + + assertTrue( + checkout.getMessages().stream() + .anyMatch( + m -> + m instanceof MessageError + && "no_fulfillment_destinations".equals(((MessageError) m).getCode()))); + } + + @Test + void handleFulfillment_shouldAddError_whenMultipleMethodsRequested() { + FulfillmentResponse requested = new FulfillmentResponse(); + requested.addMethodsItem( + new FulfillmentMethodResponse().type(FulfillmentMethodResponse.TypeEnum.SHIPPING)); + requested.addMethodsItem( + new FulfillmentMethodResponse().type(FulfillmentMethodResponse.TypeEnum.PICKUP)); + + fulfillmentManager.handleFulfillment(checkout, requested); + + assertTrue( + checkout.getMessages().stream() + .anyMatch( + m -> + m instanceof MessageError + && "multiple_fulfillment_methods_not_supported" + .equals(((MessageError) m).getCode()))); + } + + @Test + void handleFulfillment_shouldApplyFreeShipping_whenThresholdMet() { + PromotionEntity freeShippingPromo = + new PromotionEntity("promo_1", "Free Shipping", "free_shipping", 500); + when(promotionRepository.findAll()).thenReturn(List.of(freeShippingPromo)); + when(shippingRateRepository.findByCountryCodeOrCountryCode("default", "default")) + .thenReturn( + List.of( + new ShippingRateEntity( + "std-ship", "default", "standard", "Standard Shipping", 500))); + + // checkout subtotal is 1000, which is > 500 threshold + fulfillmentManager.handleFulfillment(checkout, null); + + FulfillmentResponse fr = extensionsHelper.getFulfillmentResponse(checkout); + FulfillmentOptionResponse option = + fr.getMethods().get(0).getGroups().get(0).getOptions().get(0); + assertEquals(0, option.getTotals().get(0).getAmount()); + assertTrue(option.getTitle().contains("(Free)")); + } + + @Test + void recalculateFulfillmentTotals_shouldSumTotalOfSelectedOptions() { + FulfillmentResponse fr = new FulfillmentResponse(); + FulfillmentMethodResponse method = new FulfillmentMethodResponse(); + method.setType(FulfillmentMethodResponse.TypeEnum.SHIPPING); + FulfillmentGroupResponse group = new FulfillmentGroupResponse(); + group.setSelectedOptionId(JsonNullable.of("opt_1")); + FulfillmentOptionResponse option = new FulfillmentOptionResponse().id("opt_1").title("Option 1"); + option.addTotalsItem(new TotalResponse().type(TotalResponse.TypeEnum.TOTAL).amount(750)); + group.addOptionsItem(option); + method.addGroupsItem(group); + fr.addMethodsItem(method); + + extensionsHelper.setFulfillment(checkout, fr); + + long total = fulfillmentManager.recalculateFulfillmentTotals(checkout); + assertEquals(750, total); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/InventoryManagerTest.java b/rest/java-spring/src/test/java/com/dev/ucp/InventoryManagerTest.java new file mode 100644 index 0000000..770730f --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/InventoryManagerTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import com.dev.ucp.entities.InventoryEntity; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.ItemResponse; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.MessageError; +import java.util.ArrayList; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +@SpringBootTest +public class InventoryManagerTest { + + @Autowired private InventoryManager inventoryManager; + + @MockBean private InventoryEntity.Repository inventoryRepository; + + private CheckoutResponse checkout; + + @BeforeEach + void setUp() { + checkout = new CheckoutResponse(); + checkout.setLineItems(new ArrayList<>()); + } + + private LineItemResponse createLineItem(String productId, String title, int quantity) { + LineItemResponse lineItem = new LineItemResponse(); + lineItem.setId(productId); // We can use the product ID as the line item ID for testing. + lineItem.setItem(new ItemResponse().id(productId).title(title)); + lineItem.setQuantity(quantity); + return lineItem; + } + + @Test + void validate_shouldAddMessage_whenInventoryNotFound() { + checkout.getLineItems().add(createLineItem("prod-1", "Product 1", 2)); + when(inventoryRepository.findById("prod-1")).thenReturn(Optional.empty()); + + inventoryManager.validate(checkout); + + assertEquals(1, checkout.getMessages().size()); + MessageError error = (MessageError) checkout.getMessages().get(0); + assertAll( + "Inventory Not Found Error", + () -> assertEquals("$.line_items[?(@.id=='prod-1')]", error.getPath())); + } + + @Test + void validate_shouldAddMessage_whenInsufficientStock() { + checkout.getLineItems().add(createLineItem("prod-1", "Product 1", 5)); + when(inventoryRepository.findById("prod-1")) + .thenReturn(Optional.of(new InventoryEntity("prod-1", 2))); + + inventoryManager.validate(checkout); + + assertEquals(1, checkout.getMessages().size()); + MessageError error = (MessageError) checkout.getMessages().get(0); + assertAll( + "Insufficient Stock Error", + () -> assertEquals("insufficient_stock", error.getCode()), + () -> assertEquals("$.line_items[?(@.id=='prod-1')]", error.getPath())); + } + + @Test + void validate_shouldPass_whenStockIsSufficient() { + checkout.getLineItems().add(createLineItem("prod-1", "Product 1", 2)); + when(inventoryRepository.findById("prod-1")) + .thenReturn(Optional.of(new InventoryEntity("prod-1", 10))); + + inventoryManager.validate(checkout); + + assertTrue(checkout.getMessages() == null || checkout.getMessages().isEmpty()); + } + + @Test + void reserveInventory_shouldThrowException_whenInsufficientStock() { + checkout.getLineItems().add(createLineItem("prod-1", "Product 1", 5)); + + when(inventoryRepository.findById("prod-1")) + .thenReturn(Optional.of(new InventoryEntity("prod-1", 2))); + + assertThrows(IllegalStateException.class, () -> inventoryManager.reserveInventory(checkout)); + } + + @Test + void reserveInventory_shouldUpdateStock_whenSufficientStock() { + InventoryEntity initialInventory = new InventoryEntity("prod-1", 10); + checkout.getLineItems().add(createLineItem("prod-1", "Product 1", 2)); + + when(inventoryRepository.findById("prod-1")).thenReturn(Optional.of(initialInventory)); + inventoryManager.reserveInventory(checkout); + + assertEquals(8, initialInventory.getQuantity()); // Verify stock is decremented to 8. + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/JacksonConfigTest.java b/rest/java-spring/src/test/java/com/dev/ucp/JacksonConfigTest.java new file mode 100644 index 0000000..b90d916 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/JacksonConfigTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.dev.ucp.service.shopping.model.CardCredential; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationRequest; +import com.dev.ucp.service.shopping.model.FulfillmentDestinationResponse; +import com.dev.ucp.service.shopping.model.Message; +import com.dev.ucp.service.shopping.model.MessageError; +import com.dev.ucp.service.shopping.model.MessageInfo; +import com.dev.ucp.service.shopping.model.MessageWarning; +import com.dev.ucp.service.shopping.model.PaymentCredential; +import com.dev.ucp.service.shopping.model.RetailLocationRequest; +import com.dev.ucp.service.shopping.model.RetailLocationResponse; +import com.dev.ucp.service.shopping.model.ShippingDestinationRequest; +import com.dev.ucp.service.shopping.model.ShippingDestinationResponse; +import com.dev.ucp.service.shopping.model.TokenCredentialResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openapitools.jackson.nullable.JsonNullable; + +public class JacksonConfigTest { + + private ObjectMapper objectMapper; + + @BeforeEach + public void setUp() { + JacksonConfig config = new JacksonConfig(); + objectMapper = new ObjectMapper(); + objectMapper.registerModule(config.jsonNullableModule()); + objectMapper.registerModule(config.ucpPolymorphicModule()); + } + + /** Helper class for testing JsonNullable fields. */ + private static class NullableWrapper { + public JsonNullable value = JsonNullable.undefined(); + } + + @Test + public void testJsonNullable() throws Exception { + String json = "{\"value\": null}"; + NullableWrapper wrapper = objectMapper.readValue(json, NullableWrapper.class); + assertTrue(wrapper.value.isPresent()); + assertNull(wrapper.value.get()); + + json = "{}"; + wrapper = objectMapper.readValue(json, NullableWrapper.class); + assertFalse(wrapper.value.isPresent()); + } + + @Test + public void testPaymentCredentialPolymorphism() throws Exception { + // 1. CardCredential (identified by card_number_type) + String cardJson = "{\"card_number_type\": \"fpan\", \"number\": \"1234\"}"; + PaymentCredential card = objectMapper.readValue(cardJson, PaymentCredential.class); + assertInstanceOf(CardCredential.class, card); + + // 2. TokenCredentialResponse (fallback) + String tokenJson = "{\"token\": \"some-token\", \"type\": \"mock\"}"; + PaymentCredential token = objectMapper.readValue(tokenJson, PaymentCredential.class); + assertInstanceOf(TokenCredentialResponse.class, token); + } + + @Test + public void testMessagePolymorphism() throws Exception { + // 1. Error + String errorJson = "{\"type\": \"error\", \"code\": \"err_code\"}"; + Message error = objectMapper.readValue(errorJson, Message.class); + assertInstanceOf(MessageError.class, error); + assertEquals("err_code", ((MessageError) error).getCode()); + + // 2. Warning + String warningJson = "{\"type\": \"warning\", \"content\": \"warn\"}"; + Message warning = objectMapper.readValue(warningJson, Message.class); + assertInstanceOf(MessageWarning.class, warning); + + // 3. Info + String infoJson = "{\"type\": \"info\", \"content\": \"info\"}"; + Message info = objectMapper.readValue(infoJson, Message.class); + assertInstanceOf(MessageInfo.class, info); + } + + @Test + public void testFulfillmentDestinationResponsePolymorphism() throws Exception { + // 1. RetailLocationResponse (has 'name') + String retailJson = "{\"id\": \"loc-1\", \"name\": \"Store 1\"}"; + FulfillmentDestinationResponse retail = + objectMapper.readValue(retailJson, FulfillmentDestinationResponse.class); + assertInstanceOf(RetailLocationResponse.class, retail); + assertEquals("Store 1", ((RetailLocationResponse) retail).getName()); + + // 2. ShippingDestinationResponse (no 'name') + String shippingJson = "{\"id\": \"dest-1\", \"address_country\": \"US\"}"; + FulfillmentDestinationResponse shipping = + objectMapper.readValue(shippingJson, FulfillmentDestinationResponse.class); + assertInstanceOf(ShippingDestinationResponse.class, shipping); + assertEquals("dest-1", ((ShippingDestinationResponse) shipping).getId()); + } + + @Test + public void testFulfillmentDestinationRequestPolymorphism() throws Exception { + // 1. RetailLocationRequest (has 'name') + String retailJson = "{\"name\": \"Store 1\"}"; + FulfillmentDestinationRequest retail = + objectMapper.readValue(retailJson, FulfillmentDestinationRequest.class); + assertInstanceOf(RetailLocationRequest.class, retail); + + // 2. ShippingDestinationRequest (no 'name') + String shippingJson = "{\"address_country\": \"CA\"}"; + FulfillmentDestinationRequest shipping = + objectMapper.readValue(shippingJson, FulfillmentDestinationRequest.class); + assertInstanceOf(ShippingDestinationRequest.class, shipping); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/LineItemConverterTest.java b/rest/java-spring/src/test/java/com/dev/ucp/LineItemConverterTest.java new file mode 100644 index 0000000..e0e749d --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/LineItemConverterTest.java @@ -0,0 +1,134 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +import com.dev.ucp.entities.ProductEntity; +import com.dev.ucp.exceptions.InvalidRequestException; +import com.dev.ucp.service.shopping.model.ItemCreateRequest; +import com.dev.ucp.service.shopping.model.ItemUpdateRequest; +import com.dev.ucp.service.shopping.model.LineItemCreateRequest; +import com.dev.ucp.service.shopping.model.LineItemResponse; +import com.dev.ucp.service.shopping.model.LineItemUpdateRequest; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; + +@SpringBootTest +public class LineItemConverterTest { + + @Autowired private LineItemConverter lineItemConverter; + + @MockBean private ProductEntity.Repository productRepository; + + private LineItemCreateRequest createLineItemCreateRequest(String productId, int quantity) { + LineItemCreateRequest requestItem = new LineItemCreateRequest(); + requestItem.setItem(new ItemCreateRequest().id(productId)); + requestItem.setQuantity(quantity); + return requestItem; + } + + private LineItemUpdateRequest createLineItemUpdateRequest( + String lineItemId, String productId, int quantity) { + LineItemUpdateRequest requestItem = new LineItemUpdateRequest(); + requestItem.setId(lineItemId); + requestItem.setItem(new ItemUpdateRequest().id(productId)); + requestItem.setQuantity(quantity); + return requestItem; + } + + @Test + void convertFromCreateRequest_shouldConvertSuccessfully() { + ProductEntity product = new ProductEntity("prod-1", "Product 1", 1000); + when(productRepository.findById("prod-1")).thenReturn(Optional.of(product)); + + List requestItems = new ArrayList<>(); + requestItems.add(createLineItemCreateRequest("prod-1", 2)); + + List responseItems = lineItemConverter.convertFromCreateRequest(requestItems); + + assertEquals(1, responseItems.size()); + LineItemResponse responseItem = responseItems.get(0); + assertNotNull(responseItem.getItem()); + + assertAll( + "Line Item Conversion from Create Request", + () -> assertNotNull(responseItem.getId()), + () -> assertEquals("prod-1", responseItem.getItem().getId()), + () -> assertEquals("Product 1", responseItem.getItem().getTitle()), + () -> assertEquals(1000, responseItem.getItem().getPrice()), + () -> assertEquals(2, responseItem.getQuantity()), + () -> assertNotNull(responseItem.getTotals()), + () -> assertEquals(2, responseItem.getTotals().size()), + () -> assertEquals(2000, responseItem.getTotals().get(0).getAmount()), + () -> assertEquals(2000, responseItem.getTotals().get(1).getAmount())); + } + + @Test + void convertFromCreateRequest_shouldThrowException_whenProductNotFound() { + when(productRepository.findById("prod-1")).thenReturn(Optional.empty()); + + List requestItems = new ArrayList<>(); + requestItems.add(createLineItemCreateRequest("prod-1", 2)); + + InvalidRequestException exception = + assertThrows( + InvalidRequestException.class, + () -> lineItemConverter.convertFromCreateRequest(requestItems)); + + assertEquals("Product not found: prod-1", exception.getMessage()); + } + + @Test + void convertFromUpdateRequest_shouldPreserveExistingId() { + ProductEntity product = new ProductEntity("prod-1", "Product 1", 1000); + when(productRepository.findById("prod-1")).thenReturn(Optional.of(product)); + + String existingId = UUID.randomUUID().toString(); + List requestItems = new ArrayList<>(); + requestItems.add(createLineItemUpdateRequest(existingId, "prod-1", 2)); + + List responseItems = lineItemConverter.convertFromUpdateRequest(requestItems); + + assertEquals(1, responseItems.size()); + assertEquals(existingId, responseItems.get(0).getId()); + } + + @Test + void convertFromUpdateRequest_shouldGenerateNewId_whenIdNotProvided() { + ProductEntity product = new ProductEntity("prod-1", "Product 1", 1000); + when(productRepository.findById("prod-1")).thenReturn(Optional.of(product)); + + List requestItems = new ArrayList<>(); + requestItems.add(createLineItemUpdateRequest(null, "prod-1", 2)); + + List responseItems = lineItemConverter.convertFromUpdateRequest(requestItems); + + assertEquals(1, responseItems.size()); + assertNotNull(responseItems.get(0).getId()); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/OrderWebHookNotifierTest.java b/rest/java-spring/src/test/java/com/dev/ucp/OrderWebHookNotifierTest.java new file mode 100644 index 0000000..11ea1cb --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/OrderWebHookNotifierTest.java @@ -0,0 +1,106 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.dev.ucp.service.shopping.model.OrderConfirmation; +import com.dev.ucp.service.shopping.model.UCPCheckoutResponse; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Answers; +import org.springframework.web.client.RestClient; + +public class OrderWebHookNotifierTest { + + private RestClient.Builder restClientBuilder; + private RestClient restClient; + private ObjectMapper objectMapper; + private ExtensionsHelper extensionsHelper; + private OrderWebHookNotifier notifier; + + @BeforeEach + public void setUp() throws Exception { + restClientBuilder = mock(RestClient.Builder.class); + restClient = mock(RestClient.class, Answers.RETURNS_DEEP_STUBS); + objectMapper = mock(ObjectMapper.class); + extensionsHelper = mock(ExtensionsHelper.class); + + when(restClientBuilder.build()).thenReturn(restClient); + notifier = new OrderWebHookNotifier(restClientBuilder); + + // Inject mocks into private fields + setPrivateField(notifier, "objectMapper", objectMapper); + setPrivateField(notifier, "extensionsHelper", extensionsHelper); + } + + private void setPrivateField(Object target, String fieldName, Object value) throws Exception { + var field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + } + + @Test + public void testNotifyLifecycle() throws Exception { + String webhookUrl = "http://localhost:8284/webhook"; + String orderId = "order-123"; + + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setId("checkout-123"); + + UCPCheckoutResponse ucp = new UCPCheckoutResponse(); + ucp.setVersion("2026-01-11"); + checkout.setUcp(ucp); + + OrderConfirmation order = new OrderConfirmation(); + order.setId(orderId); + checkout.setOrder(order); + + when(objectMapper.writeValueAsString(any())).thenReturn("{}"); + when(extensionsHelper.getFulfillmentResponse(any())).thenReturn(new FulfillmentResponse()); + + // Run notify in background as it enters a wait state + CompletableFuture future = + CompletableFuture.runAsync( + () -> { + notifier.notify(webhookUrl, checkout); + }); + + // Short sleep to ensure the notifier has sent order_placed and is waiting + Thread.sleep(200); + + // Trigger the shipping event + notifier.triggerShipping(orderId); + + // Wait for the async task to complete + future.get(5, TimeUnit.SECONDS); + + // Verify that restClient.post() was called twice (placed + shipped) + verify(restClient, times(2)).post(); + verify(objectMapper, atLeastOnce()).writeValueAsString(any()); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/SpringDemoAppTests.java b/rest/java-spring/src/test/java/com/dev/ucp/SpringDemoAppTests.java new file mode 100644 index 0000000..25ec7a4 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/SpringDemoAppTests.java @@ -0,0 +1,27 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SpringDemoAppTests { + + @Test + void contextLoads() {} +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/actions/CancelCheckoutActionTest.java b/rest/java-spring/src/test/java/com/dev/ucp/actions/CancelCheckoutActionTest.java new file mode 100644 index 0000000..72248c4 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/actions/CancelCheckoutActionTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +public class CancelCheckoutActionTest { + + @Autowired private CancelCheckoutAction cancelCheckoutAction; + + @Autowired private CheckoutSessionEntity.Repository checkoutSessionRepository; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private String checkoutId; + + @BeforeEach + public void setup() throws JsonProcessingException { + checkoutId = UUID.randomUUID().toString(); + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setId(checkoutId); + checkout.setStatus(CheckoutResponse.StatusEnum.READY_FOR_COMPLETE); + + CheckoutSessionEntity entity = new CheckoutSessionEntity(); + entity.setId(checkoutId); + entity.setStatus(checkout.getStatus().toString()); + entity.setData(objectMapper.writeValueAsString(checkout)); + checkoutSessionRepository.save(entity); + } + + @Test + public void testCancelCheckout_happyPath() { + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result = cancelCheckoutAction.cancelCheckout(checkoutId, idempotencyKey); + + assertNotNull(result); + assertEquals(checkoutId, result.getId()); + assertEquals(CheckoutResponse.StatusEnum.CANCELED, result.getStatus()); + } + + @Test + public void testCancelCheckout_idempotency() { + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result1 = cancelCheckoutAction.cancelCheckout(checkoutId, idempotencyKey); + CheckoutResponse result2 = cancelCheckoutAction.cancelCheckout(checkoutId, idempotencyKey); + + assertEquals(result1, result2); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/actions/CompleteCheckoutActionTest.java b/rest/java-spring/src/test/java/com/dev/ucp/actions/CompleteCheckoutActionTest.java new file mode 100644 index 0000000..dbb3852 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/actions/CompleteCheckoutActionTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.dev.ucp.ExtensionsHelper; +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.service.shopping.model.CardPaymentInstrument; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CompleteCheckoutRequest; +import com.dev.ucp.service.shopping.model.FulfillmentGroupResponse; +import com.dev.ucp.service.shopping.model.FulfillmentMethodResponse; +import com.dev.ucp.service.shopping.model.FulfillmentResponse; +import com.dev.ucp.service.shopping.model.TokenCredentialResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.TextNode; +import java.util.Collections; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openapitools.jackson.nullable.JsonNullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +public class CompleteCheckoutActionTest { + + @Autowired private CompleteCheckoutAction completeCheckoutAction; + + @Autowired private CheckoutSessionEntity.Repository checkoutSessionRepository; + + @Autowired private ExtensionsHelper extensionsHelper; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private String checkoutId; + + @BeforeEach + public void setup() throws JsonProcessingException { + checkoutId = UUID.randomUUID().toString(); + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setId(checkoutId); + checkout.setStatus(CheckoutResponse.StatusEnum.READY_FOR_COMPLETE); + + FulfillmentGroupResponse group = new FulfillmentGroupResponse(); + group.setSelectedOptionId(JsonNullable.of("shipping-standard")); + + FulfillmentMethodResponse method = new FulfillmentMethodResponse(); + method.setType(FulfillmentMethodResponse.TypeEnum.SHIPPING); + method.setSelectedDestinationId(JsonNullable.of("dest-1")); + method.setGroups(Collections.singletonList(group)); + + FulfillmentResponse fulfillment = new FulfillmentResponse(); + fulfillment.setMethods(Collections.singletonList(method)); + extensionsHelper.setFulfillment(checkout, fulfillment); + + CheckoutSessionEntity entity = new CheckoutSessionEntity(); + entity.setId(checkoutId); + entity.setStatus(checkout.getStatus().toString()); + entity.setData(objectMapper.writeValueAsString(checkout)); + checkoutSessionRepository.save(entity); + } + + @Test + public void testCompleteCheckout_happyPath() { + UUID idempotencyKey = UUID.randomUUID(); + CompleteCheckoutRequest request = new CompleteCheckoutRequest(); + request.setPaymentData(createValidPaymentInstrument()); + + CheckoutResponse result = + completeCheckoutAction.completeCheckout(checkoutId, idempotencyKey, request, null); + + assertNotNull(result); + assertNotNull(result.getOrder().getId()); + assertNotNull(result.getOrder().getPermalinkUrl()); + } + + @Test + public void testCompleteCheckout_idempotency() { + UUID idempotencyKey = UUID.randomUUID(); + CompleteCheckoutRequest request = new CompleteCheckoutRequest(); + request.setPaymentData(createValidPaymentInstrument()); + + CheckoutResponse result1 = + completeCheckoutAction.completeCheckout(checkoutId, idempotencyKey, request, null); + CheckoutResponse result2 = + completeCheckoutAction.completeCheckout(checkoutId, idempotencyKey, request, null); + + assertEquals(result1, result2); + } + + private CardPaymentInstrument createValidPaymentInstrument() { + CardPaymentInstrument instrument = new CardPaymentInstrument(); + instrument.setId("instr-1"); + instrument.setHandlerId("mock_payment_handler"); + instrument.setType(CardPaymentInstrument.TypeEnum.CARD); + instrument.setBrand("Visa"); + instrument.setLastDigits("1111"); + + TokenCredentialResponse credential = new TokenCredentialResponse(); + credential.setType("mock_token"); + credential.putAdditionalProperty("token", new TextNode("success_token")); + instrument.setCredential(credential); + + return instrument; + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/actions/CreateCheckoutActionTest.java b/rest/java-spring/src/test/java/com/dev/ucp/actions/CreateCheckoutActionTest.java new file mode 100644 index 0000000..87af6b4 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/actions/CreateCheckoutActionTest.java @@ -0,0 +1,154 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.entities.IdempotencyRecordEntity; +import com.dev.ucp.exceptions.IdempotencyConflictException; +import com.dev.ucp.service.shopping.model.Buyer; +import com.dev.ucp.service.shopping.model.CheckoutCreateRequest; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.ItemCreateRequest; +import com.dev.ucp.service.shopping.model.LineItemCreateRequest; +import com.dev.ucp.service.shopping.model.PaymentCreateRequest; +import java.util.Collections; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +@Sql(scripts = "/data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(scripts = "/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) +public class CreateCheckoutActionTest { + + @Autowired private CreateCheckoutAction createCheckoutAction; + + @Autowired private IdempotencyRecordEntity.Repository idempotencyRecordRepository; + + @Autowired private CheckoutSessionEntity.Repository checkoutSessionRepository; + + @Test + public void testCreateCheckoutSession_happyPath() { + + CheckoutCreateRequest checkout = new CheckoutCreateRequest(); + + Buyer buyer = new Buyer(); + + buyer.setEmail("john.doe@example.com"); + + checkout.setBuyer(buyer); + + checkout.setCurrency("USD"); + + LineItemCreateRequest lineItem = new LineItemCreateRequest(); + ItemCreateRequest item = new ItemCreateRequest(); + item.setId("bouquet_roses"); + lineItem.setItem(item); + lineItem.setQuantity(1); + checkout.setLineItems(Collections.singletonList(lineItem)); + checkout.setPayment(new PaymentCreateRequest()); + + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result = createCheckoutAction.createCheckoutSession(idempotencyKey, checkout); + + assertNotNull(result); + assertEquals(CheckoutResponse.StatusEnum.READY_FOR_COMPLETE, result.getStatus()); + assertNotNull(result.getId()); + assertEquals(1, result.getLineItems().size()); + + assertEquals(1, idempotencyRecordRepository.count()); + assertEquals(1, checkoutSessionRepository.count()); + } + + @Test + public void testCreateCheckoutSession_idempotency() { + + CheckoutCreateRequest checkout = new CheckoutCreateRequest(); + + Buyer buyer = new Buyer(); + + buyer.setEmail("john.doe@example.com"); + + checkout.setBuyer(buyer); + + checkout.setCurrency("USD"); + + LineItemCreateRequest lineItem = new LineItemCreateRequest(); + ItemCreateRequest item = new ItemCreateRequest(); + item.setId("bouquet_roses"); + lineItem.setItem(item); + lineItem.setQuantity(1); + checkout.setLineItems(Collections.singletonList(lineItem)); + checkout.setPayment(new PaymentCreateRequest()); + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result1 = createCheckoutAction.createCheckoutSession(idempotencyKey, checkout); + CheckoutResponse result2 = createCheckoutAction.createCheckoutSession(idempotencyKey, checkout); + + assertEquals(result1.getId(), result2.getId()); + assertEquals(result1.getLineItems().size(), result2.getLineItems().size()); + assertEquals(1, idempotencyRecordRepository.count()); + assertEquals(1, checkoutSessionRepository.count()); + } + + @Test + public void testCreateCheckoutSession_idempotencyKeyReusedWithDifferentPayload() { + UUID idempotencyKey = UUID.randomUUID(); + CheckoutCreateRequest checkout1 = new CheckoutCreateRequest(); + Buyer buyer1 = new Buyer(); + buyer1.setEmail("test1@example.com"); + checkout1.setBuyer(buyer1); + checkout1.setCurrency("USD"); + LineItemCreateRequest lineItem1 = new LineItemCreateRequest(); + ItemCreateRequest item1 = new ItemCreateRequest(); + item1.setId("bouquet_roses"); + lineItem1.setItem(item1); + lineItem1.setQuantity(1); + checkout1.setLineItems(Collections.singletonList(lineItem1)); + checkout1.setPayment(new PaymentCreateRequest()); + + createCheckoutAction.createCheckoutSession(idempotencyKey, checkout1); + + CheckoutCreateRequest checkout2 = new CheckoutCreateRequest(); + Buyer buyer2 = new Buyer(); + buyer2.setEmail("test2@example.com"); + checkout2.setBuyer(buyer2); + checkout2.setCurrency("USD"); + LineItemCreateRequest lineItem2 = new LineItemCreateRequest(); + ItemCreateRequest item2 = new ItemCreateRequest(); + item2.setId("bouquet_roses"); + lineItem2.setItem(item2); + lineItem2.setQuantity(1); + checkout2.setLineItems(Collections.singletonList(lineItem2)); + checkout2.setPayment(new PaymentCreateRequest()); + + assertThrows( + IdempotencyConflictException.class, + () -> { + createCheckoutAction.createCheckoutSession(idempotencyKey, checkout2); + }); + } +} \ No newline at end of file diff --git a/rest/java-spring/src/test/java/com/dev/ucp/actions/GetCheckoutActionTest.java b/rest/java-spring/src/test/java/com/dev/ucp/actions/GetCheckoutActionTest.java new file mode 100644 index 0000000..3cd08fc --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/actions/GetCheckoutActionTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.exceptions.ResourceNotFoundException; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +public class GetCheckoutActionTest { + + @Autowired private GetCheckoutAction getCheckoutAction; + + @Autowired private CheckoutSessionEntity.Repository checkoutSessionRepository; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void testGetCheckout_found() throws JsonProcessingException { + String checkoutId = UUID.randomUUID().toString(); + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setId(checkoutId); + + CheckoutSessionEntity entity = new CheckoutSessionEntity(); + entity.setId(checkoutId); + entity.setData(objectMapper.writeValueAsString(checkout)); + checkoutSessionRepository.save(entity); + + CheckoutResponse result = getCheckoutAction.getCheckout(checkoutId); + + assertNotNull(result); + assertEquals(checkoutId, result.getId()); + } + + @Test + public void testGetCheckout_notFound() { + assertThrows( + ResourceNotFoundException.class, + () -> { + getCheckoutAction.getCheckout(UUID.randomUUID().toString()); + }); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/actions/UpdateCheckoutActionTest.java b/rest/java-spring/src/test/java/com/dev/ucp/actions/UpdateCheckoutActionTest.java new file mode 100644 index 0000000..6880c49 --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/actions/UpdateCheckoutActionTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.actions; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.dev.ucp.entities.CheckoutSessionEntity; +import com.dev.ucp.exceptions.IdempotencyConflictException; +import com.dev.ucp.service.shopping.model.CheckoutResponse; +import com.dev.ucp.service.shopping.model.CheckoutUpdateRequest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +@SpringBootTest +@Transactional +public class UpdateCheckoutActionTest { + + @Autowired private UpdateCheckoutAction updateCheckoutAction; + + @Autowired private CheckoutSessionEntity.Repository checkoutSessionRepository; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private String checkoutId; + + @BeforeEach + public void setup() throws JsonProcessingException { + checkoutId = UUID.randomUUID().toString(); + CheckoutResponse checkout = new CheckoutResponse(); + checkout.setId(checkoutId); + checkout.setStatus(CheckoutResponse.StatusEnum.READY_FOR_COMPLETE); + + CheckoutSessionEntity entity = new CheckoutSessionEntity(); + entity.setId(checkoutId); + entity.setStatus(checkout.getStatus().toString()); + entity.setData(objectMapper.writeValueAsString(checkout)); + checkoutSessionRepository.save(entity); + } + + @Test + public void testUpdateCheckout_happyPath() { + CheckoutUpdateRequest updatedCheckout = new CheckoutUpdateRequest(); + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result = + updateCheckoutAction.updateCheckout(checkoutId, idempotencyKey, updatedCheckout); + + assertNotNull(result); + assertEquals(checkoutId, result.getId()); + } + + @Test + public void testUpdateCheckout_idempotency() { + CheckoutUpdateRequest updatedCheckout = new CheckoutUpdateRequest(); + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutResponse result1 = + updateCheckoutAction.updateCheckout(checkoutId, idempotencyKey, updatedCheckout); + CheckoutResponse result2 = + updateCheckoutAction.updateCheckout(checkoutId, idempotencyKey, updatedCheckout); + + assertEquals(result1, result2); + } + + @Test + public void testUpdateCheckout_idempotencyKeyReusedWithDifferentPayload() { + UUID idempotencyKey = UUID.randomUUID(); + + CheckoutUpdateRequest checkout1 = new CheckoutUpdateRequest(); + updateCheckoutAction.updateCheckout(checkoutId, idempotencyKey, checkout1); + + CheckoutUpdateRequest checkout2 = new CheckoutUpdateRequest(); + checkout2.setCurrency("USD"); + + assertThrows( + IdempotencyConflictException.class, + () -> { + updateCheckoutAction.updateCheckout(checkoutId, idempotencyKey, checkout2); + }); + } +} diff --git a/rest/java-spring/src/test/java/com/dev/ucp/service/UCPExceptionControllerAdviceTest.java b/rest/java-spring/src/test/java/com/dev/ucp/service/UCPExceptionControllerAdviceTest.java new file mode 100644 index 0000000..2ea169a --- /dev/null +++ b/rest/java-spring/src/test/java/com/dev/ucp/service/UCPExceptionControllerAdviceTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2026 UCP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.dev.ucp.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.dev.ucp.exceptions.InvalidRequestException; +import com.dev.ucp.exceptions.ResourceNotFoundException; +import com.dev.ucp.service.shopping.model.MessageError; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +public class UCPExceptionControllerAdviceTest { + + private final UCPExceptionControllerAdvice advice = new UCPExceptionControllerAdvice(); + + @Test + public void testHandleInvalidRequest() { + InvalidRequestException ex = new InvalidRequestException("Invalid request message"); + ResponseEntity response = + advice.handleAllExceptions(ex); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertNotNull(response.getBody()); + assertEquals("requires_escalation", response.getBody().status()); + + MessageError error = (MessageError) response.getBody().messages().get(0); + assertEquals("invalid_request", error.getCode()); + assertEquals("Invalid request message", error.getContent()); + } + + @Test + public void testHandleResourceNotFound() { + ResourceNotFoundException ex = new ResourceNotFoundException("Not found"); + ResponseEntity response = + advice.handleAllExceptions(ex); + + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + assertNotNull(response.getBody()); + + MessageError error = (MessageError) response.getBody().messages().get(0); + assertEquals("resource_not_found", error.getCode()); + } + + @Test + public void testHandleIllegalArgument() { + IllegalArgumentException ex = new IllegalArgumentException("Illegal arg"); + ResponseEntity response = + advice.handleIllegalArgument(ex); + + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + assertNotNull(response.getBody()); + + MessageError error = (MessageError) response.getBody().messages().get(0); + assertEquals("invalid_parameters", error.getCode()); + assertEquals("Illegal arg", error.getContent()); + } + + @Test + public void testHandleGeneralException() { + Exception ex = new Exception("Internal"); + ResponseEntity response = + advice.handleAllExceptions(ex); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); + assertNotNull(response.getBody()); + + MessageError error = (MessageError) response.getBody().messages().get(0); + assertEquals("internal_error", error.getCode()); + assertEquals("An unexpected error occurred.", error.getContent()); + } +}