SFCC Marketing Client
1. Descripción general
sfcc-marketing-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con Salesforce Marketing Cloud (SFMC). 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 enviar emails transaccionales (p. ej. notificación de pedido retrasado) y disparar eventos de interacción (incluyendo encuestas) hacia Journey Builder de Marketing Cloud.
La librería gestiona de forma transparente la autenticación OAuth2 mediante token Bearer (con caché de 10 minutos), de modo que los servicios consumidores no necesitan implementar ninguna lógica de autenticación.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | sfcc-marketing-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.sfccmarketingclient
├── SfccMarketingClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ ├── SfccMarketingClient.java # Interfaz @HttpExchange: sendEmailOrderDelayed, sendEvents, sendEventsSurvey
│ └── SfccMarketingTokenClient.java # Interfaz @HttpExchange para /v2/token
├── config/
│ ├── SfccMarketingAutoConfiguration.java # @AutoConfiguration principal (interceptor Bearer + caché 10 min)
│ ├── SfccMarketingConfig.java # Clase vacía sin uso aparente (ver sección 13)
│ ├── SfccMarketingConst.java # Constante TXT_TOKEN = "token"
│ └── CacheStore.java # Cache genérica en memoria (Guava)
└── models/
├── SfccMarketingTokenRequest.java / SfccMarketingTokenResponse.java
├── SfccMarketingSendOrderDelayedRequest.java / SfccMarketingSendOrderDelayedResponse.java
├── SfccMarketingSendEvent.java # Evento genérico de interacción
└── SfccMarketingSendEventSurvey.java # Evento de interacción con datos de encuesta
Flujo principal de autenticación y llamada
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as SfccMarketingClient
participant Cache as CacheStore
participant TokenClient as SfccMarketingTokenClient
participant SFMC as Salesforce Marketing Cloud
Consumidor->>Client: sendEmailOrderDelayed / sendEvents / sendEventsSurvey
Client->>Cache: get("token")
alt Token en caché (< 10 min)
Cache-->>Client: token válido
else Token ausente o expirado
Client->>TokenClient: getToken(grant_type, client_id, client_secret)
TokenClient->>SFMC: POST /v2/token
SFMC-->>TokenClient: { "access_token": "...", "expires_in": ..., "rest_instance_url": "..." }
TokenClient-->>Client: token
Client->>Cache: add("token", token)
end
Client->>SFMC: request + Authorization: Bearer <token>
SFMC-->>Client: respuesta JSON
Client-->>Consumidor: ResponseEntity<T>
La autoconfiguración (SfccMarketingAutoConfiguration) se activa condicionalmente con @ConditionalOnProperty(prefix = "sfccmarketing.credentials", name = {"url", "auth.url", "grant-type", "client-id", "client-secret"}). Registra dos beans:
sfccMarketingTokenClient—RestClientsin interceptor, apuntando asfccmarketing.credentials.auth.url, usado únicamente para/v2/token.sfccMarketingClient—RestClientapuntando asfccmarketing.credentials.url, con unrequestInterceptorque resuelve el token (desde caché de 10 minutos o solicitando uno nuevo) e inyecta la cabeceraAuthorization: Bearer <token>en cada llamada.
El registro de la autoconfiguración 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 |
com.google.guava:guava | 33.6.0-jre | Implementación de caché en CacheStore (CacheBuilder) |
com.google.code.gson:gson | 2.14.0 | Anotaciones @SerializedName en los modelos (soporte dual con Jackson) |
com.fasterxml.jackson.core:jackson-databind | (gestionada SB4) | Serialización/deserialización Jackson en los modelos |
org.projectlombok:lombok | 1.18.46 | Generación de boilerplate en los modelos (getters, setters, constructores) |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
5. API / Endpoints
No aplica a este proyecto. sfcc-marketing-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Salesforce Marketing Cloud se detallan en la sección 6.
6. Integraciones externas
Salesforce Marketing Cloud — Messaging e Interaction API
| Método cliente | HTTP | Ruta remota | Descripción |
|---|---|---|---|
sendEmailOrderDelayed | POST | /messaging/v1/messageDefinitionSends/key:HW_ORDER_DELAYED/send | Envía el email transaccional de pedido retrasado (definición HW_ORDER_DELAYED) |
sendEvents | POST | /interaction/v1/events | Dispara un evento de interacción genérico hacia un entry source de Journey Builder |
sendEventsSurvey | POST | /interaction/v1/events | Dispara un evento de interacción con datos de encuesta hacia Journey Builder |
getToken (vía SfccMarketingTokenClient) | POST | /v2/token | Obtiene un access token OAuth2 (client credentials) |
Ejemplo de payload sendEmailOrderDelayed (SfccMarketingSendOrderDelayedRequest):
{
"To": {
"SubscriberKey": "cliente@example.com",
"Address": "cliente@example.com",
"ContactAttributes": {
"SubscriberAttributes": {
"orderid": "ORD-000123",
"Name": "Cliente Final",
"Locale": "es_ES",
"Country__c": "ES",
"order_date": "2026-07-01"
}
}
},
"Options": { "RequestType": "SYNC" }
}
Ejemplo de respuesta sendEmailOrderDelayed (SfccMarketingSendOrderDelayedResponse):
{
"requestId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"responses": [
{ "recipientSendId": "9c858901-8a57-4791-81fe-4c455b099bc9", "hasErrors": false, "messages": [] }
]
}
Ejemplo de payload sendEvents (SfccMarketingSendEvent):
{
"ContactKey": "cliente@example.com",
"EventDefinitionKey": "APIEvent-order-status",
"Data": {
"SubscriberKey": "cliente@example.com",
"EmailAddress": "cliente@example.com",
"Country__c": "ES",
"OrderNumber": "ORD-000123",
"Amount": 59.90,
"FirstName": "Cliente"
}
}
Ejemplo de payload sendEventsSurvey (SfccMarketingSendEventSurvey, con claves anidadas estilo Salesforce CRM):
{
"ContactKey": "cliente@example.com",
"EventDefinitionKey": "APIEvent-survey",
"Data": {
"SubscriberKey": "cliente@example.com",
"EmailAddress": "cliente@example.com",
"Account:Id": "001XXXXXXXXXXXXXXX",
"Account:FirstName": "Cliente",
"Account:PersonContact:Email": "cliente@example.com"
}
}
Protocolo: HTTPS REST (JSON, contentType/accept = application/json). Autenticación: OAuth2 client credentials (Bearer token, obtenido dinámicamente vía /v2/token, cacheado 10 minutos).
7. Configuración
No se incluye ningún application.properties/application.yml en la librería. Las propiedades deben ser inyectadas por la aplicación consumidora.
Propiedades requeridas (prefijo sfccmarketing.credentials)
| Propiedad | Descripción | Ejemplo de valor |
|---|---|---|
sfccmarketing.credentials.url | URL base para los endpoints de messaging/interaction | ${SFCC_MARKETING_URL} |
sfccmarketing.credentials.auth.url | URL base para el endpoint de token OAuth2 | ${SFCC_MARKETING_AUTH_URL} |
sfccmarketing.credentials.grant-type | Grant type OAuth2 (p. ej. client_credentials) | client_credentials |
sfccmarketing.credentials.client-id | Client ID de la integración en Marketing Cloud | ${SFCC_MARKETING_CLIENT_ID} |
sfccmarketing.credentials.client-secret | Client secret de la integración en Marketing Cloud | ${SFCC_MARKETING_CLIENT_SECRET} |
Importante: Si falta cualquiera de las cinco propiedades, el bean
SfccMarketingAutoConfigurationno se activa (condición@ConditionalOnPropertycon las cinco claves) y ninguno de los dos clientes se registra.
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
SFCC_MARKETING_URL | sfccmarketing.credentials.url |
SFCC_MARKETING_AUTH_URL | sfccmarketing.credentials.auth.url |
SFCC_MARKETING_CLIENT_ID | sfccmarketing.credentials.client-id |
SFCC_MARKETING_CLIENT_SECRET | sfccmarketing.credentials.client-secret |
8. Persistencia
No aplica a este proyecto. La librería no accede a ninguna base de datos (DataSourceAutoConfiguration excluida explícitamente según CLAUDE.md). El único estado que persiste en memoria es la caché del token Bearer (CacheStore, TTL 10 minutos, backend Guava).
9. Procesos programados y mensajería
No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch — los eventos de Journey Builder se disparan de forma síncrona bajo demanda del consumidor, no mediante mensajería propia de esta librería.
10. Ejecución en local
sfcc-marketing-client es una librería JAR, no una aplicación ejecutable. 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
# Solo compilar
./mvnw compile
# Ejecutar un test/método concreto
./mvnw test -Dtest=ClassName
./mvnw test -Dtest=ClassName#methodName
Uso como dependencia en un microservicio consumidor
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>sfcc-marketing-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
La autoconfiguración se activa automáticamente al declarar las cinco propiedades sfccmarketing.credentials.* en la aplicación consumidora.
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.
| 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-marketing-client/
12. Manejo de errores y logging
Los métodos de ambos clientes documentan en su Javadoc que lanzan RestClientResponseException en respuestas no-2xx (comportamiento heredado de RestClient, no una excepción comprobada declarada en la firma). En fetchAndCacheToken, si la respuesta del endpoint de token no es 2xx o no tiene body, el método devuelve una cadena vacía ("") en lugar de lanzar una excepción — las llamadas posteriores se autenticarían con Authorization: Bearer (token vacío), fallando con un error HTTP del lado de Marketing Cloud en vez de fallar explícitamente en el cliente. Mismo patrón observado en hk-timeslogistics-client, meli-client y privalia-client. No hay configuración de logback ni de niveles de log específicos en la librería.
13. Notas y consideraciones
-
SfccMarketingConfiges una clase vacía sin documentación de propósito: A diferencia de placeholders similares en otros clientes del ecosistema (ServientregaSoapConfig,PrivaliaMarketplaceClientConfig), que incluyen un Javadoc explicando por qué se retienen,SfccMarketingConfiges una clase con constructor privado y ningún comentario — no está claro si es un remanente de una integración anterior (posiblemente Feign) o un placeholder pensado para uso futuro. Pendiente de verificar si puede eliminarse sin impacto. -
Fallo silencioso en
fetchAndCacheToken: Igual que en otros clientes del ecosistema, si la autenticación falla, el método devuelve""en lugar de propagar una excepción, dificultando el diagnóstico de errores de autenticación. -
SfccMarketingTokenResponsecon URLs de instancia sin uso aparente: El modelo incluyesoap_instance_urlyrest_instance_url(URLs específicas de la instancia de Marketing Cloud del cliente), peroSfccMarketingAutoConfigurationno los utiliza para nada — labaseUrldeSfccMarketingClientse fija estáticamente desdesfccmarketing.credentials.urlen lugar de resolverse dinámicamente a partir derest_instance_urldevuelto por el propio token, como sí hacen algunas integraciones SFMC. Pendiente de verificar si esto es intencional (URL fija conocida) o una oportunidad de simplificación de configuración perdida. -
sendEventsysendEventsSurveycomparten la misma ruta remota: Ambos métodos apuntan aPOST /interaction/v1/events, diferenciándose únicamente en la forma del campoDatadel payload (evento genérico vs. datos de encuesta con claves estilo Salesforce CRMAccount:Campo). Refleja un único endpoint remoto con distintos "shapes" de evento, modelados como métodos Java distintos por claridad. -
Claves con formato heterogéneo en los modelos: Los campos JSON mezclan PascalCase (
SubscriberKey,EventDefinitionKey), snake_case (grant_type,order_date) y notación con dos puntos estilo Salesforce CRM (Account:Id,Account:PersonContact:Email) — dictado por el contrato de las distintas APIs de Salesforce (Marketing Cloud REST vs. convenciones de Journey Builder/CRM), no una elección de diseño de esta librería. -
Sin tests implementados: No se ha encontrado directorio
src/test/en el proyecto. -
SfccMarketingClientApplication.java: Clase principal de Spring Boot en el paquete raíz, sin funcionalidad operativa. Artefacto residual de la generación inicial del proyecto con Spring Initializr, mismo patrón observado en otros clientes del ecosistema.