Documento histórico
Este documento se conserva solo para trazabilidad. Consulte el índice maestro para la fuente vigente.
Documento histórico — no usar como fuente de decisiones actuales
Reemplazado por la versión canónica del 2026-07-11. Conservar solo como trazabilidad histórica.
Diagrama de secuencia — Pago y despacho de envío¶
Diagrama UML de secuencia para el flujo completo de pago y despacho, que incluye al usuario (pago) y al operador (despacho).
Diagrama completo¶
sequenceDiagram
autonumber
actor U as Usuario
participant App as App
participant API as FastAPI
participant Auth as Auth Service
participant ShipSvc as Shipment Service
participant QuoteSvc as Quote Service
participant PaySvc as Payment Service
participant LockSvc as Redis Lock
participant DB as PostgreSQL
participant Notif as Notification Service
actor OP as Operador
participant Back as Backoffice
Note over U,OP: Parte A: Usuario paga
U->>App: Abre cotización pendiente
App->>API: GET /quotes/{id}
API->>Auth: Validar JWT
API->>QuoteSvc: getQuote(quote_id, user_id)
QuoteSvc->>DB: SELECT * FROM quotes WHERE id = ?
DB-->>QuoteSvc: quote
QuoteSvc-->>API: quote
API-->>App: quote (vigente?)
alt Cotización expirada
App-->>U: "Cotización expirada, se recalculará"
App->>API: POST /quotes/{id}/recalculate
API->>QuoteSvc: recalculate
QuoteSvc-->>API: nueva quote
end
U->>App: Toca "Pagar"
App->>API: POST /payments {quote_id, method}
API->>PaySvc: createPayment(quote_id, method)
PaySvc->>LockSvc: acquire lock payment:{quote_id}
LockSvc-->>PaySvc: lock acquired
PaySvc->>DB: SELECT * FROM shipments WHERE id = ? AND status = 'pendiente_pago'
DB-->>PaySvc: shipment
PaySvc->>DB: SELECT * FROM kyc_records WHERE user_id = ? AND status = 'aprobado'
DB-->>PaySvc: KYC
alt Sin KYC aprobado
PaySvc-->>API: error KYC_REQUIRED
API-->>App: 403 KYC required
end
PaySvc->>DB: BEGIN TRANSACTION
PaySvc->>DB: INSERT payments (status='completado' manual)
PaySvc->>DB: UPDATE quotes SET status='aceptada'
PaySvc->>DB: UPDATE shipments SET status='pagado'
PaySvc->>DB: UPDATE consolidations SET status='pagada'
PaySvc->>DB: UPDATE physical_packages SET status='listo_para_consolidar' WHERE consolidation_id = ?
PaySvc->>DB: INSERT charges (desglose)
PaySvc->>DB: INSERT invoices
PaySvc->>DB: COMMIT
PaySvc->>LockSvc: release lock
PaySvc-->>API: payment OK
API-->>App: 201 {payment, invoice}
App-->>U: "Pago confirmado"
par Notificaciones
API->>Notif: sendPush "Pago confirmado, en preparación"
API->>DB: INSERT audit_logs
end
Note over U,OP: Parte B: Operador despacha
OP->>Back: Accede a cola "Consolidaciones por preparar"
Back->>API: GET /consolidations?status=pagada
API->>DB: SELECT consolidations WHERE status='pagada'
DB-->>API: list
API-->>Back: list
OP->>Back: Selecciona consolidación
OP->>Back: Verifica paquetes (escaneo)
OP->>Back: Ingresa master_guide y carrier
OP->>Back: Toca "Marcar como despachado"
Back->>API: POST /shipments/{id}/dispatch {master_guide, carrier}
API->>ShipSvc: dispatchShipment(shipment_id, master_guide, carrier, operator_id)
ShipSvc->>DB: BEGIN TRANSACTION
ShipSvc->>DB: UPDATE shipments SET status='despachado_desde_almacen', master_guide, carrier, shipped_at
ShipSvc->>DB: UPDATE consolidations SET status='despachada'
ShipSvc->>DB: UPDATE physical_packages SET status='en_transito' (batch)
ShipSvc->>DB: INSERT package_events (batch)
ShipSvc->>DB: INSERT shipment_events
ShipSvc->>DB: COMMIT
DB-->>ShipSvc: OK
ShipSvc-->>API: shipment despachado
API-->>Back: 200 OK
Back-->>OP: "Envío despachado"
par Notificación
API->>Notif: sendPush "Tu envío ha sido despachado. Guía: {master_guide}"
end
Note over U,OP: Parte C: Tracking en tránsito
OP->>Back: Actualiza estado: en_transito_a_cuba
Back->>API: PATCH /shipments/{id}/status {status: 'en_transito_a_cuba'}
API->>DB: UPDATE shipments + INSERT event
API->>Notif: sendPush "En camino a Cuba"
API-->>Back: 200 OK
OP->>Back: Actualiza estado: en_aduana
Back->>API: PATCH status: 'en_aduana'
API->>DB: UPDATE
API->>Notif: sendPush "En proceso de aduana"
API-->>Back: 200 OK
OP->>Back: Actualiza estado: en_reparto
Back->>API: PATCH status: 'en_reparto'
API->>DB: UPDATE
API->>Notif: sendPush "Próximo a recibir"
API-->>Back: 200 OK
OP->>Back: Actualiza estado: entregado
Back->>API: PATCH status: 'entregado'
API->>DB: UPDATE shipments SET delivered_at, status='entregado'
API->>DB: UPDATE physical_packages SET status='entregado'
API->>Notif: sendPush "¡Tu paquete fue entregado!"
API-->>Back: 200 OK
Descripción del flujo¶
Parte A: Pago (Usuario)¶
- Usuario abre la cotización pendiente
- Si está expirada, se recalcula automáticamente
- Usuario toca "Pagar"
- Backend adquiere lock distribuido en Redis (evita doble pago)
- Valida KYC aprobado
- Transacción ACID: crea
Payment, actualizaQuote,Shipment,Consolidation,PhysicalPackage, creaChargeseInvoice - Libera lock
- Push de confirmación
Parte B: Despacho (Operador)¶
- Operador ve la cola de consolidaciones pagadas
- Selecciona una y verifica los paquetes
- Ingresa
master_guide(de la agencia) ycarrier - Toca "Marcar como despachado"
- Transacción: actualiza estados de
Shipment,Consolidation,PhysicalPackage; crea eventos - Push al usuario con el número de guía
Parte C: Tracking (Operador + Sistema)¶
- El operador actualiza el estado del envío a medida que avanza
- Cada cambio crea un
shipment_eventcon timestamp - Cada cambio genera push al usuario
- Cuando llega a
entregado, se cierra el ciclo
Lock distribuido (detalle)¶
El lock sobre payment:{quote_id} previene:
- Doble pago por clics concurrentes
- Pago y cancelación simultáneos
- Pago de una cotización que se está recalculando
# Pseudocódigo simplificado
with redis.lock(f"payment:{quote_id}", timeout=10):
if shipment.status != 'pendiente_pago':
raise InvalidStateError
# transacción ACID
Si el lock no se adquiere en 3 reintentos, se devuelve LOCK_NOT_ACQUIRED y se le pide al usuario reintentar.
Manejo de errores¶
| Etapa | Error | Manejo |
|---|---|---|
| Pago | Cotización expirada | Recalcular y notificar |
| Pago | Sin KYC | Bloqueo con CTA a KYC |
| Pago | Lock no adquirido | Mensaje de reintento |
| Pago | DB error | Rollback automático + log |
| Despacho | Master guide inválido | Validación en front |
| Despacho | Estado incorrecto | No permite despachar si no está pagado |
| Tracking | Transición inválida | Validar con máquina de estados |