Volver al blogProducto

Hailan Envíos: automatizamos guías con Skydropx PRO

30 de mayo de 20268 min de lectura

Detrás de la integración Skydropx en Hailan. El formato real de la API PRO, el patrón de lock atómico contra doble cobro, los 11 eventos del webhook y cómo notificamos al cliente vía WhatsApp en tiempo real.

El módulo de envíos de un e-commerce parece simple desde afuera: el cliente captura dirección, se cotiza, se elige carrier, se compra guía, se rastrea. En la realidad, cada uno de esos pasos tiene 5 detalles operativos que pueden romper el flujo. Esta es la nota técnica de cómo levantamos Hailan Envíos integrando Skydropx PRO.

Por qué Skydropx PRO

Skydropx es el aggregator de paqueterías más sólido en México: DHL, FedEx, Paquetexpress, AMPM, Estafeta, Redpack, 99 Minutos, J&T, Quiken y más, accesibles desde una sola API. Ya cubren el 95%+ del mercado mexicano de e-commerce.

Formato real de la API PRO (no el que dice la doc)

La documentación pública no es trivial de leer. Después de iterar con el playground del dashboard, el formato real para cotizar es:

POST /api/v1/quotations
{
  "quotation": {
    "address_from": {
      "country_code": "MX",
      "postal_code": "72410",
      "area_level1": "Puebla",
      "area_level2": "Puebla",
      "area_level3": "Centro"
    },
    "address_to": { ... },
    "parcels": [{ "weight": 2.5, "length": 40, "width": 30, "height": 20 }]
  }
}

Los tres area_level NO pueden ir vacíos. Skydropx los exige incluso si vas a usar "N/A" (lo infiere del CP, pero el campo debe existir). Esto no estaba documentado en ningún lado — lo descubrimos probando contra el endpoint real.

El lock atómico contra doble cobro

Al generar una guía, el riesgo más caro es el doble clic: dos requests simultáneos compran dos guías distintas, una se desperdicia, el cliente paga el doble. Lo resolvimos con un UPDATE atómico de Postgres:

UPDATE orders
SET shipping_skydropx_shipment_id = 'pending:<uuid>'
WHERE id = $orderId
AND shipping_skydropx_shipment_id IS NULL
RETURNING id

Solo el request que gana ese UPDATE procede a comprar. El otro recibe HTTP 409 con "Guía en proceso". Si algo falla en Skydropx después del lock, lo liberamos en el catch global. Si la guía se compra exitosa, el UPDATE final sobrescribe el marcador pending: con el ID real del shipment.

11 eventos del webhook, todos cubiertos

Skydropx PRO emite webhooks por cada evento del paquete. Los 11 que mapeamos:

  • created → guía generada
  • picked_up → paquete en manos del carrier (notifica al cliente: "ya lo recogimos")
  • in_transit → en camino (notifica: "va con DHL")
  • last_mile → cerca de entrega (notifica: "está cerca")
  • delivery_attempt → intento fallido (notifica al cliente para que esté pendiente del re-intento)
  • delivered → entregado (notifica: "✅ gracias por tu compra")
  • delivered_to_branch → listo en sucursal (notifica: "recógelo en sucursal de FedEx")
  • in_return → devolución en curso (notifica + escala a soporte)
  • exception / error → incidencia (notifica + escala)
  • canceled → cancelado (interno, no notifica al cliente)

Cada notificación al cliente sale por WhatsApp Business desde el mismo número que el tenant usa para todo lo demás. Marca consistente, fricción cero.

Tracking URL del carrier real, no el nuestro

Cuando el webhook llega con tracking_url_provider (la URL oficial del carrier: DHL.com, FedEx.com, etc.), la guardamos y se la mostramos al cliente. Si no llega, generamos una nosotros con un mapper local por carrier. El cliente siempre tiene un link real de rastreo.

Margen central editable desde super-admin

Hailan opera Skydropx centralmente y revende a los tenants con un margen del 20% sobre venta (la fórmula customer_amount = provider_amount / (1 - margin/100)). El porcentaje vive en una tabla hailan_central_config con una sola fila, editable desde super-admin. Si mañana cambiamos a 25%, todos los tenants lo reciben automático en su siguiente cotización.

Cliente nunca ve el PDF, solo el tracking

La guía PDF es para el admin del tenant que prepara el envío. El cliente final solo ve número de guía + carrier + link de rastreo. Esto es importante: el PDF tiene metadata interna que no debe estar pública.

Todo esto vive en src/features/shipping/services/skydropx-client.ts, src/app/api/orders/[id]/shipping-label/route.ts y src/app/api/skydropx/webhook/route.ts en el repo. Si te interesa el approach técnico, escríbenos.

¿Quieres aplicar esto a tu negocio?

Te ayudamos a montar tu operación en Hailan en menos de una semana.

Hablar con Hailan