Skip to main content

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

PropiedadValor
artifactIdsfcc-client
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6
Tipo de artefactoJAR (librería, no ejecutable)
MódulosProyecto ú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

DependenciaVersiónPropó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:guava33.4.0-jreImplementación de caché en CacheStore (CacheBuilder)
org.json:json20240303Extracción de access_token de las respuestas de token (JSONObject)
com.sun.xml.bind:jaxb-core / jaxb-impl4.0.5Soporte JAXB (declarado; no se ha localizado ningún modelo @XmlRootElement en este proyecto — pendiente de verificar su uso real)
com.google.code.gson:gson2.11.0Anotaciones @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:lombok1.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étodoHTTPRuta remotaDescripción
getCoupons (2 sobrecargas)GET/{apiVersion}/sites/{siteId}/couponsLista cupones de un sitio (con o sin paginación start/count)
getCouponsNextGET/{next}Continúa la paginación siguiendo el enlace next devuelto por SFCC
getCodesByCouponsGET/{apiVersion}/sites/{siteId}/coupons/{couponId}/codesLista los códigos de un cupón
addCodesPOST/{apiVersion}/sites/{siteId}/coupons/{couponId}/multiple_codesAñade códigos a un cupón
deleteCodePOST/{apiVersion}/sites/{siteId}/coupons/{couponId}/multiple_codes?delete=Elimina códigos de un cupón
searchProductsPOST/{apiVersion}/product_search?site_id={siteId}Búsqueda de productos (OCAPI)
searchCouponPOST/{apiVersion}/sites/{siteId}/coupon_searchBúsqueda avanzada de cupones (body JSON crudo)
searchCouponRedemptionPOST/{apiVersion}/sites/{siteId}/coupon_redemption_searchBú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étodoHTTPRuta remotaDescripción
getProductsByIdsGET/s/{siteId}/dw/shop/{apiVersion}/products/({skus})?client_id=&expand=Consulta productos por lista de SKUs
updateShipmentsPATCH/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)
updateShipmentTrackingPATCH/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

ClientePropiedadesEjemplo de valor
SfccAuthClientsfcc.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}
SfccShopClientsfcc.shop.client.url${SFCC_SHOP_CLIENT_URL}
SfccAuthShopShipmentClientsfcc.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.url es compartida entre SfccAuthShopShipmentClient y SfccShopShipmentClient (y coincide en nombre con la propiedad de SfccClient, aunque estos dos últimos son independientes entre sí: SfccClient no depende de sfcc.api.clientid ni de SfccAuthShopShipmentClient).

sfcc.auth.basic (para SfccAuthShopShipmentClient) debe contener el valor Base64 ya codificado de client_id:client_secret; sfcc.auth.client.client-id/client-secret (para SfccAuthClient) se codifican en Base64 automáticamente dentro de la autoconfiguración.

Variables de entorno recomendadas

VariablePropiedad mapeada
SFCC_AUTH_URLsfcc.auth.client.url
SFCC_CLIENT_IDsfcc.auth.client.client-id
SFCC_CLIENT_SECRETsfcc.auth.client.client-secret
SFCC_CLIENT_URLsfcc.client.url
SFCC_STAGING_CLIENT_URLsfcc.staging.client.url
SFCC_SHOP_CLIENT_URLsfcc.shop.client.url
SFCC_AUTH_BASICsfcc.auth.basic
SFCC_API_CLIENT_IDsfcc.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:

  1. Checkout — descarga el código del repositorio.
  2. Publish to Artifact Registry — ejecuta mvn deploy -DskipTests para 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ámetroValor
JDKJDK25 (tool Jenkins)
MavenMaven3 (tool Jenkins)
Repositorioeurope-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 en meli-client y auro-client, existe un bean CacheStore<String> de propósito general (TTL 30 minutos) expuesto por CacheStoreBeans, pero cada autoconfiguración de cliente (SfccClientAutoConfiguration, SfccStagingClientAutoConfiguration, SfccShopShipmentClientAutoConfiguration) crea su propia instancia local de CacheStore con 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.

  • SfccAuthClient implementado como lambda, no como proxy @HttpExchange: Aunque la interfaz está anotada con @HttpExchange/@PostExchange (sugiriendo el patrón habitual), SfccAuthClientAutoConfiguration la implementa manualmente como una expresión lambda que invoca restClient.post() directamente, en lugar de usar HttpServiceProxyFactory. Es el único de los seis clientes que se comporta así — comportamiento documentado explícitamente en CLAUDE.md, pero puede confundir a quien solo lea la interfaz @HttpExchange esperando el patrón estándar del resto del proyecto.

  • Dependencia cruzada entre sfcc.client.url y dos autoconfiguraciones no relacionadas: La propiedad sfcc.client.url es requerida tanto por SfccClientAutoConfiguration (cliente OCAPI de cupones) como por SfccAuthShopShipmentAutoConfiguration/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.

  • updateShipmentsStates sobrecargado 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 (StateSfccRequest tipado vs. String crudo) — 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 @XmlRootElement ni 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 a StateSfccRequest existe una variante StateSfccRequestAlt, que no aparece referenciada por ningún método de SfccShopShipmentClient en 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 en CLAUDE.md como existente únicamente para el bootstrap de autoconfiguración, no como aplicación desplegable.