SFCC Client
1. Descripción general
sfcc-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con las APIs OCAPI y Shop de Salesforce Commerce Cloud (SFCC). El proyecto no expone ningún endpoint REST propio; se publica en el registro de artefactos Maven interno y es consumido por otros microservicios del ecosistema Hawkers que necesiten gestionar cupones/códigos promocionales (OCAPI, producción y staging), consultar productos (Shop API) y actualizar el estado/tracking de envíos de un pedido (Shop API, PATCH).
La librería expone seis clientes HTTP declarativos, cada uno con su propia autoconfiguración y, en varios casos, su propio flujo de autenticación OAuth2 con caché de token independiente.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | sfcc-client |
groupId | com.hawkersco |
version | 1.0.25-SNAPSHOT |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | JAR (librería, no ejecutable) |
| Módulos | Proyecto único (no multi-módulo) |
3. Arquitectura y diseño
Estructura del proyecto:
com.hawkersco.sfccclient
├── SfccClientApplication.java # Clase @SpringBootApplication (bootstrap de autoconfiguración, no desplegable)
├── client/
│ ├── SfccClient.java # OCAPI producción: cupones, códigos, búsqueda de productos y cupones
│ ├── SfccStagingClient.java # OCAPI staging: subconjunto de operaciones de cupones
│ ├── SfccShopClient.java # Shop API: consulta de productos por SKU
│ ├── SfccShopShipmentClient.java # Shop API: PATCH de envíos/estado/tracking de un pedido
│ ├── SfccAuthClient.java # Interfaz funcional para obtención de token OCAPI (implementada como lambda, no vía proxy)
│ └── SfccAuthShopShipmentClient.java # Obtención de token para el flujo de envíos (grant type DWSID)
├── config/
│ ├── SfccClientAutoConfiguration.java # @AutoConfiguration de SfccClient (token cacheado 15 min)
│ ├── SfccStagingClientAutoConfiguration.java # @AutoConfiguration de SfccStagingClient (token cacheado 15 min)
│ ├── SfccShopClientAutoConfiguration.java # @AutoConfiguration de SfccShopClient (sin autenticación)
│ ├── SfccShopShipmentClientAutoConfiguration.java # @AutoConfiguration de SfccShopShipmentClient (token cacheado 1 min)
│ ├── SfccAuthClientAutoConfiguration.java # @AutoConfiguration que implementa SfccAuthClient como lambda (Basic Auth + form-urlencoded)
│ └── SfccAuthShopShipmentAutoConfiguration.java # @AutoConfiguration de SfccAuthShopShipmentClient (Basic Auth estática)
├── dto/
│ ├── AuthForm.java, Codes.java
│ ├── request/ — CodesSfccSearchRequest, CouponSfccSearchAllRequest, CouponSfccSearchByFieldRequest, ShipmentSfccRequest, StateSfccRequest(Alt), StateSfccTrackingRequest
│ └── response/ — ApiResponse, CodesByCouponsResponse, CouponRedemptionResponse, FashionaliaProductsResponse, SfccProductsResponse
└── service/
├── CacheStore.java # Caché genérica en memoria (Guava)
└── CacheStoreBeans.java # Bean CacheStore<String> de propósito general (TTL 30 min), no usado por las autoconfiguraciones de token (ver sección 13)
Flujo — OCAPI con token cacheado (SfccClient/SfccStagingClient)
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as SfccClient
participant Cache as CacheStore (15 min)
participant AuthClient as SfccAuthClient (lambda)
participant SFCC as OCAPI SFCC
Consumidor->>Client: getCoupons / searchCoupon / addCodes / ...
Client->>Cache: get("token")
alt Token en caché
Cache-->>Client: token válido
else Token ausente/expirado
Client->>AuthClient: getToken(grant_type=client_credentials)
AuthClient->>SFCC: POST /dwsso/oauth2/access_token (Basic Auth + form-urlencoded)
SFCC-->>AuthClient: { "access_token": "..." }
AuthClient-->>Client: token
Client->>Cache: add("token", "Bearer <token>")
end
Client->>SFCC: request + Authorization: Bearer <token>
SFCC-->>Client: ResponseEntity<T>
SfccClientAutoConfiguration y SfccStagingClientAutoConfiguration se activan con @ConditionalOnProperty sobre sfcc.client.url/sfcc.staging.client.url respectivamente, y además con @ConditionalOnBean(SfccAuthClient.class) — solo se registran si el bean SfccAuthClient (de SfccAuthClientAutoConfiguration) ya existe en el contexto, garantizado con @AutoConfiguration(after = SfccAuthClientAutoConfiguration.class).
SfccAuthClientAutoConfiguration no construye SfccAuthClient mediante HttpServiceProxyFactory: dado que la interfaz solo tiene un método, se implementa como una expresión lambda que ejecuta directamente restClient.post()... — patrón distinto al resto de clientes del proyecto, documentado explícitamente en CLAUDE.md.
SfccClientAutoConfiguration registra además un HttpMessageConverter personalizado (rawJsonStringConverter) para permitir que ciertos métodos del cliente devuelvan ResponseEntity<String> con el JSON crudo sin que el converter por defecto interfiera.
Flujo — Shop API sin autenticación (SfccShopClient)
sequenceDiagram
participant Consumidor
participant Client as SfccShopClient
participant SFCC as Shop API SFCC
Consumidor->>Client: getProductsByIds(siteId, apiVersion, skus, clientId, expand)
Client->>SFCC: GET /s/{siteId}/dw/shop/{apiVersion}/products/({skus})?client_id=&expand=
SFCC-->>Client: ResponseEntity<String>
SfccShopClientAutoConfiguration no aplica ningún interceptor de autenticación: la Shop API pública de SFCC solo requiere el parámetro client_id en la query string.
Flujo — Envíos con token cacheado 1 minuto (SfccShopShipmentClient)
sequenceDiagram
participant Consumidor
participant Client as SfccShopShipmentClient
participant Cache as CacheStore (1 min)
participant AuthClient as SfccAuthShopShipmentClient
participant SFCC as Shop API SFCC
Consumidor->>Client: updateShipments / updateShipmentsStates / updateShipmentTracking
Client->>Cache: get("token")
alt Token ausente/expirado
Client->>AuthClient: getToken(clientId, grant_type=dwsid)
AuthClient->>SFCC: POST /dw/oauth2/access_token?client_id=&grant_type=urn:demandware:...:dwsid:dwsecuretoken (Basic Auth)
SFCC-->>AuthClient: { "access_token": "..." }
AuthClient-->>Client: token
Client->>Cache: add("token", "Bearer <token>")
end
Client->>SFCC: PATCH /s/{siteId}/dw/shop/{apiVersion}/orders/{orderId}?client_id= + Authorization
SFCC-->>Client: ResponseEntity<String>
El registro de todas las autoconfiguraciones se realiza mediante el fichero estándar de Spring Boot:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
4. Dependencias principales
| Dependencia | Versión | Propósito |
|---|---|---|
spring-boot-starter | (gestionada SB4) | Base de Spring Boot (contexto, autoconfiguración) |
spring-web | (gestionada SB4) | RestClient + @HttpExchange / HttpServiceProxyFactory |
spring-boot-starter-cache | (gestionada SB4) | Declarada como dependencia; no se ha encontrado uso de @Cacheable/Spring Cache — la caché de tokens usa CacheStore propio (Guava), no Spring Cache |
com.google.guava:guava | 33.4.0-jre | Implementación de caché en CacheStore (CacheBuilder) |
org.json:json | 20240303 | Extracción de access_token de las respuestas de token (JSONObject) |
com.sun.xml.bind:jaxb-core / jaxb-impl | 4.0.5 | Soporte JAXB (declarado; no se ha localizado ningún modelo @XmlRootElement en este proyecto — pendiente de verificar su uso real) |
com.google.code.gson:gson | 2.11.0 | Anotaciones @SerializedName en los DTOs (soporte dual con Jackson) |
com.fasterxml.jackson.core:jackson-annotations / jackson-databind | (gestionada SB4) | Anotaciones y serialización Jackson en los DTOs |
org.projectlombok:lombok | 1.18.42 (dependencia; annotationProcessorPath del compilador usa 1.18.46) | Generación de boilerplate (@Data) en los DTOs |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
5. API / Endpoints
No aplica a este proyecto. sfcc-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre las APIs de SFCC se detallan en la sección 6.
6. Integraciones externas
OCAPI SFCC (producción) — SfccClient
| Método | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getCoupons (2 sobrecargas) | GET | /{apiVersion}/sites/{siteId}/coupons | Lista cupones de un sitio (con o sin paginación start/count) |
getCouponsNext | GET | /{next} | Continúa la paginación siguiendo el enlace next devuelto por SFCC |
getCodesByCoupons | GET | /{apiVersion}/sites/{siteId}/coupons/{couponId}/codes | Lista los códigos de un cupón |
addCodes | POST | /{apiVersion}/sites/{siteId}/coupons/{couponId}/multiple_codes | Añade códigos a un cupón |
deleteCode | POST | /{apiVersion}/sites/{siteId}/coupons/{couponId}/multiple_codes?delete= | Elimina códigos de un cupón |
searchProducts | POST | /{apiVersion}/product_search?site_id={siteId} | Búsqueda de productos (OCAPI) |
searchCoupon | POST | /{apiVersion}/sites/{siteId}/coupon_search | Búsqueda avanzada de cupones (body JSON crudo) |
searchCouponRedemption | POST | /{apiVersion}/sites/{siteId}/coupon_redemption_search | Búsqueda de redenciones de cupón |
OCAPI SFCC (staging) — SfccStagingClient
Subconjunto de SfccClient: getCoupons, getCodesByCoupons, addCodes, deleteCode (mismas rutas relativas, apuntando a sfcc.staging.client.url).
Shop API SFCC — SfccShopClient / SfccShopShipmentClient
| Método | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getProductsByIds | GET | /s/{siteId}/dw/shop/{apiVersion}/products/({skus})?client_id=&expand= | Consulta productos por lista de SKUs |
updateShipments | PATCH | /s/{siteId}/dw/shop/{apiVersion}/orders/{orderId}?client_id= | Actualiza los envíos (c_shipment_edit) de un pedido |
updateShipmentsStates (x2) | PATCH | /s/{siteId}/dw/shop/{apiVersion}/orders/{orderId}?client_id= | Actualiza el estado del envío (versión tipada y versión con String crudo) |
updateShipmentTracking | PATCH | /s/{siteId}/dw/shop/{apiVersion}/orders/{orderId}?client_id= | Actualiza la información de tracking del envío |
Ejemplo de payload updateShipments (ShipmentSfccRequest):
{
"c_shipment_edit": {
"shipments": [
{ "id": "SHIP-001", "products": ["SKU-001", "SKU-002"] }
]
}
}
Protocolo: HTTPS REST (JSON para OCAPI y Shop API; application/x-www-form-urlencoded para los endpoints de token). Autenticación: OAuth2 client credentials con Basic Auth para la obtención del token en SfccClient/SfccStagingClient/SfccShopShipmentClient (Bearer token resultante, cacheado 15 min o 1 min según el cliente); SfccShopClient no requiere autenticación más allá del client_id en la query.
7. Configuración
No se incluye ningún application.properties/application.yml en la librería (confirmado en CLAUDE.md: "no application.yml"). Las propiedades deben ser inyectadas por la aplicación consumidora.
Propiedades requeridas por cliente
| Cliente | Propiedades | Ejemplo de valor |
|---|---|---|
SfccAuthClient | sfcc.auth.client.url, sfcc.auth.client.client-id, sfcc.auth.client.client-secret | ${SFCC_AUTH_URL}, ${SFCC_CLIENT_ID}, ${SFCC_CLIENT_SECRET} |
SfccClient (además requiere SfccAuthClient activo) | sfcc.client.url | ${SFCC_CLIENT_URL} |
SfccStagingClient (además requiere SfccAuthClient activo) | sfcc.staging.client.url | ${SFCC_STAGING_CLIENT_URL} |
SfccShopClient | sfcc.shop.client.url | ${SFCC_SHOP_CLIENT_URL} |
SfccAuthShopShipmentClient | sfcc.client.url, sfcc.auth.basic | ${SFCC_CLIENT_URL}, ${SFCC_AUTH_BASIC} |
SfccShopShipmentClient (además requiere SfccAuthShopShipmentClient activo) | sfcc.client.url, sfcc.api.clientid | ${SFCC_CLIENT_URL}, ${SFCC_API_CLIENT_ID} |
Importante:
sfcc.client.urles compartida entreSfccAuthShopShipmentClientySfccShopShipmentClient(y coincide en nombre con la propiedad deSfccClient, aunque estos dos últimos son independientes entre sí:SfccClientno depende desfcc.api.clientidni deSfccAuthShopShipmentClient).
sfcc.auth.basic(paraSfccAuthShopShipmentClient) debe contener el valor Base64 ya codificado declient_id:client_secret;sfcc.auth.client.client-id/client-secret(paraSfccAuthClient) se codifican en Base64 automáticamente dentro de la autoconfiguración.
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
SFCC_AUTH_URL | sfcc.auth.client.url |
SFCC_CLIENT_ID | sfcc.auth.client.client-id |
SFCC_CLIENT_SECRET | sfcc.auth.client.client-secret |
SFCC_CLIENT_URL | sfcc.client.url |
SFCC_STAGING_CLIENT_URL | sfcc.staging.client.url |
SFCC_SHOP_CLIENT_URL | sfcc.shop.client.url |
SFCC_AUTH_BASIC | sfcc.auth.basic |
SFCC_API_CLIENT_ID | sfcc.api.clientid |
8. Persistencia
No aplica a este proyecto. La librería no accede a ninguna base de datos. El estado en memoria se limita a las cachés de token (CacheStore, Guava): 15 minutos para SfccClient/SfccStagingClient, 1 minuto para SfccShopShipmentClient, cada una con su propia instancia local (no comparten caché entre sí ni con el bean general de CacheStoreBeans, ver sección 13).
9. Procesos programados y mensajería
No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch.
10. Ejecución en local
sfcc-client es una librería JAR, no una aplicación ejecutable; SfccClientApplication existe únicamente para el bootstrap de autoconfiguración según CLAUDE.md. No tiene servidor embebido ni endpoint de health.
Requisitos previos
- Java 25
- Maven 3.x
- Acceso al registro de artefactos Maven interno (
europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven) para resolver/publicar dependencias.
Compilar e instalar en repositorio local
# Compilar sin tests
./mvnw clean install -DskipTests
# Compilar con tests
./mvnw clean install
# Ejecutar un test concreto
./mvnw test -Dtest=MyTestClass
Uso como dependencia en un microservicio consumidor
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>sfcc-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
Cada cliente se activa de forma independiente según las propiedades declaradas por el consumidor (ver sección 7); no es necesario configurar todos los clientes si solo se van a usar algunos.
11. Despliegue
El pipeline de Jenkins (Jenkinsfile) consta de dos etapas:
- Checkout — descarga el código del repositorio.
- Publish to Artifact Registry — ejecuta
mvn deploy -DskipTestspara publicar el JAR en Google Artifact Registry.
CLAUDE.md documenta un pipeline de cuatro etapas (Build, KICS Scan, SonarQube, Clean), que no se corresponde con el Jenkinsfile actual del repositorio (dos etapas: Checkout y Publish to Artifact Registry). Se documenta el Jenkinsfile realmente presente.
| Parámetro | Valor |
|---|---|
| JDK | JDK25 (tool Jenkins) |
| Maven | Maven3 (tool Jenkins) |
| Repositorio | europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven (Artifact Registry GCP) |
No existe Dockerfile ni despliegue como servicio independiente; el artefacto es un JAR publicado en el registro Maven.
Job de Jenkins:
https://jenkins-pi.hawkersco.net/job/sfcc-client/
12. Manejo de errores y logging
La librería no implementa ninguna estrategia propia de manejo de excepciones ni logging estructurado. Ningún método de los seis clientes declara throws explícito; las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. La extracción del token con new JSONObject(response.getBody()).getString("access_token") en las autoconfiguraciones lanzará una excepción no comprobada (JSONException) si la respuesta de autenticación no contiene ese campo, sin ningún manejo específico. No hay configuración de logback ni de niveles de log específicos en la librería.
13. Notas y consideraciones
-
CacheStoreBeans.token()no utilizado internamente: Igual que enmeli-clientyauro-client, existe un beanCacheStore<String>de propósito general (TTL 30 minutos) expuesto porCacheStoreBeans, pero cada autoconfiguración de cliente (SfccClientAutoConfiguration,SfccStagingClientAutoConfiguration,SfccShopShipmentClientAutoConfiguration) crea su propia instancia local deCacheStorecon un TTL distinto (15 min, 15 min, 1 min respectivamente). El bean general queda disponible en el contexto Spring pero no es consumido por ningún componente de esta librería. -
SfccAuthClientimplementado como lambda, no como proxy@HttpExchange: Aunque la interfaz está anotada con@HttpExchange/@PostExchange(sugiriendo el patrón habitual),SfccAuthClientAutoConfigurationla implementa manualmente como una expresión lambda que invocarestClient.post()directamente, en lugar de usarHttpServiceProxyFactory. Es el único de los seis clientes que se comporta así — comportamiento documentado explícitamente enCLAUDE.md, pero puede confundir a quien solo lea la interfaz@HttpExchangeesperando el patrón estándar del resto del proyecto. -
Dependencia cruzada entre
sfcc.client.urly dos autoconfiguraciones no relacionadas: La propiedadsfcc.client.urles requerida tanto porSfccClientAutoConfiguration(cliente OCAPI de cupones) como porSfccAuthShopShipmentAutoConfiguration/SfccShopShipmentClientAutoConfiguration(cliente de envíos Shop API) — mismos nombres de propiedad para bases URL potencialmente distintas (OCAPI vs. Shop API), lo que puede llevar a configurar por error la misma URL para ambos usos si no se conoce esta particularidad. Pendiente de verificar si en la práctica ambas URLs coinciden en el entorno real de SFCC. -
updateShipmentsStatessobrecargado con tipos de body incompatibles en la misma ruta: Dos métodos con el mismo nombre (updateShipmentsStates) y la misma ruta PATCH difieren solo en el tipo del@RequestBody(StateSfccRequesttipado vs.Stringcrudo) — el consumidor elige la sobrecarga según si prefiere construir el objeto tipado o enviar el JSON ya serializado. -
Dependencias JAXB (
jaxb-core/jaxb-impl) sin uso aparente: No se ha localizado ningún modelo anotado con@XmlRootElementni otras anotaciones JAXB en este proyecto — a diferencia de otros clientes del ecosistema que sí las usan (p. ej.servientrega-client,owd-client). Podrían ser un remanente de una versión anterior de la integración con SFCC que usaba XML. -
Existencia de
StateSfccRequestAlt: Junto aStateSfccRequestexiste una varianteStateSfccRequestAlt, que no aparece referenciada por ningún método deSfccShopShipmentClienten el código revisado — pendiente de verificar su propósito y si es un modelo huérfano. -
Sin tests implementados: No se ha encontrado directorio
src/test/en el proyecto. -
SfccClientApplication.java: Clase principal de Spring Boot en el paquete raíz, documentada enCLAUDE.mdcomo existente únicamente para el bootstrap de autoconfiguración, no como aplicación desplegable.