Skip to content

Journal OTC

Pré-requisitos de acesso

  • Permissão (módulo): viewOtc
  • Licença/Feature: Nenhuma
  • Contêiner do menu: GERAL → grupo Jornais de transaçõesJournal OTC (/otc-journal)

O que é / quando usar

É o desk de OTC (balcão) do BackOffice: lista as ordens de câmbio cripto↔fiat (tipicamente USDT ↔ BRL), com a taxa travada, o total cobrado do cliente, o total no provedor, o spread (receita da casa) e o status interno de cada ordem. Além de consultar, o operador executa ações administrativas sobre uma ordem: forçar atualização de status, cancelar, liberar reserva e — em modo interno — confirmar/liquidar a operação.

As ordens podem operar em dois modos:

  • ENOR — liquidação via provedor externo (eNor). O ciclo de vida é dirigido pelo scheduler de polling que consulta o provedor.
  • INTERNAL — liquidação in-house: a plataforma debita o token de pagamento do usuário, credita a tesouraria e paga o usuário no token de destino a partir da tesouraria.

Pré-condições

  • Permissão: viewOtc (permissão dupla — enum CPM + módulo dinâmico no DB). As ações de mutação (cancelar/confirmar/liberar) são sensíveis e podem exigir re-autenticação (step-up senha+MFA) conforme política do tenant; o gating final é validado no API-Gateway.
  • Licença/Feature: nenhuma.
  • Dependências de outras telas: Nenhuma. As ordens nascem do fluxo de OTC (PaymentManagementService). Para o modo INTERNAL liquidar, a configuração customerToPay (conta-tesouraria) precisa existir.

Passo a passo

  1. Acesse Jornais de transações → Journal OTC.
  2. Confira os cards de resumo no topo (totais do período filtrado).
  3. Aplique filtros: período (de/até), lado (compra/venda), ID do usuário, faixa de USDT e chips de status (multi-seleção). Clique em Aplicar.
  4. Clique em Exportar CSV para baixar a lista filtrada.
  5. Clique no ícone de detalhe (receipt_long) de uma ordem para abrir o modal com a timeline de eventos e as ações administrativas.

Cards de resumo

CardO que mostra
Total de ordensQuantidade de ordens no período.
Total USDTVolume total em USDT.
Total BRL (cliente)Soma do que foi cobrado dos clientes.
Total spread (BRL)Receita da casa no período (delta entre preço do cliente e preço do provedor).
Entregues / Pendentes / Falhas / CanceladasContagens por desfecho.

Filtros e colunas

Filtro / ColunaO que mostra / fazOrigem do dado
De / AtéJanela de datas.from / to.
Lado (side)client_buys_usdt (BUY) ou client_sells_usdt (SELL).side.
ID do usuárioFiltra por usuário.userId.
USDT mín/máxFaixa de volume.usdtMin / usdtMax.
Chips de statusMulti-seleção: LOCKED, PIX_SCHEDULED, PIX_SENT, ONCHAIN_PENDING, ONCHAIN_CONFIRMED, PAYMENT_SENT, DELIVERED, CANCELLED, EXPIRED, FAILED.status[].
Data (createdAt)Criação da ordem.createdAt.
UsuárioDono da ordem.userId.
Modo (otcMode)ENOR ou INTERNAL.otcMode.
LadoBUY/SELL.side.
USDTQuantidade negociada.usdtAmount.
Taxa travada (lockedRate)Câmbio congelado para o cliente.lockedRate.
BRL clienteTotal cobrado do cliente.brlTotal.
BRL provedor (eNor)Total no provedor.enorBrlTotal (cai para brlTotal se ausente).
SpreadValor + percentual (bps/100). Vazio () quando zero.spreadAmount / spreadBps.
Status internoEstado da ordem (cor por classe).internalStatus.
Detalhe (ação)Abre o modal da ordem.openDetail(o).

Ações e modais

O modal de detalhe (OtcOrderDetailModalComponent) mostra a ordem, o pagamento, a liquidação e a timeline de eventos (cada evento expansível com request/response JSON). As ações abrem um diálogo de confirmação com campo de motivo (auditado) e um tom visual (azul/laranja/vermelho):

