Skip to main content

Miravia Client

1. Descripción general

miravia-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con la API del marketplace Miravia. 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 autenticación, catálogo de productos, empaquetado de pedidos y documentación de envío (AWB) en Miravia.

A diferencia de otros clientes del ecosistema, este proyecto combina dos mecanismos de integración distintos:

  1. Un cliente declarativo Spring (@HttpExchange) para autenticación (refresco de token) y operaciones de catálogo/precio-stock.
  2. El SDK oficial IOP de Miravia (com.miravia:miravia), invocado a través de utilidades propias (MiraviaUtils) para operaciones de empaquetado de pedidos y obtención de documentos AWB (etiquetas de envío).

Todas las peticiones al SDK IOP se firman con HMAC-SHA256, siguiendo el esquema de firma estándar de las APIs de Alibaba/Miravia (app_key, sign_method, timestamp, sign).

2. Información técnica

PropiedadValor
artifactIdmiravia-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.miraviaclient
├── MiraviaClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ └── MiraviaClient.java # Interfaz @HttpExchange: getToken, getProducts, updateProductPriceQuantity
├── config/
│ └── MiraviaClientAutoConfiguration.java # @AutoConfiguration principal
├── utils/
│ └── MiraviaUtils.java # Firma HMAC-SHA256, Base64, empaquetado de pedidos y obtención de AWB vía SDK IOP
└── dao/ # ~18 POJOs de request/response (Lombok + Jackson + Gson)
├── MiraviaCredentialsResponse.java # Respuesta de token/credenciales OAuth
├── MiraviaProducts.java / MiraviaProductsRequest.java
├── MiraviaOrderItems.java, MiraviaOrderResponse.java, MiraviaOrderListResponse.java
├── MiraviaPackRequest.java / MiraviaPackResponse.java # Empaquetado de pedidos (SDK IOP)
├── MiraviaAWBRequest.java / MiraviaAWBResponse.java # Documento de envío / etiqueta (SDK IOP)
├── MiraviaReadyToShipRequest.java / MiraviaReadyToShipResponse.java
├── MiraviaTrackingRequest.java, MiraviaUpdateTrackingInfoRequest.java / MiraviaUpdateTrackingResponse.java
├── MiraviaShipmentProviderRequest.java
├── MiraviaRefundListResponse.java
└── MiraviaSellerResponse.java
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as MiraviaClient
participant API as API Miravia

Consumidor->>Client: getToken(appKey, refreshToken, signMethod, timestamp, sign)
Client->>API: POST /auth/token/refresh (form-urlencoded)
API-->>Client: MiraviaCredentialsResponse (access_token, refresh_token)
Consumidor->>Client: getProducts(...) / updateProductPriceQuantity(...)
Client->>API: GET /products/get | POST /product/price_quantity/update
API-->>Client: ResponseEntity<T>

MiraviaClientAutoConfiguration se activa condicionalmente con @ConditionalOnProperty(prefix = "miravia.client", name = "url"), registrando el bean MiraviaClient con un RestClient simple (sin interceptores). A diferencia de otros clientes del ecosistema, MiraviaClient no gestiona el token automáticamente: cada método (getToken, getProducts, updateProductPriceQuantity) recibe appKey, accessToken/refreshToken, signMethod, timestamp y sign como parámetros explícitos — el consumidor es responsable de generar la firma (con MiraviaUtils.signApiRequest) y de gestionar la caché/renovación del token.

Flujo 2 — MiraviaUtils (SDK IOP, empaquetado y AWB)

sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Utils as MiraviaUtils
participant IopClient as SDK IOP (com.miravia:miravia)
participant API as API Miravia (IOP)

Consumidor->>Utils: updateToPackMiravia(dsOrder, items, iopClient, credentials, gson)
Utils->>IopClient: execute(IopRequest "/order/pack", accessToken)
IopClient->>API: POST /order/pack
API-->>IopClient: respuesta IOP (código + body JSON)
IopClient-->>Utils: IopResponse
Utils-->>Consumidor: packageID (o "" si ya empaquetado/error)

Consumidor->>Utils: getPrintAWB(packageID, iopClient, credentials, ext)
Utils->>IopClient: execute(IopRequest "/order/package/document/get", accessToken)
IopClient->>API: GET /order/package/document/get
API-->>IopClient: respuesta IOP
IopClient-->>Utils: IopResponse
Utils-->>Consumidor: MiraviaAWBResponse (URLs de PDF/ZPL) o null

MiraviaUtils no es un bean Spring ni está registrado como autoconfiguración: es una clase de utilidad plana que el consumidor instancia directamente, pasándole como parámetro un IopClient (del SDK oficial com.miravia:miravia) ya inicializado con las credenciales de la aplicación.

