# 环境变量

Monetarie PIX 使用的所有环境变量的完整参考，按类别组织。

## 前提条件

- 可以访问将部署 Monetarie PIX 的服务器或 Kubernetes 集群
- 有权在 `pix` 命名空间中创建/编辑 ConfigMap 和 Secret
- 了解 Docker/K8s 容器中环境变量的基本知识

## 配置优先级

Monetarie PIX 使用 Elixir/Phoenix 配置系统，优先级如下（最高优先级在前）：

```mermaid
flowchart LR
    A["环境变量<br/>(最高优先级)"] --> B["runtime.exs<br/>(运行时)"]
    B --> C["prod.exs / dev.exs<br/>(环境)"]
    C --> D["config.exs<br/>(基础)"]
```

1. **环境变量** -- 始终具有最高优先级
2. **`config/runtime.exs`** -- 在运行时执行（包括在 release 中）
3. **`config/prod.exs`** 或 **`config/dev.exs`** -- 每个环境的配置
4. **`config/config.exs`** -- 共享的基础配置

::: warning 重要
在 release 模式（生产环境）中，只有 `runtime.exs` 会被执行。在此文件中定义的环境变量是在生产环境中配置系统的唯一方式。
:::

## 数据库

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `DATABASE_URL` | -- | 否* | 完整的 PostgreSQL 连接 URL（`postgres://user:pass@host:port/db`）。如果设置，将覆盖下面的各个变量 |
| `DB_HOST` | `localhost` | 是 (生产) | PostgreSQL 服务器地址 |
| `DB_PORT` | `5432` | 否 | PostgreSQL 端口 |
| `DB_USER` | `postgres` | 是 (生产) | 数据库用户 |
| `DB_PASS` | `postgres` | 是 (生产) | 数据库密码 |
| `DB_NAME` | `monetarie` | 否 | 数据库名称 |
| `POOL_SIZE` | `250` | 否 | **每个 repository** 的连接池大小 |
| `DATABASE_SSL` | `false` | 否 | 启用数据库连接 SSL |
| `ECTO_IPV6` | `false` | 否 | 启用数据库连接 IPv6 |

::: tip POOL_SIZE
默认值 250 个连接是**每个 repository** 的。有 4 个 repository（Shared、Dict、SPI、Settlement），总计 `250 x 4 = 1000` 个连接每 pod。有 2 个 pods，即 2,000 个连接。Cloud SQL 的 `max_connections >= 2200`。
:::

::: danger 密码中的特殊字符
如果数据库密码包含特殊字符，在 `DATABASE_URL` 中使用 URL 编码：
- `/` -> `%2F`
- `=` -> `%3D`
- `@` -> `%40`
- `#` -> `%23`
:::

## Redis

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `REDIS_HOST` | `localhost` | 是 (生产) | Redis 服务器地址 |
| `REDIS_PORT` | `6379` | 否 | Redis 端口 |

## NATS JetStream

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `NATS_HOST` | `localhost` | 是 (生产) | NATS 服务器地址 |
| `NATS_PORT` | `4222` | 否 | NATS 端口 |
| `NATS_USER` | -- | 否 | NATS 认证用户 |
| `NATS_PASS` | -- | 否 | NATS 认证密码 |
| `NATS_ENABLED` | `false` | 否 | 启用 NATS JetStream workers。在生产环境中设置为 `true` |

::: warning NATS_ENABLED
NATS workers 默认禁用。在生产环境中，设置 `NATS_ENABLED=true` 以激活 SPI workers（InboundProcessor、OutboundSender、StatusUpdater、ReturnProcessor）和 CoreEventProcessor。
:::

## 认证和安全

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `SECRET_KEY_BASE` | -- | **是** | Phoenix 密钥，用于 cookies 和 sessions。使用 `mix phx.gen.secret` 生成 |
| `JWT_SECRET` | -- | 否 | JWT 签名密钥。回退链：`GUARDIAN_SECRET_KEY` -> `SECRET_KEY_BASE` |
| `GUARDIAN_SECRET_KEY` | -- | 否 | 与 Core Banking 共享的密钥（Guardian HS256）。Core 集成必需 |

JWT 密钥回退链：

```
JWT_SECRET  -->  GUARDIAN_SECRET_KEY  -->  SECRET_KEY_BASE
```

::: tip 与 Core 的本地开发
与 Core Banking 进行本地集成测试时，将 `JWT_SECRET` 设置为 `"dev-only-secret-key-not-for-production"`（Core 的开发密钥）。
:::

## BACEN（中央银行）

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `BACEN_ENV` | `homolog` | 否 | BACEN 环境：`homolog` 或 `production` |
| `BACEN_ENABLED` | `false` | 否 | 启用通过 RSFN 的真实 BACEN 集成 |
| `BACEN_ISPB` | `00000000` | 是 (生产) | 机构的 ISPB 代码（8 位数字） |
| `BACEN_PARTICIPANT_NAME` | `MONETARIE` | 否 | 参与者简称 |
| `BACEN_CNPJ` | -- | 是 (生产) | 机构的 CNPJ |
| `BACEN_CA_CERT_PATH` | -- | 是 (生产) | BACEN 根 CA 证书路径 |
| `BACEN_CLIENT_CERT_PATH` | -- | 是 (生产) | ICP-Brasil 机构证书路径 (CPIC) |
| `BACEN_CLIENT_KEY_PATH` | -- | 是 (生产) | 证书私钥路径 |
| `BACEN_CLIENT_KEY_PASSWORD` | -- | 否 | 私钥密码（如加密） |
| `BACEN_TIMEOUT` | `30000` | 否 | BACEN 连接超时（毫秒） |
| `BACEN_FAILOVER_ENABLED` | `true` | 否 | 启用 CPM/CSM 通道间自动故障转移 |
| `BACEN_FAILURE_THRESHOLD` | `3` | 否 | 标记通道为不活跃前的连续失败次数 |
| `BACEN_HEALTH_CHECK_INTERVAL` | `30` | 否 | 通道健康检查间隔（秒） |

