Skip to content

Journal OTC

Requisitos previos de acceso

  • Permiso (módulo): viewOtc
  • Licencia/Feature: Ninguna
  • Contenedor del menú: GENERAL → grupo Diarios de transaccionesJournal OTC (/otc-journal)

Qué es / cuándo usar

Es el desk de OTC (mostrador) del BackOffice: lista las órdenes de cambio cripto↔fiat (típicamente USDT ↔ BRL), con el bloqueo cambiario, el total cobrado al cliente, el total en el proveedor, el spread (ingreso de la casa) y el estado interno de cada orden. Además de consultar, el operador ejecuta acciones administrativas sobre una orden: forzar actualización de estado, cancelar, liberar reserva y — en modo interno — confirmar/liquidar la operación.

Las órdenes pueden operar en dos modos:

  • ENOR — liquidación mediante proveedor externo (eNor). El ciclo de vida es dirigido por el scheduler de polling que consulta al proveedor.
  • INTERNAL — liquidación in-house: la plataforma debita el token de pago del usuario, acredita la tesorería y paga al usuario en el token de destino a partir de la tesorería.

Requisitos previos

  • Permiso: viewOtc (permiso doble — enum CPM + módulo dinámico en DB). Las acciones de mutación (cancelar/confirmar/liberar) son sensibles y pueden requerir reautenticación (step-up contraseña+MFA) según la política del tenant; el gating final es validado en el API-Gateway.
  • Licencia/Feature: ninguna.
  • Dependencias con otras pantallas: Ninguna. Las órdenes nacen del flujo de OTC (PaymentManagementService). Para que el modo INTERNAL liquide, debe existir la configuración customerToPay (cuenta de tesorería).

Paso a paso

  1. Acceda a Diarios de transacciones → Journal OTC.
  2. Revise los cards de resumen en la parte superior (totales del período filtrado).
  3. Aplique filtros: período (de/hasta), lado (compra/venta), ID de usuario, rango de USDT y chips de estado (selección múltiple). Haga clic en Aplicar.
  4. Haga clic en Exportar CSV para descargar la lista filtrada.
  5. Haga clic en el ícono de detalle (receipt_long) de una orden para abrir el modal con la línea de tiempo de eventos y las acciones administrativas.

Cards de resumen

CardQué muestra
Total de órdenesCantidad de órdenes en el período.
Total USDTVolumen total en USDT.
Total BRL (cliente)Suma de lo cobrado a los clientes.
Total spread (BRL)Ingreso de la casa en el período (delta entre el precio del cliente y el precio del proveedor).
Entregadas / Pendientes / Fallidas / CanceladasConteos por resultado.

Filtros y columnas

Filtro / ColumnaQué muestra / haceOrigen del dato
De / HastaVentana de fechas.from / to.
Lado (side)client_buys_usdt (BUY) o client_sells_usdt (SELL).side.
ID de usuarioFiltra por usuario.userId.
USDT mín/máxRango de volumen.usdtMin / usdtMax.
Chips de estadoSelección múltiple: LOCKED, PIX_SCHEDULED, PIX_SENT, ONCHAIN_PENDING, ONCHAIN_CONFIRMED, PAYMENT_SENT, DELIVERED, CANCELLED, EXPIRED, FAILED.status[].
Fecha (createdAt)Creación de la orden.createdAt.
UsuarioPropietario de la orden.userId.
Modo (otcMode)ENOR o INTERNAL.otcMode.
LadoBUY/SELL.side.
USDTCantidad negociada.usdtAmount.
Bloqueo cambiario (lockedRate)Tipo de cambio congelado para el cliente.lockedRate.
BRL clienteTotal cobrado al cliente.brlTotal.
BRL proveedor (eNor)Total en el proveedor.enorBrlTotal (cae a brlTotal si está ausente).
SpreadMonto + porcentaje (bps/100). Vacío () cuando es cero.spreadAmount / spreadBps.
Estado internoEstado de la orden (color por clase).internalStatus.
Detalle (acción)Abre el modal de la orden.openDetail(o).

Acciones y modales

El modal de detalle (OtcOrderDetailModalComponent) muestra la orden, el pago, la liquidación y la línea de tiempo de eventos (cada evento expandible con JSON de request/response). Las acciones abren un diálogo de confirmación con campo de motivo (auditado) y un tono visual (azul/naranja/rojo):

