@whsflow/sdk
v1.0.0
Published
SDK TypeScript para conectar aplicaciones con backend-clubhx
Maintainers
Readme
ClubHX SDK
SDK TypeScript standalone para conectar aplicaciones frontend/Node.js con backend-clubhx. Diseñado para ser copiado y pegado en cualquier proyecto sin dependencias externas.
Características
- ✅ Type-safe: TypeScript completo con tipos bien definidos
- ✅ Autenticación dual: Soporta service-to-service y Bearer tokens
- ✅ Multi-tenancy: Soporte para
X-Shop-Domainheader - ✅ Sin dependencias: Usa solo APIs nativas de Node.js 18+
- ✅ Manejo de errores: Versiones
safe()que no lanzan excepciones - ✅ Modular: Recursos organizados por entidad
Instalación
Copia la carpeta src/ completa a tu proyecto:
cp -r clubhx-sdk/src ./src/lib/clubhx-sdkO simplemente copia los archivos que necesites según tu caso de uso.
Uso Básico
Autenticación Service-to-Service
Para aplicaciones como whsflow que usan X-Club-Service-Token:
import { ClubClient } from './src/lib/clubhx-sdk/client';
const client = new ClubClient({
baseUrl: process.env.CLUB_BACKEND_URL || 'http://localhost:3005',
auth: {
type: 'service',
serviceToken: process.env.INTEGRATIONS_SERVICE_TOKEN || '',
},
shopDomain: 'mi-tienda.myshopify.com', // Opcional
});
// Listar customers
const customers = await client.customers.list({ page: 1, limit: 20 });
// Usar versión safe (no lanza errores)
const result = await client.customers.listSafe({ page: 1, limit: 20 });
if (result.ok) {
console.log(result.items);
} else {
console.error(result.error);
}Autenticación Bearer Token
Para storefronts que usan tokens JWT de usuario:
import { ClubClient } from './src/lib/clubhx-sdk/client';
const client = new ClubClient({
baseUrl: process.env.CLUB_BACKEND_URL || 'http://localhost:3005',
auth: {
type: 'bearer',
token: userAccessToken, // Token JWT del usuario
},
shopDomain: 'mi-tienda.myshopify.com', // Opcional
});
// Listar events
const events = await client.events.list({ status: 'active' });Recursos Disponibles
Products (/api/v1/product)
// Listar products
const products = await client.products.list({ limit: 20, offset: 0, search: 'term' });
// Obtener product por ID
const product = await client.products.get('product-uuid');
// Crear product (con imagen opcional)
const newProduct = await client.products.create({
name: 'Producto Ejemplo',
code: 'PROD-001',
price: '15000',
image: fileObject, // File object para multipart/form-data
});
// Actualizar product completo
const updated = await client.products.update('product-uuid', { name: 'Nuevo Nombre' });
// Actualizar product parcialmente
const patched = await client.products.patch('product-uuid', { price: '20000' });
// Eliminar product
await client.products.delete('product-uuid');Addresses (/api/v1/addresses)
// Listar addresses
const addresses = await client.addresses.list();
// Crear address
const address = await client.addresses.create({
customerId: 'customer-uuid',
street: 'Calle Principal',
number: '123',
commune: 'Santiago',
region: 'Región Metropolitana',
isDefault: true,
});
// Actualizar address
const updated = await client.addresses.update('address-uuid', { number: '456' });
// Eliminar address
await client.addresses.delete('address-uuid');
// Establecer address como predeterminado
const defaultAddr = await client.addresses.setDefault('address-uuid');Auth (/api/v1/auth)
// Login
const authResponse = await client.auth.login({
identification: '12345678-9',
password: 'password123',
});
// Registro
const registerResponse = await client.auth.register({
identification: '12345678-9',
password: 'password123',
email: '[email protected]',
name: 'Usuario Ejemplo',
});Catalog (/api/v1/catalog)
// Listar categories
const categories = await client.catalog.listCategories();
// Crear category
const category = await client.catalog.createCategory({
name: 'Electrónica',
description: 'Productos electrónicos',
order: 1,
});
// Actualizar category
const updated = await client.catalog.updateCategory('category-uuid', { name: 'Electronics' });
// Eliminar category
await client.catalog.deleteCategory('category-uuid');
// Listar brands
const brands = await client.catalog.listBrands();Wishlist (/api/v1/wishlist)
// Obtener analytics de wishlist
const analytics = await client.wishlist.getAnalytics();Loyalty Points (/api/v1/loyalty)
// Obtener puntos de lealtad
const points = await client.loyaltyPoints.getPoints();
// Obtener puntos próximos a expirar
const expiring = await client.loyaltyPoints.getPointsExpiring();
// Obtener historial de puntos ganados
const earned = await client.loyaltyPoints.getPointsEarned({ limit: 20, offset: 0 });Delivery (/api/v1/delivery)
// Generar labels de envío
const labels = await client.delivery.generateLabels({
orderIds: ['order-uuid-1', 'order-uuid-2'],
});
// Obtener tracking
const tracking = await client.delivery.getTracking('order-uuid');
// Actualizar estados masivamente
const updates = await client.delivery.bulkUpdateStatuses({
orderIds: ['order-uuid-1', 'order-uuid-2'],
});Dashboard (/api/v1/dashboard)
// Obtener métricas
const metrics = await client.dashboard.getMetrics({ period: 'month' });Customers (/api/v1/clients)
// Listar customers
const customers = await client.customers.list({ page: 1, limit: 20 });
const result = await client.customers.listSafe({ page: 1, limit: 20 });Orders (/api/v1/order)
// Listar orders
const orders = await client.orders.list({ page: 1, limit: 10 });
const result = await client.orders.listSafe({ page: 1, limit: 10 });Loyalty Rewards (/api/v1/loyalty-rewards)
// Listar rewards
const rewards = await client.loyalty.list({ page: 1, limit: 10, category: 'Descuentos' });
// Crear reward
const newReward = await client.loyalty.create({
name: 'Descuento 10%',
category: 'Descuentos',
points_required: 100,
stock_quantity: 50,
is_featured: true,
});
// Obtener estadísticas
const stats = await client.loyalty.getStats();Vendors (/api/v1/vendors)
// Listar vendors
const vendors = await client.vendors.list();
// Crear vendor
const vendor = await client.vendors.create({
name: 'Juan Pérez',
email: '[email protected]',
status: 'active',
});
// Establecer goal
const goal = await client.vendors.setGoal({
vendorId: 'vendor-uuid',
period: '2026-01',
salesTarget: 5000000,
});Visits (/api/v1/visits)
// Listar visits
const visits = await client.visits.list({
from: '2026-01-01',
to: '2026-01-31',
salesPersonId: 'vendor-uuid',
});
// Crear visit
const visit = await client.visits.create({
customerId: 'customer-uuid',
date: '2026-01-15',
time: '14:00',
duration: 60,
status: 'scheduled',
});
// Actualizar visit
const updated = await client.visits.update('visit-uuid', { status: 'completed' });
// Eliminar visit
await client.visits.delete('visit-uuid');Events (/api/v1/events)
// Listar events
const events = await client.events.list({
page: 1,
limit: 10,
status: 'active',
isPublic: true,
});
// Crear event
const event = await client.events.create({
title: 'Evento de Lanzamiento',
description: 'Descripción del evento',
start_date: '2026-02-01T10:00:00Z',
end_date: '2026-02-01T18:00:00Z',
currency: 'CLP',
max_capacity: 100,
is_public: true,
});
// Obtener estadísticas
const stats = await client.events.getStats();Credit Requests (/api/v1/credit-requests)
// Listar credit requests
const requests = await client.creditRequests.list({
page: 1,
limit: 20,
status: 'pending',
});
// Crear request
const request = await client.creditRequests.create({
customerId: 'customer-uuid',
customerName: 'Cliente Ejemplo',
requestedBy: 'user-uuid',
requestedLimit: 1000000,
});
// Aprobar request
const approved = await client.creditRequests.approve({
id: 'request-uuid',
notes: 'Aprobado por admin',
});
// Obtener estadísticas
const stats = await client.creditRequests.getStats();Shipping Types (/api/v1/shippingtype)
// Listar shipping types
const types = await client.shipping.list();
const result = await client.shipping.listSafe();Payment Methods (/api/v1/paymentmethod)
// Listar payment methods
const methods = await client.payment.list();
const result = await client.payment.listSafe();Integrations (/integrations/*)
// Onboard de tienda Shopify
const onboard = await client.integrations.onboardShopifyShop({
shop_domain: 'mi-tienda.myshopify.com',
access_token: 'shpat_...',
scopes: 'read_products,write_products',
});
// Obtener conexión activa
const connection = await client.integrations.getActiveShopConnection('mi-tienda.myshopify.com');
// Listar conexiones
const connections = await client.integrations.listShopConnections('mi-tienda.myshopify.com');
// Forward webhook
await client.integrations.forwardShopifyWebhookToClub({
shop_domain: 'mi-tienda.myshopify.com',
topic: 'orders/create',
webhook_id: 'webhook-id',
payload: { /* webhook data */ },
});Configuración
Variables de Entorno
# URL del backend
CLUB_BACKEND_URL=http://localhost:3005
# Para service-to-service auth
INTEGRATIONS_SERVICE_TOKEN=your-service-token
# Para Bearer auth (obtenido del login)
USER_ACCESS_TOKEN=eyJhbGc...Logger Personalizado
Puedes proporcionar un logger personalizado:
const client = new ClubClient({
baseUrl: 'http://localhost:3005',
auth: { type: 'service', serviceToken: 'token' },
logger: {
error: (msg, meta) => console.error(`[ERROR] ${msg}`, meta),
warn: (msg, meta) => console.warn(`[WARN] ${msg}`, meta),
info: (msg, meta) => console.info(`[INFO] ${msg}`, meta),
debug: (msg, meta) => console.debug(`[DEBUG] ${msg}`, meta),
},
});Migración desde whsflow
Si estás migrando desde whsflow/app/lib/club.server.ts:
Antes:
import { listCustomersSafe } from '../lib/club.server';
const result = await listCustomersSafe({
shop_domain: 'mi-tienda.myshopify.com',
authorization: undefined,
page: 1,
limit: 20,
});Después:
import { ClubClient } from './lib/clubhx-sdk/client';
const client = new ClubClient({
baseUrl: process.env.CLUB_BACKEND_URL,
auth: { type: 'service', serviceToken: process.env.INTEGRATIONS_SERVICE_TOKEN },
shopDomain: 'mi-tienda.myshopify.com',
});
const result = await client.customers.listSafe({ page: 1, limit: 20 });Ventajas:
- Centralización: Toda la lógica de conexión está en un solo lugar
- Type-safety: TypeScript completo con autocompletado
- Reutilizable: Fácil copiar a otros proyectos
- Mantenible: Agregar nuevos recursos no requiere modificar código existente
Estructura del SDK
src/
├── client.ts # Clase principal ClubClient
├── config.ts # Tipos de configuración
├── auth/
│ ├── service-auth.ts # Autenticación service-to-service
│ └── bearer-auth.ts # Autenticación Bearer token
├── resources/
│ ├── customers.ts # Resource de customers
│ ├── orders.ts # Resource de orders
│ ├── loyalty.ts # Resource de loyalty
│ └── ... # Otros resources
├── types/
│ ├── common.ts # Tipos compartidos
│ └── ... # Tipos por recurso
└── utils/
├── fetch.ts # Helpers para fetch
└── url.ts # Normalización de URLsRequisitos
- Node.js 18+ (para
fetchnativo) - TypeScript 4.5+
Ejemplos
Ver examples/ para ejemplos completos de uso:
examples/service-auth.example.ts- Uso con service-to-service authexamples/bearer-auth.example.ts- Uso con Bearer token auth
Licencia
Este SDK es parte del proyecto ClubHX y está sujeto a la misma licencia.
