Skip to main content

Recharge Client

1. Descripción general

recharge-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con la API de gestión de suscripciones de Recharge, plataforma de suscripción recurrente integrada con Shopify. 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 clientes, direcciones, suscripciones, métodos de pago, cargos y pedidos de suscripción.

La librería expone dos clientes HTTP independientes con estrategias de autenticación distintas:

  • RechargeSfccClient — cliente principal, con autenticación inyectada globalmente vía autoconfiguración (cabeceras por defecto). Cubre clientes, direcciones, suscripciones, métodos de pago y cargos.
  • RechargeHaLeClient — cliente secundario con URL fija (https://api.rechargeapps.com), donde el token de acceso se pasa explícitamente en cada llamada. Cubre cargos por pedido de Shopify, clientes por ID de Shopify, descuentos y pedidos.

2. Información técnica

PropiedadValor
artifactIdrecharge-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.rechargeclient
├── RechargeClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ ├── RechargeSfccClient.java # Interfaz @HttpExchange: customers, addresses, subscriptions, payment_methods, charges
│ └── RechargeHaLeClient.java # Interfaz @HttpExchange: charges, customers, addresses, orders (URL fija, token por llamada)
├── config/
│ ├── RechargeSfccClientConfig.java # @AutoConfiguration del cliente principal (cabeceras por defecto)
│ └── RechargeHaLeAutoConfiguration.java # @AutoConfiguration del cliente secundario (sin condición, URL hardcodeada)
├── entity/
│ ├── DiscountForm.java # Modelo de aplicación de descuento (RechargeHaLeClient)
│ └── OrdersRecharge.java # Modelo de respuesta de pedidos (RechargeHaLeClient)
└── sfccentity/ # DTOs que reflejan el contrato de la API de Recharge (RechargeSfccClient)
├── Customer.java / CustomerRequest.java / CustomerResponse.java / CustomersResponse.java
├── Address.java / AddressRequest.java / AddressResponse.java / AddressesResponse.java
├── Subscription.java / SubscriptionRequest.java / SubscriptionCreateRequest.java / SubscriptionResponse.java / SubscriptionsResponse.java
├── PaymentMethod.java / PaymentMethodRequest.java / PaymentMethodUpdateRequest.java / PaymentMethodResponse.java / PaymentMethodsResponse.java
├── Charge.java / ChargeResponse.java / ChargesResponse.java
└── userarea/ # Patrón de DTO dual: *Request (cara al consumidor) vs *RechargeRequest (cara a la API)
├── ChargeSkipRequest.java / ChargeSkipRechargeRequest.java
├── FrequencyUpdateRequest.java / FrequencyUpdateRechargeRequest.java
├── ProductUpdateRequest.java / ProductUpdateRechargeRequest.java
├── SubscriptionCancelRequest.java / SubscriptionCancelRechargeRequest.java
├── SubscriptionNextChargeDateUpdateRequest.java / SubscriptionNextChargeDateUpdateRechargeRequest.java
├── SubscriptionProductQtyUpdateRequest.java / SubscriptionProductQtyUpdateRechargeRequest.java
├── AddressUpdateRequest.java, ExternalTID.java, UserResponse.java

Flujo — RechargeSfccClient (autenticación global)

sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as RechargeSfccClient
participant API as API Recharge

Consumidor->>Client: getCustomers / createSubscription / skipCharge / ...
Client->>API: request + X-Recharge-Version + X-Recharge-Access-Token (cabeceras por defecto)
API-->>Client: ResponseEntity<T>
Client-->>Consumidor: ResponseEntity<T>

RechargeSfccClientConfig se activa condicionalmente con @ConditionalOnProperty(prefix = "recharge.auth.client", name = {"url", "version", "token"}), registrando RechargeSfccClient con un RestClient que fija X-Recharge-Version y X-Recharge-Access-Token como cabeceras por defecto (estáticas, sin renovación de token).

Flujo — RechargeHaLeClient (token por llamada)

sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as RechargeHaLeClient
participant API as api.rechargeapps.com

Consumidor->>Client: getChargesById(token, shopifyOrderId) / applyDiscount(token, addressId, form) / ...
Client->>API: request + header X-Recharge-Access-Token
API-->>Client: ResponseEntity<String | OrdersRecharge>
Client-->>Consumidor: ResponseEntity<T>

RechargeHaLeAutoConfiguration no declara ninguna condición @ConditionalOnProperty: registra siempre el bean RechargeHaLeClient con la URL base fija https://api.rechargeapps.com (hardcodeada en el código, no configurable).

El registro de ambas autoconfiguraciones se realiza mediante el fichero estándar de Spring Boot:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

Patrón de DTO dual en sfccentity/userarea/: cada operación de área de usuario tiene un DTO de cara al consumidor (*Request, con nombres de campo simplificados, p. ej. ChargeSkipRequest.idSubscription) y un DTO de cara a la API de Recharge (*RechargeRequest, con la forma exacta que exige el contrato remoto, p. ej. ChargeSkipRechargeRequest.subscriptionIDS/purchaseItemID). El consumidor debe mapear manualmente entre ambos antes de invocar al cliente — la librería no realiza esta conversión.

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.code.gson:gson2.14.0Anotaciones @SerializedName en los DTOs de sfccentity/userarea (soporte dual con Jackson)
com.fasterxml.jackson.core:jackson-annotations(gestionada SB4)Anotaciones @JsonProperty 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. recharge-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Recharge se detallan en la sección 6.

6. Integraciones externas

API de Recharge — RechargeSfccClient (cliente principal)

ÁreaMétodo clienteHTTPRuta remota
CustomersgetCustomersGET/customers
CustomersgetCustomerByIdGET/customers/{id}
CustomersgetCustomerByEmailGET/customers?email=
CustomerscreateCustomerPOST/customers
AddressesgetAddressGET/addresses/{id}
AddressesgetAddressesGET/addresses
AddressesgetAddressesByCustomerGET/addresses?customer_id=
AddressescreateAddressPOST/addresses
AddressesupdateAddressPUT/addresses/{id}
SubscriptionsgetSubscriptionsGET/subscriptions
SubscriptionsgetSubscriptionGET/subscriptions/{id} (ver anomalía en sección 13)
SubscriptionsgetSubscriptionsByCustomerGET/subscriptions?customer_id=
SubscriptionsgetSubscriptionsByCustomerAndAddressGET/subscriptions?customer_id=&address_id=
SubscriptionsgetSubscriptionsByAddressGET/subscriptions?address_id=
SubscriptionscreateSubscriptionPOST/subscriptions
SubscriptionsupdateFrequencySubscriptionPUT/subscriptions/{id}
SubscriptionsupdateProductSubscriptionPUT/subscriptions/{id}
SubscriptionsupdateNextChargeDatePOST/subscriptions/{id}/set_next_charge_date
SubscriptionsupdateQuantityPUT/subscriptions/{id}
SubscriptionscancelSubscriptionPOST/subscriptions/{id}/cancel
SubscriptionsactivateSubscriptionPOST/subscriptions/{id}/activate
Payment methodsgetPaymentMethodGET/payment_methods/{id}
Payment methodsgetPaymentMethodsByCustomerGET/payment_methods?customer_id=
Payment methodscreatePaymentMethodPOST/payment_methods
ChargesgetChargesByCustomerGET/charges?customer_id=&status=
ChargesgetChargesByAddressGET/charges?address_id=&status=
ChargesskipChargePOST/charges/{id}/skip

API de Recharge — RechargeHaLeClient (URL fija https://api.rechargeapps.com)

Método clienteHTTPRuta remotaDescripción
getChargesGET/chargesLista cargos (respuesta cruda String)
getChargesByIdGET/charges?shopify_order_id=Consulta cargos por ID de pedido de Shopify
getCustomersGET/customers?shopify_customer_id=Consulta clientes por ID de cliente de Shopify
getCustomerAddressGET/customers/{id_customer}/addressesConsulta las direcciones de un cliente
applyDiscountPOST/addresses/{id_addresses}/apply_discountAplica un descuento a una dirección/suscripción
getOrderGET/orders/{order_id}Consulta un pedido por ID de Recharge (respuesta cruda String)
getOrderByIdGET/orders?shopify_order_id=Consulta un pedido por ID de pedido de Shopify
removeDiscountPOST/addresses/{address_id}/remove_discountElimina un descuento aplicado a una dirección

Ejemplo de payload applyDiscount (DiscountForm):

{
"discount_id": 12345,
"discount_code": "PROMO2026"
}

Ejemplo de respuesta getOrderById/getChargesById (OrdersRecharge, resumida):

{
"orders": [
{
"id": 111222333,
"shopify_order_id": "444555666",
"customer_id": 987654,
"status": "success",
"charge_status": "success",
"total_price": "59.90",
"currency": "EUR",
"created_at": "2026-07-01T10:00:00Z"
}
]
}

Protocolo: HTTPS REST (JSON). Autenticación: cabecera X-Recharge-Access-Token — inyectada globalmente vía cabeceras por defecto en RechargeSfccClient, o pasada explícitamente por llamada en RechargeHaLeClient.

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 recharge.auth.client, solo para RechargeSfccClient)

PropiedadDescripciónEjemplo de valor
recharge.auth.client.urlURL base de la API de Recharge (activa la autoconfiguración)${RECHARGE_CLIENT_URL}
recharge.auth.client.versionValor de la cabecera X-Recharge-Version${RECHARGE_API_VERSION}
recharge.auth.client.tokenValor de la cabecera X-Recharge-Access-Token${RECHARGE_ACCESS_TOKEN}

Importante: Si falta cualquiera de las tres propiedades, el bean RechargeSfccClient no se registra. RechargeHaLeClient, en cambio, no depende de ninguna propiedad: se registra siempre con la URL fija https://api.rechargeapps.com, y el token se pasa por llamada.

Variables de entorno recomendadas

VariablePropiedad mapeada
RECHARGE_CLIENT_URLrecharge.auth.client.url
RECHARGE_API_VERSIONrecharge.auth.client.version
RECHARGE_ACCESS_TOKENrecharge.auth.client.token

8. Persistencia

No aplica a este proyecto. La librería no accede a ninguna base de datos ni mantiene estado en memoria.

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

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

# Ejecutar tests
./mvnw test

Uso como dependencia en un microservicio consumidor

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

RechargeSfccClient se activa automáticamente al declarar recharge.auth.client.*; RechargeHaLeClient está siempre disponible sin configuración adicional.

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/recharge-client/

12. Manejo de errores y logging

La mayoría de métodos de RechargeSfccClient y RechargeHaLeClient declaran throws RestClientResponseException, propagando al consumidor los errores HTTP devueltos por la API de Recharge. La excepción es skipCharge en RechargeSfccClient, que no declara ningún throws explícito (inconsistencia menor respecto al resto de métodos del mismo cliente). No hay configuración de logback ni de niveles de log específicos en la librería.

13. Notas y consideraciones

  • Posible bug en getSubscription: El método está anotado @GetExchange("/subscriptions/{id}") (con placeholder de ruta {id}), pero su parámetro está anotado @RequestParam(name = "id") en lugar de @PathVariable("id"). Con esta combinación, el parámetro se trataría como un query param (?id=...) en lugar de sustituir el placeholder {id} de la ruta, lo que probablemente provoque un error en tiempo de ejecución (variable de plantilla de URI sin resolver) al invocar este método. Pendiente de verificar en tiempo de ejecución; de confirmarse, es un defecto a corregir cambiando la anotación a @PathVariable.

  • RechargeHaLeAutoConfiguration sin @ConditionalOnProperty y con URL hardcodeada: A diferencia de RechargeSfccClientConfig (condicionado a tres propiedades) y de la práctica general del resto de clientes del ecosistema, RechargeHaLeAutoConfiguration registra el bean RechargeHaLeClient siempre, con la URL de producción de Recharge (https://api.rechargeapps.com) fijada en el código — no hay forma de apuntar este cliente a un entorno de pruebas/sandbox sin modificar la librería.

  • Dos clientes para la misma API con estrategias de autenticación incompatibles: RechargeSfccClient inyecta el token globalmente vía cabeceras por defecto (estático, sin renovación); RechargeHaLeClient exige el token como parámetro explícito en cada llamada. Un consumidor que necesite ambos conjuntos de operaciones debe gestionar dos flujos de autenticación distintos para el mismo proveedor.

  • Mezcla de @PathVariable sobre parámetros de query en RechargeHaLeClient: Métodos como getChargesById (@GetExchange("/charges?shopify_order_id={shopify_order_id}")) usan @PathVariable para rellenar un placeholder que en realidad forma parte de la query string, en lugar de @RequestParam. Aunque Spring resuelve correctamente placeholders {} dentro de la query string mediante variables de plantilla de URI (a diferencia del caso de getSubscription), es un patrón menos idiomático que usar @RequestParam directamente.

  • Patrón de DTO dual en sfccentity/userarea/: Cada operación tiene un par de clases (*Request/*RechargeRequest) con forma de campos distinta; el consumidor es responsable de mapear del DTO "de cara al usuario" al DTO "de cara a la API" antes de invocar el cliente. Aporta una capa de indirección que puede confundir si no se conoce el patrón de antemano — no hay ningún mapper automático en la librería.

  • Respuestas crudas (String) en varios métodos de RechargeHaLeClient: getCharges, getCustomers, getCustomerAddress, applyDiscount, getOrder y removeDiscount devuelven ResponseEntity<String> sin deserializar, mientras que getChargesById y getOrderById sí devuelven el tipo OrdersRecharge. La cobertura de modelos tipados es parcial.

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

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