AcciónCuándo apareceQué hace en el backend
Forzar actualización (force-refresh)Siempre que exista un enorOrderId.Reconsulta el estado en el proveedor (eNor) y actualiza el estado local; si retorna client_delivered/cancelled, marca DELIVERED/CANCELLED. Útil cuando el polling se detuvo. Registra evento ADMIN_FORCE_REFRESH.
Cancelar (cancel)Estado en LOCKED, PIX_SCHEDULED, PIX_SENT, ONCHAIN_PENDING, ONCHAIN_CONFIRMED, PAYMENT_SENT.Cancela la orden en el proveedor y libera la reserva del usuario (best-effort) si existe reserveReference. Marca CANCELLED. Evento ADMIN_CANCEL.
Liberar reserva (release-reserve)Cuando existe una reserva pendiente o el estado es CANCELLED/FAILED/EXPIRED.Libera la reserva bloqueada del usuario (para órdenes con reserva "colgada"). Evento ADMIN_RELEASE_RESERVE.
Confirmar negociación (confirm-trade)Solo modo INTERNAL y estado LOCKED.Liquida la orden in-house: (1) compromete la reserva del usuario hacia la tesorería (debita el token de pago, acredita customerToPay); (2) paga al usuario en el token de destino a partir de la tesorería; (3) marca DELIVERED. Evento ADMIN_CONFIRM_TRADE.

Todas las acciones requieren confirmación y aceptan un motivo, que se graba como evento de auditoría en la orden.

Reglas de negocio / precauciones

Confirmar negociación (INTERNAL) es una liquidación real e irreversible

  • confirm-trade mueve valor de verdad: debita al usuario, acredita la tesorería y paga al usuario en el token de destino. El backend rechaza la acción si la orden no es INTERNAL, no está LOCKED, no tiene reserva o ya fue entregada — precisamente para evitar doble liquidación. Confirme el modo y el estado antes de actuar.
  • Las órdenes ENOR liquidan mediante el scheduler de polling, nunca por confirm-trade. No intente liquidar una orden ENOR manualmente aquí.

Atención

  • El spread es el ingreso de la casa: el delta entre el precio cobrado al cliente (brlTotal) y el precio del proveedor (enorBrlTotal). Es exactamente el patrón de "precio límite × ejecución" — no confunda el spread con un error de cálculo.
  • Cancelar ya intenta liberar la reserva; Liberar reserva es la reparación aislada para reservas que quedaron colgadas tras CANCELLED/FAILED/EXPIRED.
  • EXPIRED significa que la ventana del bloqueo cambiario venció antes de la liquidación.
  • Valores financieros: usdtAmount, lockedRate, brlTotal, spreadAmount se tratan como BigNumber/strings de alta precisión en el backend — la UI los formatea solo para lectura. No concilie por el valor mostrado sin verificar la precisión.
  • Idempotencia: los pasos de liquidación interna se apoyan en referencias de reserva/transacción idempotentes; un retry que retorne E00021 ("already processed") es éxito.

Ejemplos

Escenario 1 — Forzar actualización de una orden ENOR bloqueada en PIX_SENT

Una orden ENOR permaneció PIX_SENT durante horas porque el polling no avanzó. Abra el detalle → Forzar actualización. El backend reconsulta al proveedor: si ya existe client_delivered, la orden pasa a DELIVERED y la línea de tiempo gana el evento ADMIN_FORCE_REFRESH. Sin mover valor — solo sincroniza el estado.

Escenario 2 — Liquidar una orden INTERNAL (compra de USDT) confirmando la negociación

Un cliente compró USDT en modo INTERNAL; la orden está LOCKED con reserva creada. Abra el detalle → Confirmar negociación → ingrese el motivo. El backend: (1) compromete la reserva del usuario hacia la tesorería customerToPay (debita BRL/token de pago, acredita la tesorería); (2) paga el USDT al usuario a partir de la tesorería; (3) marca DELIVERED. La línea de tiempo registra ADMIN_CONFIRM_TRADE. Operación irreversible.

Escenario 3 — Cancelar y liberar una reserva colgada

Una orden FAILED dejó una reserva bloqueada en el saldo del usuario. Abra el detalle → Liberar reserva → ingrese el motivo. El backend libera la reserva (best-effort) y graba ADMIN_RELEASE_RESERVE. Si la orden todavía estuviera activa, el camino sería Cancelar, que ya libera la reserva en el mismo paso.

Pantallas relacionadas