Skip to main content

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

PropiedadValor
artifactIdsfcc-marketing-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.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:

  • sfccMarketingTokenClientRestClient sin interceptor, apuntando a sfccmarketing.credentials.auth.url, usado únicamente para /v2/token.
  • sfccMarketingClientRestClient apuntando a sfccmarketing.credentials.url, con un requestInterceptor que resuelve el token (desde caché de 10 minutos o solicitando uno nuevo) e inyecta la cabecera Authorization: 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

DependenciaVersiónPropósito
spring-boot-starter(gestionada SB4)Base de Spring Boot (contexto, autoconfiguración)
spring-web(gestionada SB4)RestClient + @HttpExchange / HttpServiceProxyFactory
com.google.guava:guava33.6.0-jreImplementación de caché en CacheStore (CacheBuilder)
com.google.code.gson:gson2.14.0Anotaciones @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:lombok1.18.46Generació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 clienteHTTPRuta remotaDescripción
sendEmailOrderDelayedPOST/messaging/v1/messageDefinitionSends/key:HW_ORDER_DELAYED/sendEnvía el email transaccional de pedido retrasado (definición HW_ORDER_DELAYED)
sendEventsPOST/interaction/v1/eventsDispara un evento de interacción genérico hacia un entry source de Journey Builder
sendEventsSurveyPOST/interaction/v1/eventsDispara un evento de interacción con datos de encuesta hacia Journey Builder
getToken (vía SfccMarketingTokenClient)POST/v2/tokenObtiene 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)

PropiedadDescripciónEjemplo de valor
sfccmarketing.credentials.urlURL base para los endpoints de messaging/interaction${SFCC_MARKETING_URL}
sfccmarketing.credentials.auth.urlURL base para el endpoint de token OAuth2${SFCC_MARKETING_AUTH_URL}
sfccmarketing.credentials.grant-typeGrant type OAuth2 (p. ej. client_credentials)client_credentials
sfccmarketing.credentials.client-idClient ID de la integración en Marketing Cloud${SFCC_MARKETING_CLIENT_ID}
sfccmarketing.credentials.client-secretClient secret de la integración en Marketing Cloud${SFCC_MARKETING_CLIENT_SECRET}

Importante: Si falta cualquiera de las cinco propiedades, el bean SfccMarketingAutoConfiguration no se activa (condición @ConditionalOnProperty con las cinco claves) y ninguno de los dos clientes se registra.

Variables de entorno recomendadas

VariablePropiedad mapeada
SFCC_MARKETING_URLsfccmarketing.credentials.url
SFCC_MARKETING_AUTH_URLsfccmarketing.credentials.auth.url
SFCC_MARKETING_CLIENT_IDsfccmarketing.credentials.client-id
SFCC_MARKETING_CLIENT_SECRETsfccmarketing.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:

  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.
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-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

  • SfccMarketingConfig es 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, SfccMarketingConfig es 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.

  • SfccMarketingTokenResponse con URLs de instancia sin uso aparente: El modelo incluye soap_instance_url y rest_instance_url (URLs específicas de la instancia de Marketing Cloud del cliente), pero SfccMarketingAutoConfiguration no los utiliza para nada — la baseUrl de SfccMarketingClient se fija estáticamente desde sfccmarketing.credentials.url en lugar de resolverse dinámicamente a partir de rest_instance_url devuelto 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.

  • sendEvents y sendEventsSurvey comparten la misma ruta remota: Ambos métodos apuntan a POST /interaction/v1/events, diferenciándose únicamente en la forma del campo Data del payload (evento genérico vs. datos de encuesta con claves estilo Salesforce CRM Account: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.