# 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:** 设备唯一 ID,WS 注册 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 项目是否一致 |