# merchant-gateway 商户网关服务

## 服务职责

merchant-gateway 是商户对接统一入口，负责高并发商户 API 的接入层能力。

职责包括：

- 商户签名校验。
- API Key / Secret 校验。
- IP 白名单。
- 请求限流。
- 幂等预检。
- 请求参数标准化。
- 将订单请求写入 order-ingest-service。
- 提供余额查询、注单查询、回调补推等商户接口入口。

## 不负责的边界

- 不直接修改玩家余额。
- 不直接写账本。
- 不直接生成游戏结果。
- 不直接写 ClickHouse 统计表。
- 不绕过 order-ingest-service 同步写 PostgreSQL 创建订单。

## 上游调用方

- 商户系统。

## 下游依赖

- order-ingest-service
- wallet-service
- bet-order-service
- callback-service
- idempotency-service 或 Redis 幂等组件

## 技术约束

- HTTP 框架必须使用 CloudWeGo Hertz。
- 内部调用必须使用 Kitex 或明确的内部契约。
- 高并发下单请求必须进入 Redpanda 顺序事件日志。

## 命令

```bash
# 本地运行
go run ./cmd/merchant-gateway

# 测试
go test ./...

# 构建镜像
docker build -t registry.local/merchant-gateway:v1 .

# 部署
kubectl apply -f deploy/k8s/deployment.yaml

# 查看日志
kubectl logs -n game-platform deploy/merchant-gateway --tail=200 -f
```

## 当前代码目录

```text
平台服务/merchant-gateway
├── cmd/merchant-gateway          进程入口
├── api/openapi                   商户 HTTP API 契约
├── configs                       配置模板，不存生产密钥
├── internal/bootstrap            服务启动组装
├── internal/config               环境变量读取
├── internal/domain               领域错误和后续状态机
├── internal/handler/http         Hertz 路由和请求响应适配
├── internal/middleware           HMAC 签名等中间件
├── internal/model                请求、响应、玩家、交易、会话模型
├── internal/repository/postgres  PostgreSQL 持久化实现
├── internal/service              业务接口和本地内存实现
└── migrations                    PostgreSQL 迁移脚本
```

说明：

- `cmd/merchant-gateway/main.go` 只负责调用 `internal/bootstrap`。
- HTTP handler 不直接访问 PostgreSQL。
- PostgreSQL 建表 SQL 不允许写死在 Go 启动代码中，必须放在 `migrations`。
- 当前 `api/openapi/merchant-gateway.openapi.yaml` 是第一阶段接口契约初稿，后续每次接口变更必须同步更新。

## 第一阶段已实现接口

当前第一阶段先实现商户 API 契约和可运行链路。服务支持两种存储模式：

- 未配置 `DATABASE_URL`：使用进程内内存，适合本地测试。
- 已配置 `DATABASE_URL`：使用 PostgreSQL，适合测试服和第一阶段单机部署。

当前 PostgreSQL 版用于持久化玩家、余额、交易记录和登录 session。最终高并发正式版本还需要继续拆到 wallet-service、ledger-service、order-ingest-service、Redis 幂等和 Redpanda 事件流。

### 存储环境变量

```bash
DATABASE_URL='postgres://jiekouapi:密码@127.0.0.1:5432/jiekouapi?sslmode=disable'
GAME_BASE_URL='https://game.amssss.com/'
MERCHANT_API_KEY='demo-key'
MERCHANT_API_SECRET='demo-secret'
MERCHANT_MIGRATIONS_DIR='migrations'
```

服务配置 `DATABASE_URL` 后，会读取 `MERCHANT_MIGRATIONS_DIR/0001_merchant_gateway_core.sql` 执行第一阶段 PostgreSQL 迁移。默认迁移目录是 `migrations`。

### 签名规则

除 `/health` 和 `/ready` 外，商户接口默认要求 HMAC-SHA256 签名。

请求头：

```text
X-API-Key: demo-key
X-Timestamp: 2026-06-06T08:00:00Z
X-Signature: hex(hmac_sha256(secret, timestamp + "." + raw_body))
X-Request-Id: 可选请求ID
```

默认测试密钥：

```text
API Key: demo-key
Secret: demo-secret
```

生产环境必须通过环境变量替换：

```bash
MERCHANT_API_KEY=正式商户key
MERCHANT_API_SECRET=正式商户secret
```

### 统一响应

```json
{
  "code": "OK",
  "message": "success",
  "request_id": "req-001",
  "data": {}
}
```

### CreateMember 创建玩家

```bash
BODY='{"merchant_id":"DEMO","player_id":"player_001","nickname":"测试玩家","currency":"CNY"}'
TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
SIG="$(printf '%s.%s' "$TS" "$BODY" | openssl dgst -sha256 -hmac 'demo-secret' -binary | xxd -p -c 256)"
curl -sS https://api.amssss.com/CreateMember \
  -H "Content-Type: application/json" \
  -H "X-API-Key: demo-key" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -d "$BODY"
```

### Login 登录并返回游戏入口

```json
{
  "merchant_id": "DEMO",
  "player_id": "player_001",
  "game_code": "demo-slots",
  "language": "zh-CN",
  "return_url": "https://merchant.example.com"
}
```

返回字段包含：

```text
token
game_url
expires_at
```

注意：`game_url` 当前指向 `https://game.amssss.com/` 占位域名。正式采集游戏前端部署后，需要按真实游戏前端入口改为兼容 URL。

### Deposit 上分

金额单位使用最小货币单位，例如分。

```json
{
  "merchant_id": "DEMO",
  "player_id": "player_001",
  "txn_id": "deposit_001",
  "amount": 10000,
  "currency": "CNY",
  "remark": "测试上分"
}
```

`txn_id` 在同一商户下必须幂等。相同 `txn_id`、相同玩家、相同金额重复请求会返回第一次成功记录。

### Withdraw 下分

```json
{
  "merchant_id": "DEMO",
  "player_id": "player_001",
  "txn_id": "withdraw_001",
  "amount": 1000,
  "currency": "CNY",
  "remark": "测试下分"
}
```

余额不足返回：

```json
{
  "code": "WALLET_INSUFFICIENT_BALANCE",
  "message": "insufficient balance"
}
```

### Balance 查询余额

```json
{
  "merchant_id": "DEMO",
  "player_id": "player_001"
}
```

### BetRecord 查询记录

```json
{
  "merchant_id": "DEMO",
  "player_id": "player_001",
  "limit": 20
}
```

当前返回 Deposit/Withdraw 记录。后续接入下注、派彩、冲正后，必须改为读取 bet-order-service 和 ledger-service 的事实记录。

## 第一阶段 PostgreSQL 表

第一阶段迁移文件：

```text
平台服务/merchant-gateway/migrations/0001_merchant_gateway_core.sql
```

该迁移创建以下表：

```text
merchant_players      玩家基础信息
merchant_wallets      玩家余额
merchant_transfers    上分、下分和后续交易记录
merchant_sessions     登录 token 和游戏入口
```

关键约束：

- `merchant_players(merchant_id, player_id)` 为主键。
- `merchant_wallets(merchant_id, player_id)` 为主键。
- `merchant_transfers(merchant_id, txn_id)` 为主键，用于商户交易幂等。
- 转账时会对 `merchant_wallets` 使用 `for update` 锁定玩家余额行，避免并发扣款错账。
