Files
rnpay/docs/walletman后端对接.md
2026-06-16 11:29:27 +08:00

310 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WalletMan 后端对接文档
> 前端对接见 [walletman前端对接.md](./walletman前端对接.md)
Android 宿主通过 **HTTP + WebSocket + FCM** 与 walletman 服务端通信。服务端默认 `:16000`
---
## 1. 架构
```
Android 宿主 ◄── HTTP/WS ──► walletman :16000 ◄── 代理 TCP ──► 钱包 API
◄── FCM data-only ──► Firebase
```
| 链路 | 用途 |
|------|------|
| HTTP REST | 登录、绑钱包、业务 |
| WebSocket `/ws` | 设备注册、TCP 代理、心跳 |
| FCM | WS 断线拉活、token 过期重绑 |
**clientId** 设备唯一 IDWS 注册 key + FCM 目标 key需与 App 侧 `DeviceInfo.getUniqueIdSync()` 一致。
---
## 2. 启动与配置
```bash
cd servers/walletman/cmd/server
go run . -tls android
```
测试页:`http://localhost:16000/test/index.html`
**FCM 服务账号:**
```
servers/walletman/cmd/server/config/fcm-service-account.json
```
`FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App `google-services.json` **同一 Firebase 项目**
---
## 3. 通用约定
**响应:**
```json
{ "success": true, "message": "说明", "data": {} }
```
**需登录接口请求头:**
```
Content-Type: application/json
X-User-ID: 10000
```
---
## 4. HTTP 接口
### POST `/login`
```json
// req
{ "username": "test123", "password": "123456" }
// data
{ "userId": 10000, "userToken": "10000" }
```
### POST `/register`
Token 绑钱包。
```json
{
"walletType": "paytm",
"params": {
"type": "paytm",
"token": "...",
"refreshToken": "...",
"mobile": "9xxxxxxxxx",
"deviceId": "...",
"chType": "ipay",
"chVersion": "3"
}
}
```
```json
// data
{ "walletId": "paytm_9xxxxxxxxx", "walletType": "paytm" }
```
`walletId` = `{walletType}_{phone}`
常用 `walletType``paytm` | `phonepe` | `mobikwik` | `freecharge` | `paytm business` | `phonepe business`
### POST `/request-otp` / POST `/verify-otp`
OTP 绑钱包,见 `http_handler.go`
### GET `/wallets`
```json
{
"wallets": [{
"id": "paytm_9876543210",
"walletType": "paytm",
"phone": "9876543210",
"upi": "xxx@paytm",
"status": "active",
"otpMode": false
}]
}
```
`inactive` + 非 OTP → 可能触发 FCM 重绑(`maybeNotifyWalletRebind`)。
### POST `/fcm/register`
```json
{ "clientId": "设备ID", "fcmToken": "..." }
```
与 WS `register.data.fcmToken` 等价,均 `storeFcmToken`
### POST `/fcm/send-wake`
```json
{ "clientId": "xxx" }
// 或 { "userId": 10000 }
```
### POST `/fcm/send-rebind`
```json
{
"walletId": "paytm_9876543210",
"walletType": "paytm",
"phone": "9876543210",
"retry": false,
"notify": true
}
```
`clientId` 可省略,按 wallet owner 查 `GetFcmClientID`
### GET `/fcm/clients`
已注册 FCM 的 clientId 列表。
---
## 5. WebSocket `/ws`
URL`ws://host:16000/ws``wss://...`
### 客户端 → 服务端
**register连上即发**
```json
{
"type": "register",
"messageId": "register_1700000000000",
"clientId": "设备ID",
"data": { "userId": 10000, "fcmToken": "可选" }
}
```
-`clientId → Client`
- `userManager.SetProxy(userId, clientId, ...)`
- 同 clientId 新连接踢旧连接
**ping建议 10s**
```json
{ "type": "ping", "messageId": "ping_xxx" }
```
**proxyReady / proxyData / proxyClose**
| type | 说明 |
|------|------|
| proxyReady | TCP 已连 host:port |
| proxyData | data.data = base64 |
| proxyClose | 结束 |
### 服务端 → 客户端
**proxyRequest**
```json
{
"type": "proxyRequest",
"messageId": "req_1700000000000",
"data": { "host": "api.phonepe.com", "port": 443 }
}
```
443 端口服务端侧做 TLS再 HTTP/2 转发。
### 服务端行为
| 事件 | 行为 |
|------|------|
| WS 断开 | `cleanupClient``sendFcmWake(clientId)` |
| 45s 无活动 | 心跳超时清理 + wake |
| 代理时 client 离线 | `requestProxy` 失败 → wake |
| 钱包 API 请求 | `proxyRoundTripper``requestProxy` |
代码:`websocket_handler.go``proxy.go``fcm.go`
---
## 6. FCM 下发data-only
重绑 **不发 notification 块**,避免绕过 App 静默逻辑。
| data key | 说明 |
|----------|------|
| msg | 文案(客户端 AIDL 失败才本地弹) |
| cmd | `wake_proxy` \| `rebind_wallet` |
| params | JSON 字符串 |
### wake_proxy
```json
{ "cmd": "wake_proxy", "params": "{}" }
```
### rebind_wallet
```json
{
"cmd": "rebind_wallet",
"msg": "Paytm 已过期,点我自动重绑",
"params": "{\"walletId\":\"paytm_xxx\",\"walletType\":\"paytm\",\"phone\":\"9xxx\",\"retry\":false,\"tryGtkAnyway\":false}"
}
```
PhonePe 默认 `retry=true`
### 自动触发
`wallet.GetStatus() == inactive``!GetOTPMode()``sendFcmRebind(..., notify=true)`
### 发送实现
`sendFcmToClient` → FCM HTTP v1 `projects/{id}/messages:send`
---
## 7. 时序(服务端视角)
**代理请求**
```
walletman HTTP 出网
→ GetProxyClientID(userId)
→ requestProxy(clientId, host, 443)
→ WS proxyRequest → 等 proxyReady
→ 双向 proxyData
```
**断线拉活**
```
WS cleanupClient
→ sendFcmWake(clientId)
→ 客户端 wakeFromPrefs → 重连 WS register
```
**重绑**
```
inactive 检测 / 手动 /fcm/send-rebind
→ FCM rebind_wallet
→ 客户端静默 AIDL → POST /register宿主实现
→ 失败则客户端弹通知,用户点击后走前端 handler
```
---
## 8. 调试
```bash
# 服务端日志
go run . -tls android
# 测试页
http://localhost:16000/test/index.html
```
| 接口 | 用途 |
|------|------|
| GET /fcm/clients | 看已注册设备 |
| POST /fcm/send-wake | 手动拉活 |
| POST /fcm/send-rebind | 手动重绑 |
| 现象 | 排查 |
|------|------|
| FCM send failed | 服务账号路径、project_id |
| no FCM token for clientId | App 未 POST /fcm/register 或 WS 未带 fcmToken |
| Client not connected | WS 未连;发 wake 后重试 |
| 重绑发了客户端无反应 | 查 FCM send ok 日志Firebase 项目是否一致 |