Skip to main content

Shopee Client

1. Descripción general

shopee-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con la API de Shopee Open Platform. 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 tokens de acceso, consultar/buscar productos, actualizar stock, consultar información de tienda/almacén y gestionar conversaciones de mensajería con compradores en Shopee.

A diferencia de otros clientes del ecosistema, ShopeeClient es genérico respecto a la ruta: cada método recibe la ruta del endpoint de Shopee como parámetro (path), y no gestiona ni la firma HMAC ni el token de acceso — ambos deben ser calculados y pasados por el consumidor en cada llamada.

2. Información técnica

PropiedadValor
artifactIdshopee-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.shopeeclient
├── ShopeeClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ └── ShopeeClient.java # Interfaz @HttpExchange con 12 operaciones, todas parametrizadas por {path}
├── config/
│ ├── ShopeeClientAutoConfiguration.java # @AutoConfiguration principal (URI encoding especial)
│ └── CacheStore.java # Cache genérica en memoria (Guava), disponible para que el consumidor cachee tokens
├── request/ # ShopeeAccessTokenRequest, ShopeeRefreshTokenRequest, ShopeeGetProductInfoRequest,
│ # ShopeeUpdateStockRequest, ShopeeMessageRequest, ShopeeMarkUnreadRequest
└── response/ # ShopeeRefreshTokenResponse, ShopeeSeachItemResponse, ShopeeSearchAllItemResponse,
# ShopeeGetModelListResponse, ShopeeUpdateStockResponse, ShopeeConversationResponse

Flujo principal

sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as ShopeeClient
participant Shopee as API Shopee Open Platform

Consumidor->>Consumidor: calcula sign (HMAC-SHA256) + timestamp + resuelve/cachea access_token
Consumidor->>Client: updateStock(path, accessToken, partnerId, shopId, sign, timestamp, body) / ...
Client->>Shopee: request a {baseUrl}{path}?partner_id=&sign=&timestamp=&access_token=...
Shopee-->>Client: ResponseEntity<T>
Client-->>Consumidor: ResponseEntity<T>

La autoconfiguración (ShopeeClientAutoConfiguration) se activa condicionalmente con @ConditionalOnProperty(prefix = "shopee.api", name = "host"), registrando el bean ShopeeClient con un RestClient cuya UriBuilderFactory se configura con EncodingMode.NONE, para que el valor sustituido en {path} (p. ej. /api/v2/auth/token/get) no sufra percent-encoding de las barras /.

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

CacheStore<T> (backend Guava) está disponible como utilidad para que el consumidor cachee el access_token con TTL, pero no se usa internamente por ningún componente de la librería — la gestión completa del ciclo de vida del token (obtención, refresco, caché) es responsabilidad del consumidor.

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); disponible para el consumidor
com.google.code.gson:gson(gestionada SB4)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:lombok1.18.46Generación de boilerplate en los modelos (getters, setters, constructores, @Data)
spring-boot-starter-test(gestionada SB4)Testing (scope test)

5. API / Endpoints

No aplica a este proyecto. shopee-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Shopee se detallan en la sección 6.

6. Integraciones externas

API de Shopee Open Platform

Todos los métodos comparten el mismo patrón: reciben path (ruta remota concreta de Shopee), partner_id, sign (firma HMAC-SHA256 precalculada) y timestamp como parámetros obligatorios; la mayoría requieren además access_token y shop_id.

ÁreaMétodoHTTPParámetros propiosDescripción
Auth / tokengenerateAccessTokenPOSTbody: code, partner_id, shop_idIntercambia un código de autorización por access/refresh token
Auth / tokengetAccessTokenPOSTbody: refresh token, partner y shop IDRefresca el access token con un refresh token
ProductogetItemBaseInfoGETitem_id_list fijo en la URL (ver sección 13)Consulta información base de ítems
ProductosearchItemGETitem_sku, page_size=1 fijoBusca un ítem por SKU (máx. 1 resultado)
ProductosearchAllItemGEToffset, page_size=100&item_status=NORMAL fijosLista paginada de ítems en estado NORMAL
ProductogetProductInfoPOSTbody: lista de IDs de ítem y tipos de info deseadosConsulta información detallada de producto
ProductogetModelListGETitem_idLista de variantes (modelos) de un ítem, con stock y precio
InventarioupdateStockPOSTbody: item_id, lista de stock_list por modeloActualiza el stock del vendedor para uno o varios modelos
TiendagetShopInfoGETConsulta información general de la tienda
AlmacéngetWarehouseDetailGETConsulta el detalle del almacén
MensajeríagetConversationListGETdirection=older&type=unread fijosLista conversaciones no leídas (de más antigua a más reciente)
MensajeríasendMessagePOSTbody: destinatario, tipo y contenido del mensajeEnvía un mensaje (texto, sticker, imagen o referencia de pedido)
MensajeríamarkAsUnreadPOSTbody: conversation_idMarca una conversación como no leída (ID en el body)
MensajeríamarkAsUnreadParamsPOSTconversation_id como query paramMarca una conversación como no leída (ID como parámetro de query)

Ejemplo de payload updateStock (ShopeeUpdateStockRequest):

{
"item_id": 19253756461,
"stock_list": [
{ "model_id": 100001, "seller_stock": [{ "stock": 25 }] }
]
}

Ejemplo de respuesta updateStock (ShopeeUpdateStockResponse):

{
"error": "",
"message": "",
"request_id": "abc123",
"response": {
"success_list": [{ "model_id": 100001, "normal_stock": 25 }],
"failure_list": []
}
}