El registro de la autoconfiguración de MiraviaClient 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.miravia:miravia20200901SDK oficial IOP de Miravia: IopClient, IopRequest, IopResponse, firma y ejecución de llamadas al API IOP
com.fasterxml.jackson.core:jackson-annotations(gestionada SB4)Anotaciones @JsonProperty en los DAOs (mapeo snake_case ↔ camelCase)
com.google.code.gson:gson(gestionada SB4)Anotaciones @SerializedName y serialización usada por MiraviaUtils (SDK IOP trabaja con JSON vía Gson)
org.projectlombok:lombok1.18.46Generación de boilerplate en los DAOs (getters, setters, constructores)
spring-boot-starter-test(gestionada SB4)Testing (scope test)

5. API / Endpoints

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

6. Integraciones externas

API de Miravia — vía MiraviaClient (@HttpExchange)

MétodoHTTPRuta remotaDescripción
getTokenPOST/auth/token/refreshRenueva el access token usando el refresh token
getProductsGET/products/getConsulta el catálogo de productos (paginado, con filtro)
updateProductPriceQuantityPOST/product/price_quantity/updateActualiza precio y/o cantidad de uno o varios SKUs

Todas las llamadas requieren app_key, sign_method, timestamp y sign (firma HMAC-SHA256) como parámetros explícitos; getProducts y updateProductPriceQuantity requieren además access_token.

Ejemplo de respuesta getToken (MiraviaCredentialsResponse):

{
"access_token": "********",
"country": "ES",
"refresh_token": "********",
"account_platform": "seller_center",
"refresh_expires_in": 2592000,
"expires_in": 2592000,
"code": "0",
"request_id": "abc123"
}

API de Miravia — vía SDK IOP (MiraviaUtils)

MétodoAPI IOP remotaDirecciónDescripción
updateToPackMiraviaPOST /order/packSalienteMarca las líneas de un pedido como empaquetadas (deliveryType=dropship); idempotente — repetir la llamada devuelve la misma información sin error
getPrintAWBGET /order/package/document/getSalienteObtiene las URLs del documento de envío (PDF/ZPL) para un paquete

Ejemplo de payload interno MiraviaPackRequest (serializado a JSON por MiraviaUtils antes de enviarse vía IOP):

{
"pack_order_list": [
{ "order_id": "000123456789", "order_item_list": ["ITEM-001", "ITEM-002"] }
],
"delivery_type": "dropship"
}

Ejemplo de respuesta MiraviaAWBResponse.Result.Data:

{
"code": "0",
"request_id": "abc123",
"result": {
"success": "true",
"data": {
"file": "...",
"zpl_url": "https://miravia.example/awb/package123.zpl",
"pdf_url": "https://miravia.example/awb/package123.pdf",
"doc_type": "pdf"
}
}
}

Otros DAOs presentes que no están cableados a ningún método de MiraviaClient ni MiraviaUtils (MiraviaOrderItems, MiraviaOrderResponse, MiraviaOrderListResponse, MiraviaReadyToShipRequest/Response, MiraviaTrackingRequest, MiraviaUpdateTrackingInfoRequest/Response, MiraviaShipmentProviderRequest, MiraviaRefundListResponse, MiraviaSellerResponse): modelan operaciones adicionales del API IOP de Miravia (consulta/listado de pedidos, marcar listo para envío, actualizar tracking, asignar proveedor de envío, listar reembolsos, consultar datos del vendedor), pero no existe en este repositorio ningún método que los invoque — su uso real se realizaría construyendo manualmente un IopRequest con el IopClient del SDK, de forma análoga a como lo hace MiraviaUtils para pack y document/get. Pendiente de verificar si el consumidor implementa estas llamadas directamente contra el SDK.

Protocolo: HTTPS REST. MiraviaClient: application/x-www-form-urlencoded (auth) y JSON (catálogo). SDK IOP: JSON serializado con Gson, ejecutado por IopClient.execute(...). Autenticación: firma HMAC-SHA256 (app_key + app_secret, vía MiraviaUtils.signApiRequest) combinada con access_token/refresh_token OAuth.

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

PropiedadDescripciónEjemplo de valor
miravia.client.urlURL base de la API de Miravia (activa la autoconfiguración de MiraviaClient)${MIRAVIA_CLIENT_URL}

Las credenciales de aplicación (app_key, app_secret) y las de OAuth (access_token, refresh_token) no se configuran como propiedades Spring: se gestionan por el consumidor, ya sea pasándolas como parámetros explícitos a MiraviaClient o inicializando el IopClient del SDK con ellas.

Variables de entorno recomendadas

VariablePropiedad mapeada
MIRAVIA_CLIENT_URLmiravia.client.url

Importante: Si miravia.client.url no está definida, el bean MiraviaClient no se registra (condición @ConditionalOnProperty). MiraviaUtils no depende de ninguna propiedad Spring, ya que no es un bean autoconfigurado.

8. Persistencia

