Skip to content

BACEN Gateway

The BACEN Gateway (bacen_gateway) is the core integration layer between FluxiQ SPB and the Brazilian Central Bank's RSFN (Rede do Sistema Financeiro Nacional). It handles all 979 BACEN message types, manages IBM MQ connections, and enforces message schema validation and digital signatures.

Port: 4002

Responsibilities

  • Connect to BACEN's RSFN network via IBM MQ
  • Build, validate, and parse all 979 BACEN XML message types
  • Apply digital signatures to outbound messages via HSM
  • Verify digital signatures on inbound BACEN messages
  • Manage message sequencing and the NUOp (Numero Unico de Operacao)
  • Handle R1, R2, and R3 response correlation
  • Provide a BACEN simulator mode for development and testing

RSFN Connection Architecture

FluxiQ SPB                          BACEN / RSFN
+------------------+                +------------------+
|                  |   IBM MQ       |                  |
| BACEN Gateway    |<-------------->|  RSFN Network    |
|                  |   Channel:     |                  |
| +-------------+  |   BACEN.SVRCONN|  +------------+  |
| | MQ Producer |--+--------------->|  | Inbound Q  |  |
| +-------------+  |                |  +------------+  |
|                  |                |                  |
| +-------------+  |                |  +------------+  |
| | MQ Consumer |<-+----------------|  | Outbound Q |  |
| +-------------+  |                |  +------------+  |
+------------------+                +------------------+

Configuration

elixir
# config/runtime.exs
config :bacen_gateway, BacenGateway.MQ,
  host: System.get_env("BACEN_MQ_HOST", "localhost"),
  port: System.get_env("BACEN_MQ_PORT", "1414") |> String.to_integer(),
  channel: System.get_env("BACEN_MQ_CHANNEL", "BACEN.SVRCONN"),
  queue_manager: System.get_env("BACEN_MQ_QUEUE_MANAGER", "QM1"),
  inbound_queue: System.get_env("BACEN_MQ_INBOUND", "BACEN.IN"),
  outbound_queue: System.get_env("BACEN_MQ_OUTBOUND", "BACEN.OUT"),
  ssl_cipher_spec: "TLS_AES_256_GCM_SHA384",
  ssl_key_repository: System.get_env("MQ_SSL_KEY_REPO")

config :bacen_gateway, BacenGateway.Institution,
  ispb: System.fetch_env!("BACEN_ISPB"),
  institution_name: System.get_env("INSTITUTION_NAME", "FluxiQ Dev"),
  mode: System.get_env("BACEN_MODE", "simulator")

config :bacen_gateway, BacenGateway.Schema,
  xsd_path: Path.join(:code.priv_dir(:bacen_gateway), "xsd"),
  catalog_version: System.get_env("BACEN_CATALOG_VERSION", "042")

Message Lifecycle

Outbound (Sending to BACEN)