## 模拟器

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `SIMULATOR_ENABLED` | `false` | 否 | 启用 BACEN PIX 模拟器。请勿在生产环境中使用 |

::: danger 生产环境中的模拟器
**切勿**在生产环境中启用 `SIMULATOR_ENABLED=true`。模拟器会覆盖真实的 BACEN 通信行为。
:::

## 内部服务

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `DICT_SERVICE_URL` | `http://localhost:4001` | 否 | 内部 DICT 服务 URL（CoreEventProcessor 使用） |
| `SPI_SERVICE_URL` | `http://localhost:4002` | 否 | 内部 SPI 服务 URL |
| `DICT_HOST` | `localhost` | 否 | DICT 端点主机 |
| `DICT_PORT` | `4001` | 否 | DICT 端点端口 |
| `SPI_HOST` | `localhost` | 否 | SPI 端点主机 |
| `SPI_PORT` | `4002` | 否 | SPI 端点端口 |
| `SETTLEMENT_HOST` | `localhost` | 否 | Settlement（API 网关）端点主机 |
| `SETTLEMENT_PORT` | `4003` | 否 | Settlement（API 网关）端点端口 |

## 应用

| 变量 | 默认值 | 必需 | 描述 |
|------|--------|------|------|
| `PHX_HOST` | -- | 否 | URL 生成的公共主机名 |
| `PHX_PORT` | `443` | 否 | 公共端口 (HTTPS) |
| `MIX_ENV` | `dev` | 是 (生产) | Elixir 环境：`dev`、`test` 或 `prod` |
| `RELEASE_NAME` | `monetarie_pix` | 否 | Elixir release 名称 |

## 完整的生产环境示例

```bash
# === 数据库 ===
export DB_HOST="10.140.241.2"
export DB_PORT="5432"
export DB_USER="monetarie_prod"
export DB_PASS="SecurePassword123"
export DB_NAME="monetarie"
export POOL_SIZE="250"

# === Redis ===
export REDIS_HOST="10.140.240.4"
export REDIS_PORT="6379"

# === NATS ===
export NATS_HOST="10.10.40.5"
export NATS_PORT="4222"
export NATS_ENABLED="true"

# === 认证 ===
export SECRET_KEY_BASE="$(mix phx.gen.secret)"
export GUARDIAN_SECRET_KEY="shared-key-with-core"

# === BACEN ===
export BACEN_ENV="homolog"
export BACEN_ENABLED="true"
export BACEN_ISPB="12345678"
export BACEN_CNPJ="12345678000199"
export BACEN_CA_CERT_PATH="/certs/bacen-ca.pem"
export BACEN_CLIENT_CERT_PATH="/certs/cpic.pem"
export BACEN_CLIENT_KEY_PATH="/certs/cpic-key.pem"

# === 服务 ===
export DICT_SERVICE_URL="http://localhost:4001"
export SPI_SERVICE_URL="http://localhost:4002"
```

## Kubernetes 配置 (Secrets)

在 Kubernetes 中，密钥存储在使用 **kebab-case** 键的 Secret 对象中：

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: pix
type: Opaque
stringData:
  database-url: "postgres://monetarie_prod:SecurePassword@10.140.241.2:5432/monetarie"
  secret-key-base: "your-secret-key-here"
```

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: monetarie-shared-jwt-secret
  namespace: pix
type: Opaque
stringData:
  jwt-secret: "shared-key-with-core-banking"
```

::: warning KEBAB-CASE 键名
Kubernetes Secret 键使用 **kebab-case**（`database-url`、`secret-key-base`），不是 SCREAMING_SNAKE_CASE。`backend.yaml` 会将这些映射到正确的环境变量。
:::

## 密钥管理

### 建议

1. **切勿**在版本控制的配置文件中存储密钥
2. 在 Kubernetes 环境中使用 **K8s Secrets**
3. 对于企业环境，考虑：
   - **HashiCorp Vault** 配合 Kubernetes operator
   - **GCP Secret Manager** 配合 workload identity
   - **AWS Secrets Manager** 配合 IRSA
4. 定期轮换 `SECRET_KEY_BASE` 和 `GUARDIAN_SECRET_KEY`
5. 使用强数据库密码（最少 32 个字符，字母数字 + 特殊字符）

### 配置验证

设置所有变量后，验证配置：

```bash
# 开发环境
cd backend
mix phx.server

# Kubernetes（检查 pod 日志）
kubectl logs -n pix deployment/pix-backend --tail=50

# 检查所有服务是否启动
# 查找日志中的: "=== Starting Shared Application ==="
```

## 预期结果

配置所有必需变量后：

- 后端无错误启动，日志中显示 `"=== Starting Shared Application (postgrex 0.16.x) ==="`
- 所有 4 个 HTTP 端点响应（DICT:4001、SPI:4002、Settlement:4003）
- 如果 `NATS_ENABLED=true`，SPI workers 和 CoreEventProcessor 处于活跃状态
- 如果 `BACEN_ENABLED=true`，与 BACEN 的 mTLS 连接已建立
- `GET /health` 健康检查返回状态 200
