This commit is contained in:
2026-06-17 01:57:49 +08:00
parent d3a62b31a2
commit 4d319c8138
3 changed files with 206 additions and 113 deletions

View File

@@ -1,25 +1,23 @@
# WalletMan 后端对接文档
> 前端对接见 [walletman前端对接.md](./walletman前端对接.md)
Android 宿主通过 **HTTP + WebSocket + FCM** 与 walletman 服务端通信。服务端默认 `:16000`
> 前端集成见 [walletman前端对接.md](./walletman前端对接.md)
---
## 1. 架构
```
Android 宿主 ◄── HTTP/WS ──► walletman :16000 ◄── 代理 TCP ──► 钱包 API
◄── FCM data-only ──► Firebase
Android 宿主 ◄── HTTP / WS ──► walletman (:16000 或反代) ◄── 代理 TCP ──► 钱包 API
◄── FCM data ──► Firebase
```
| 链路 | 用途 |
|------|------|
| HTTP REST | 登录、绑钱包、业务 |
| HTTP REST | 登录、绑钱包、查流水等业务 |
| WebSocket `/ws` | 设备注册、TCP 代理、心跳 |
| FCM | WS 断线拉活、token 过期重绑 |
| FCM data | WS 断线拉活、token 过期重绑 |
**clientId** 设备唯一 IDWS 注册 key + FCM 目标 key与 App `DeviceInfo.getUniqueIdSync()` 一致。
**clientId**设备唯一 IDWS 注册 key + FCM 目标 key与 App `DeviceInfo.getUniqueIdSync()` 一致。
---
@@ -30,21 +28,23 @@ cd servers/walletman/cmd/server
go run . -tls android
```
测试页:`http://localhost:16000/test/index.html`
- 默认监听 `:16000`
- 测试页:`http://localhost:16000/test/index.html`
- 钱包持久化:`cmd/wallets.json`
**FCM 服务账号**
**FCM 服务账号**(二选一):
```
servers/walletman/cmd/server/config/fcm-service-account.json
```
`FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App `google-services.json` **同一 Firebase 项目**
环境变量 `FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App `google-services.json` **同一 Firebase 项目**
---
## 3. 通用约定
**响应:**
**响应格式**
```json
{ "success": true, "message": "说明", "data": {} }
@@ -72,7 +72,7 @@ X-User-ID: 10000
### POST `/register`
Token 绑钱包。
Token 模式绑钱包 / 静默重绑落库
```json
{
@@ -81,7 +81,7 @@ Token 绑钱包。
"type": "paytm",
"token": "...",
"refreshToken": "...",
"mobile": "9xxxxxxxxx",
"mobile": "9876543210",
"deviceId": "...",
"chType": "ipay",
"chVersion": "3"
@@ -91,16 +91,30 @@ Token 绑钱包。
```json
// data
{ "walletId": "paytm_9xxxxxxxxx", "walletType": "paytm" }
{ "walletId": "paytm_9876543210", "walletType": "paytm" }
```
`walletId` = `{walletType}_{phone}`
常用 `walletType``paytm` | `phonepe` | `mobikwik` | `freecharge` | `paytm business` | `phonepe business`
**支持的 walletType**(见 `walletman.GetAllWalletTypes()`
| walletType | 说明 |
|------------|------|
| `paytm` | Paytm Personalipay AIDL |
| `phonepe` | PhonePe Personalipay AIDL |
| `mobikwik` | Mobikwik Personal |
| `freecharge` | Freecharge Personal |
| `amazonpay` | Amazon Pay Personal |
| `paytm business` | Paytm Business |
| `phonepe business` | PhonePe Business |
| `googlepay business` | Google Pay Business |
| `bharatpe business` | BharatPe Business |
各类型 `params` 字段以对应 `*_personal.go` / `*_business.go` 及前端 Bind 组件为准。
### POST `/request-otp` / POST `/verify-otp`
OTP 绑钱包,见 `http_handler.go`
OTP 绑钱包。重绑场景若钱包已存在,会复用已有 params保留 deviceId 等),见 `requestOTPHTTP`
### GET `/wallets`
@@ -117,7 +131,7 @@ OTP 绑钱包,见 `http_handler.go`。
}
```
`inactive` + 非 OTP → 可能触发 FCM 重绑(`maybeNotifyWalletRebind`)。
`status=inactive` `otpMode=false` 时,可能触发 FCM 重绑(见 §6)。
### POST `/fcm/register`
@@ -125,15 +139,18 @@ OTP 绑钱包,见 `http_handler.go`。
{ "clientId": "设备ID", "fcmToken": "..." }
```
与 WS `register.data.fcmToken` 等价,均 `storeFcmToken`
与 WS `register.data.fcmToken` 等价,均写入 FCM token 存储
### POST `/fcm/send-wake`
```json
{ "clientId": "xxx" }
// 或 { "userId": 10000 }
// 或
{ "userId": 10000 }
```
下发 `wake_proxy`,客户端重连 WS。
### POST `/fcm/send-rebind`
```json
@@ -146,21 +163,29 @@ OTP 绑钱包,见 `http_handler.go`。
}
```
`clientId` 可省略,按 wallet owner 查 `GetFcmClientID`
| 字段 | 说明 |
|------|------|
| `clientId` | 可省略,按 wallet owner 查 `GetFcmClientID` |
| `retry` | 传给客户端;**phonepe 类型服务端会强制 `retry=true`** |
| `notify` | `true` 时在 data 中带 `msg` 文案;客户端 AIDL 失败才本地弹通知 |
### GET `/fcm/clients`
已注册 FCM 的 clientId 列表。
### POST `/wallet/set-status`
手动改钱包状态;设为 inactive 时会 `maybeNotifyWalletRebind`
---
## 5. WebSocket `/ws`
URL`ws://host:16000/ws``wss://...`
URL`ws://host:16000/ws``wss://host/ws`(反代)
### 客户端 → 服务端
**register即发)**
**register接后立即发**
```json
{
@@ -171,7 +196,7 @@ URL`ws://host:16000/ws` 或 `wss://...`
}
```
- `clientId → Client`
- 绑定 `clientId → Client`
- `userManager.SetProxy(userId, clientId, ...)`
- 同 clientId 新连接踢旧连接
@@ -186,8 +211,8 @@ URL`ws://host:16000/ws` 或 `wss://...`
| type | 说明 |
|------|------|
| proxyReady | TCP 已连 host:port |
| proxyData | data.data = base64 |
| proxyClose | 结束 |
| proxyData | `data.data` = base64 |
| proxyClose | 结束本次代理 |
### 服务端 → 客户端
@@ -201,7 +226,7 @@ URL`ws://host:16000/ws` 或 `wss://...`
}
```
443 端口服务端侧做 TLS再 HTTP/2 转发。
443 端口服务端侧做 TLS再 HTTP/2 转发。
### 服务端行为
@@ -210,21 +235,21 @@ URL`ws://host:16000/ws` 或 `wss://...`
| WS 断开 | `cleanupClient``sendFcmWake(clientId)` |
| 45s 无活动 | 心跳超时清理 + wake |
| 代理时 client 离线 | `requestProxy` 失败 → wake |
| 钱包 API 请求 | `proxyRoundTripper``requestProxy` |
| 钱包 API 出网 | `proxyRoundTripper``requestProxy` |
代码:`websocket_handler.go``proxy.go``fcm.go`
代码入口`websocket_handler.go``proxy.go``fcm.go`
---
## 6. FCM 下发data-only
## 6. FCM 下发
重绑 **不发 notification 块**,避免绕过 App 静默逻辑
重绑 **data-only**(不发 FCM `notification` 块),文案放在 `data.msg`,由客户端决定何时弹本地通知
| data key | 说明 |
|----------|------|
| msg | 文案(客户端 AIDL 失败才本地弹 |
| cmd | `wake_proxy` \| `rebind_wallet` |
| params | JSON 字符串 |
| `msg` | 通知文案(`notify=true` 时有值 |
| `cmd` | `wake_proxy` \| `rebind_wallet` |
| `params` | JSON 字符串 |
### wake_proxy
@@ -238,15 +263,26 @@ URL`ws://host:16000/ws` 或 `wss://...`
{
"cmd": "rebind_wallet",
"msg": "Paytm 已过期,点我自动重绑",
"params": "{\"walletId\":\"paytm_xxx\",\"walletType\":\"paytm\",\"phone\":\"9xxx\",\"retry\":false,\"tryGtkAnyway\":false}"
"params": "{\"walletId\":\"paytm_9876543210\",\"walletType\":\"paytm\",\"phone\":\"9876543210\",\"retry\":false,\"tryGtkAnyway\":false}"
}
```
PhonePe 默认 `retry=true`
**params 字段:**
| 字段 | 说明 |
|------|------|
| `walletId` | 钱包 ID |
| `walletType` | 钱包类型 |
| `phone` | 手机号 |
| `retry` | PhonePe 是否走 gtk 重试phonepe 类型服务端默认 true |
| `tryGtkAnyway` | 客户端扩展,当前服务端固定 false |
### 自动触发
`wallet.GetStatus() == inactive``!GetOTPMode()``sendFcmRebind(..., notify=true)`
1. `POST /wallet/set-status` 设为 inactive → `maybeNotifyWalletRebind(..., retry=false)`
2. 拉流水后 token 失效变 inactive → `persistWalletIfChanged` → phonepe 且 inactiveCount>1 时 `retry=true`
条件:`GetOTPMode()==false``GetStatus()==inactive`
### 发送实现
@@ -254,7 +290,7 @@ PhonePe 默认 `retry=true`。
---
## 7. 时序(服务端视角)
## 7. 时序
**代理请求**
@@ -277,33 +313,26 @@ WS cleanupClient
**重绑**
```
inactive 检测 / 手动 /fcm/send-rebind
→ FCM rebind_wallet
→ 客户端静默 AIDL → POST /register(宿主实现)
→ 失败则客户端弹通知,用户点击后走前端 handler
inactive 检测 / POST /fcm/send-rebind
→ FCM rebind_walletdata.msg + params
→ 客户端静默 AIDL → registerWallet → POST /register
→ 失败 → 客户端本地通知 → 用户点击 → JS handler 打开 Bind
```
---
## 8. 调试
```bash
# 服务端日志
go run . -tls android
# 测试页
http://localhost:16000/test/index.html
```
| 接口 | 用途 |
| 方式 | 说明 |
|------|------|
| GET /fcm/clients | 看已注册设备 |
| POST /fcm/send-wake | 手动拉活 |
| POST /fcm/send-rebind | 手动重绑 |
| `http://localhost:16000/test/index.html` | 发 wake / rebind、看 WS |
| 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 项目是否一致 |
| no FCM token for clientId | App 未 POST `/fcm/register` WS 未带 fcmToken |
| Client not connected | WS 未连;发 wake |
| 重绑发了客户端无反应 | Firebase 项目是否与 App 一致logcat `ProxyFcmService` |