elixir
defmodule BacenGateway.Outbound do
  @doc "Sends a message to BACEN through the full outbound pipeline"

  def send_message(system, message_type, payload) do
    with {:ok, nuop} <- generate_nuop(),
         {:ok, xml} <- build_xml(system, message_type, payload, nuop),
         {:ok, validated} <- validate_schema(xml, message_type),
         {:ok, signed} <- sign_message(validated),
         {:ok, _ref} <- enqueue_mq(signed),
         {:ok, _} <- register_pending(nuop, message_type) do
      {:ok, %{nuop: nuop, status: :sent}}
    end
  end

  defp generate_nuop do
    # NUOp format: ISPB + Date + Sequential
    # Example: 12345678202601150001
    seq = :counters.get(counter_ref(), 1)
    :counters.add(counter_ref(), 1, 1)
    date = Date.utc_today() |> Date.to_iso8601(:basic)
    {:ok, "#{@ispb}#{date}#{String.pad_leading("#{seq}", 4, "0")}"}
  end
end

Inbound (Receiving from BACEN)

elixir
defmodule BacenGateway.Inbound do
  use Broadway

  def start_link(_opts) do
    Broadway.start_link(__MODULE__,
      name: __MODULE__,
      producer: [
        module: {BacenGateway.MQProducer,
          queue: @inbound_queue,
          poll_interval: 100
        },
        concurrency: 1
      ],
      processors: [
        default: [concurrency: 20, max_demand: 1]
      ]
    )
  end

  @impl true
  def handle_message(_, %Broadway.Message{data: xml} = message, _) do
    with {:ok, parsed} <- parse_xml(xml),
         {:ok, verified} <- verify_signature(parsed),
         {:ok, validated} <- validate_schema(verified),
         {:ok, routed} <- route_message(validated) do
      message
    else
      {:error, reason} ->
        Broadway.Message.failed(message, reason)
    end
  end
end

Message Building

The gateway uses XSD-driven XML builders for each message type:

elixir
defmodule BacenGateway.XML.Builder do
  @doc "Builds a BACEN XML message from a structured map"

  def build(:STR, "STR0001", params) do
    xml_document do
      element :BCMSG do
        element :IdentdEmissor, params.ispb
        element :IdentdDesworko, "99999999"
        element :NUOp, params.nuop
        element :DtHrMsg, DateTime.utc_now() |> format_bacen_datetime()
      end

      element :SISMSG do
        element :STR0001 do
          element :CodMsg, "STR0001"
          element :NumCtrlIF, params.control_number
          element :ISPBIF, params.sender_ispb
          element :ISPBFav, params.receiver_ispb
          element :Valor, Decimal.to_string(params.amount)
          element :DtMovto, Date.to_iso8601(params.date)
          element :FinlddTrsf, params.purpose_code
        end
      end
    end
  end
end

Response Correlation

When a message is sent to BACEN, the gateway tracks its NUOp and correlates R1, R2, and R3 responses:

elixir
defmodule BacenGateway.ResponseTracker do
  use GenServer

  @doc """
  Tracks outbound messages and correlates responses.

  State machine per message:
    :sent -> :r1_received -> :r2_received -> :r3_received
    :sent -> :r1_received -> :r2_rejected
    :sent -> :timeout
  """

  def track(nuop, message_type) do
    GenServer.call(__MODULE__, {:track, nuop, message_type})
  end

  def correlate_response(nuop, response_type, payload) do
    GenServer.cast(__MODULE__, {:response, nuop, response_type, payload})
  end
end

Schema Validation

All messages are validated against BACEN's official XSD schemas:

elixir
defmodule BacenGateway.Schema.Validator do
  @doc "Validates XML against the corresponding BACEN XSD schema"

  def validate(xml, message_type) do
    xsd_path = Path.join(@xsd_base, "#{message_type}.xsd")

    case :xmerl_xsd.process_schema(String.to_charlist(xsd_path)) do
      {:ok, schema} ->
        case :xmerl_xsd.validate(xml, schema) do
          {:ok, _} -> {:ok, xml}
          {:error, reason} -> {:error, {:validation_failed, reason}}
        end

      {:error, reason} ->
        {:error, {:schema_load_failed, reason}}
    end
  end
end

BACEN Simulator

When BACEN_MODE=simulator, the gateway routes messages to the built-in simulator instead of IBM MQ:

elixir
defmodule BacenGateway.Simulator do
  @moduledoc """
  Simulates BACEN/RSFN behavior for development and testing.

  Features:
  - Message validation against official XSD schemas
  - Configurable response delays (simulating network latency)
  - Settlement window simulation with configurable schedules
  - Error injection for testing failure scenarios
  - R1/R2/R3 response generation with realistic payloads
  """

  def handle_message(xml) do
    with {:ok, parsed} <- parse(xml),
         {:ok, _} <- validate(parsed),
         {:ok, r1} <- generate_r1(parsed),
         {:ok, r2} <- schedule_r2(parsed),
         {:ok, r3} <- schedule_r3(parsed) do
      {:ok, %{r1: r1, r2_scheduled: r2, r3_scheduled: r3}}
    end
  end
end

The simulator dashboard is accessible at http://localhost:4020 and provides:

  • Real-time message flow visualization
  • Settlement window controls (open/close/force-settle)
  • Error injection panel (reject messages, simulate timeouts)
  • Message history with full XML inspection

Health Check

bash
curl http://localhost:4002/health
# {
#   "status": "ok",
#   "mode": "simulator",
#   "mq_connection": "connected",
#   "pending_messages": 3,
#   "schema_version": "042"
# }

Metrics

  • bacen_messages_sent_total - Outbound messages by system and type
  • bacen_messages_received_total - Inbound messages by system and type
  • bacen_message_processing_duration_seconds - Processing time histogram
  • bacen_response_correlation_duration_seconds - Time between send and R2/R3
  • bacen_mq_connection_status - MQ connection health gauge
  • bacen_schema_validation_errors_total - Schema validation failures

Plataforma de Integracao BACEN/SPB