Ejemplo de respuesta de token (ShopeeRefreshTokenResponse, común a generateAccessToken/getAccessToken):

{
"partner_id": 123456,
"shop_id": 789012,
"access_token": "********",
"refresh_token": "********",
"expire_in": 14400,
"request_id": "abc123",
"error": "",
"message": ""
}

Protocolo: HTTPS REST (JSON, contentType/accept = application/json). Autenticación: firma HMAC-SHA256 (sign) y access_token calculados/gestionados enteramente por el consumidor; el cliente solo los transporta como parámetros.

7. Configuración

No se incluye ningún application.properties/application.yml en la librería. La única propiedad requerida debe ser suministrada por la aplicación consumidora:

PropiedadDescripciónEjemplo de valor
shopee.api.hostURL base de la API de Shopee (activa la autoconfiguración)${SHOPEE_API_HOST}

Las credenciales (partner_id, partner_key/secreto para firmar, access_token, refresh_token) no se configuran como propiedades Spring: el consumidor las gestiona por completo, incluyendo el cálculo de la firma HMAC-SHA256 en cada llamada.

Variables de entorno recomendadas

VariablePropiedad mapeada
SHOPEE_API_HOSTshopee.api.host

Importante: Si shopee.api.host no está definida, el bean ShopeeClient no se registra (condición @ConditionalOnProperty).

8. Persistencia

No aplica a este proyecto. La librería no accede a ninguna base de datos. CacheStore está disponible como utilidad de caché en memoria, pero no la usa internamente ningún componente de esta librería — queda a criterio del consumidor emplearla para cachear tokens.

9. Procesos programados y mensajería

No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch. La "mensajería" cubierta por getConversationList/sendMessage/markAsUnread es la API de chat de Shopee (llamadas HTTP síncronas), no un mecanismo de mensajería propio de la librería.

10. Ejecución en local

shopee-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 (como en CI)
./mvnw -B -DskipTests clean install

# Compilar con tests
./mvnw clean verify

# Ejecutar un test/método concreto
./mvnw -Dtest=MyTestClass test
./mvnw -Dtest=MyTestClass#myMethod test

Uso como dependencia en un microservicio consumidor

<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>shopee-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>

La autoconfiguración se activa automáticamente al declarar shopee.api.host en la aplicación consumidora. El consumidor debe calcular la firma HMAC-SHA256 (partner_key + path + timestamp [+ access_token + shop_id según el endpoint], según el esquema de firma de Shopee Open Platform) e implementar su propia gestión de tokens antes de invocar cualquier método del cliente.

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/shopee-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 ShopeeClient declara throws explícito; las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. Varios modelos de respuesta (ShopeeRefreshTokenResponse, ShopeeUpdateStockResponse) incluyen campos error/message propios del contrato de Shopee, que el consumidor debe inspeccionar manualmente para detectar errores de negocio (Shopee puede responder 200 OK con un error no vacío). No hay configuración de logback ni de niveles de log específicos en la librería.

13. Notas y consideraciones

  • getItemBaseInfo con item_id hardcodeado en la ruta: El método está anotado @GetExchange("{path}?item_id_list=[19253756461]") — el identificador de ítem 19253756461 está fijado literalmente en la plantilla de la URL, sin ningún parámetro que permita al consumidor especificar el/los ítems a consultar. Esto es casi con certeza un defecto: cualquier llamada a getItemBaseInfo, independientemente de la intención del consumidor, consultará siempre ese ítem fijo (que parece un ID de prueba dejado en el código). Debe corregirse añadiendo un parámetro @RequestParam/@RequestBody para la lista de IDs antes de usar este método en producción.

  • CacheStore disponible pero sin ningún consumidor interno: A diferencia de otros clientes del ecosistema, aquí la caché de Guava se expone deliberadamente como utilidad de apoyo (documentado en CLAUDE.md: "available for callers to cache access tokens"), no como mecanismo interno de la autoconfiguración — es el único cliente del ecosistema revisado hasta ahora donde esto es el diseño intencional, no un descuido.

  • Firma y token completamente delegados al consumidor: A diferencia de la mayoría de clientes del ecosistema (que resuelven el token internamente vía interceptor), aquí el consumidor debe implementar por su cuenta el algoritmo de firma HMAC-SHA256 de Shopee Open Platform y gestionar el ciclo de vida completo del access_token/refresh_token. La librería es deliberadamente un transporte genérico parametrizado por path, no un cliente semánticamente cerrado por endpoint.

  • markAsUnread y markAsUnreadParams para la misma operación con distinta forma: Ambos métodos marcan una conversación como no leída, pero uno recibe el conversation_id en el body (ShopeeMarkUnreadRequest) y otro como parámetro de query (int conversationId) — sugiere incertidumbre sobre qué forma exige realmente la API de Shopee para este endpoint, con ambas variantes conservadas por si alguna deja de funcionar.

  • Valores fijos embebidos en la plantilla de ruta: Además del caso de getItemBaseInfo, searchItem (page_size=1), searchAllItem (page_size=100&item_status=NORMAL) y getConversationList (direction=older&type=unread) fijan parámetros de query directamente en la anotación @GetExchange, sin posibilidad de que el consumidor los override. A diferencia del caso de getItemBaseInfo, estos parecen decisiones de diseño deliberadas (paginación/filtro por defecto), no errores, pero limitan la flexibilidad del cliente para casos de uso que necesiten otro tamaño de página o estado de ítem.

  • Sin tests implementados: No se ha encontrado directorio src/test/ en el proyecto.

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