No aplica a este proyecto. La librería no accede a ninguna base de datos ni mantiene estado en memoria propio (a diferencia de otros clientes del ecosistema, no implementa caché de token).

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

miravia-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), tanto para resolver dependencias como para el propio SDK com.miravia:miravia.

Compilar e instalar en repositorio local

# Compilar con tests
./mvnw clean install

# Compilar sin tests (como en CI)
./mvnw -DskipTests clean install

# Ejecutar tests
./mvnw test

Uso como dependencia en un microservicio consumidor

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

MiraviaClient se activa automáticamente al declarar miravia.client.url. Para usar MiraviaUtils (empaquetado/AWB), el consumidor debe instanciar y configurar por su cuenta un IopClient del SDK com.miravia:miravia con las credenciales de la aplicación.

11. Despliegue

El pipeline de Jenkins (Jenkinsfile) consta de dos etapas:

  1. Checkout — descarga el código del repositorio.
  2. Publish to Artifact Registry — antes de compilar, elimina la copia local cacheada del SDK com.miravia (rm -rf ~/.m2/repository/com/miravia) para forzar su re-resolución desde el registro remoto, y luego ejecuta mvn deploy -DskipTests para publicar el JAR en Google Artifact Registry. Mismo patrón de limpieza de caché de SDK de terceros observado en lazada-client.
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/miravia-client/

12. Manejo de errores y logging

MiraviaClient no implementa ninguna estrategia propia de manejo de excepciones; las excepciones HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. CLAUDE.md señala explícitamente que los consumidores que capturaban feign.FeignException deben migrar a RestClientResponseException, confirmando que el proyecto migró de OpenFeign a @HttpExchange.

MiraviaUtils declara throws ApiException (excepción propia del SDK IOP) en updateToPackMiravia y getPrintAWB. Ambos métodos, además, devuelven un valor "vacío" ("" o null respectivamente) cuando la respuesta IOP no tiene código de éxito ("0") o el paquete ya estaba empaquetado (código 700038), en lugar de lanzar una excepción — el consumidor debe comprobar explícitamente estos valores de retorno para detectar fallos silenciosos.

No hay configuración de logback ni de niveles de log específicos en la librería.

13. Notas y consideraciones

  • Dos mecanismos de integración incompatibles en el mismo JAR: MiraviaClient (Spring @HttpExchange, sin gestión de token) y MiraviaUtils (SDK IOP externo, requiere un IopClient inicializado por el consumidor) cubren operaciones distintas de la misma API de Miravia sin ningún punto de integración común — el consumidor debe entender y mantener ambos flujos de autenticación/firma por separado.

  • Sin gestión de token/firma en MiraviaClient: A diferencia de auro-client, hk-timeslogistics-client o meli-client (que resuelven el token automáticamente mediante interceptor y caché), MiraviaClient traslada completamente la generación de la firma HMAC-SHA256 y la gestión del access_token/refresh_token al consumidor, en cada llamada.

  • Numerosos DAOs sin cableado a ningún método invocable desde este repositorio: MiraviaOrderItems, MiraviaOrderResponse, MiraviaOrderListResponse, MiraviaReadyToShipRequest/Response, MiraviaTrackingRequest, MiraviaUpdateTrackingInfoRequest/Response, MiraviaShipmentProviderRequest, MiraviaRefundListResponse y MiraviaSellerResponse no son usados por MiraviaClient ni por MiraviaUtils. Sugieren que la librería está pensada para ampliarse (o para que el consumidor construya sus propias llamadas IOP con estos modelos), pero actualmente representan superficie sin comportamiento asociado en el código de esta librería.

  • Fallo silencioso en updateToPackMiravia/getPrintAWB: Ambos métodos devuelven un resultado "vacío" (""/null) en varios escenarios de error (código IOP distinto de éxito, ya empaquetado, error de negocio), sin diferenciarlos entre sí para el llamador — dificulta distinguir "ya estaba empaquetado" (caso esperado) de un fallo real de la API.

  • Limpieza forzada del SDK en Jenkins: Igual que en lazada-client, el Jenkinsfile elimina la copia cacheada del SDK de terceros (~/.m2/repository/com/miravia) antes de cada build, sugiriendo problemas previos de caché con versiones desactualizadas de este artefacto.

  • MiraviaProductsRequest en formato XML-like con claves en mayúsculas: A diferencia del resto de DAOs (snake_case vía @SerializedName/@JsonProperty), MiraviaProductsRequest usa claves en PascalCase (Request, Product, Skus, Sku, SellerSku, Quantity), reflejando un formato de payload distinto (probablemente heredado de una integración basada en XML/SOAP, similar al patrón visto en lazada-client). No está referenciado por ningún método de MiraviaClient.

  • Sin tests implementados: El directorio src/test/ existe pero está vacío, según confirma CLAUDE.md.

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