AçãoQuando apareceO que faz no backend
Forçar atualização (force-refresh)Sempre que há enorOrderId.Reconsulta o status no provedor (eNor) e atualiza o estado local; se vier client_delivered/cancelled, marca DELIVERED/CANCELLED. Útil quando o polling travou. Registra evento ADMIN_FORCE_REFRESH.
Cancelar (cancel)Status em LOCKED, PIX_SCHEDULED, PIX_SENT, ONCHAIN_PENDING, ONCHAIN_CONFIRMED, PAYMENT_SENT.Cancela a ordem no provedor e libera a reserva do usuário (best-effort) se houver reserveReference. Marca CANCELLED. Evento ADMIN_CANCEL.
Liberar reserva (release-reserve)Quando há reserva pendente ou status em CANCELLED/FAILED/EXPIRED.Libera a reserva travada do usuário (para ordens com reserva "pendurada"). Evento ADMIN_RELEASE_RESERVE.
Confirmar negociação (confirm-trade)Apenas modo INTERNAL e status LOCKED.Liquida a ordem in-house: (1) compromete a reserva do usuário para a tesouraria (debita o token de pagamento, credita customerToPay); (2) paga o usuário no token de destino a partir da tesouraria; (3) marca DELIVERED. Evento ADMIN_CONFIRM_TRADE.

Todas as ações pedem confirmação e aceitam um motivo, que é gravado como evento de auditoria na ordem.

Regras de negócio / cuidados

Confirmar negociação (INTERNAL) é liquidação real e irreversível

  • confirm-trade move valor de fato: debita o usuário, credita a tesouraria e paga o usuário no token de destino. O backend rejeita a ação se a ordem não for INTERNAL, não estiver LOCKED, não tiver reserva, ou já estiver entregue — justamente para evitar dupla liquidação. Confirme o modo e o status antes de agir.
  • Ordens ENOR liquidam pelo scheduler de polling, nunca por confirm-trade. Não tente liquidar uma ordem ENOR manualmente por aqui.

Atenção

  • O spread é a receita da casa: o delta entre o preço cobrado do cliente (brlTotal) e o preço do provedor (enorBrlTotal). É exatamente o padrão de "preço-limite × execução" — não confunda o spread com erro de cálculo.
  • Cancelar já tenta liberar a reserva; Liberar reserva é o reparo isolado para reservas que ficaram penduradas após CANCELLED/FAILED/EXPIRED.
  • EXPIRED significa que a janela da taxa travada venceu antes da liquidação.
  • Valores financeiros: usdtAmount, lockedRate, brlTotal, spreadAmount são tratados como BigNumber/strings de alta precisão no backend — a UI formata apenas para leitura. Não reconcilie pelo valor exibido sem conferir a precisão.
  • Idempotência: os passos de liquidação interna se apoiam em referências de reserva/transação idempotentes; um retry que retorne E00021 ("already processed") é sucesso.

Exemplos

Cenário 1 — Forçar atualização de uma ordem ENOR travada em PIX_SENT

Uma ordem ENOR ficou PIX_SENT por horas porque o polling não evoluiu. Abra o detalhe → Forçar atualização. O backend reconsulta o provedor: se já houver client_delivered, a ordem passa a DELIVERED e a timeline ganha o evento ADMIN_FORCE_REFRESH. Sem mover valor — apenas sincroniza o status.

Cenário 2 — Liquidar uma ordem INTERNAL (compra de USDT) confirmando a negociação

Cliente comprou USDT em modo INTERNAL; a ordem está LOCKED com reserva criada. Abra o detalhe → Confirmar negociação → informe o motivo. O backend: (1) compromete a reserva do usuário para a tesouraria customerToPay (debita BRL/token de pagamento, credita a tesouraria); (2) paga o USDT ao usuário a partir da tesouraria; (3) marca DELIVERED. A timeline registra ADMIN_CONFIRM_TRADE. Operação irreversível.

Cenário 3 — Cancelar e liberar reserva pendurada

Uma ordem FAILED deixou uma reserva travada no saldo do usuário. Abra o detalhe → Liberar reserva → informe o motivo. O backend libera a reserva (best-effort) e grava ADMIN_RELEASE_RESERVE. Se a ordem ainda estivesse ativa, o caminho seria Cancelar, que já libera a reserva no mesmo passo.

Telas relacionadas