Files
rnpay/docs/walletman后端对接.md
2026-06-17 01:57:49 +08:00

339 lines
7.4 KiB
Markdown
Raw Permalink 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)
---
## 1. 架构
```
Android 宿主 ◄── HTTP / WS ──► walletman (:16000 或反代) ◄── 代理 TCP ──► 钱包 API
◄── FCM data ──► Firebase
```
| 链路 | 用途 |
|------|------|
| HTTP REST | 登录、绑钱包、查流水等业务 |
| WebSocket `/ws` | 设备注册、TCP 代理、心跳 |
| FCM data | WS 断线拉活、token 过期重绑 |
**clientId**:设备唯一 IDWS 注册 key + FCM 目标 key须与 App `DeviceInfo.getUniqueIdSync()` 一致。
---
## 2. 启动与配置
```bash
cd servers/walletman/cmd/server
go run . -tls android
```
- 默认监听 `:16000`
- 测试页:`http://localhost:16000/test/index.html`
- 钱包持久化:`cmd/wallets.json`
**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": "9876543210",
"deviceId": "...",
"chType": "ipay",
"chVersion": "3"
}
}
```
```json
// data
{ "walletId": "paytm_9876543210", "walletType": "paytm" }
```
`walletId` = `{walletType}_{phone}`
**支持的 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 绑钱包。重绑场景若钱包已存在,会复用已有 params保留 deviceId 等),见 `requestOTPHTTP`
### GET `/wallets`
```json
{
"wallets": [{
"id": "paytm_9876543210",
"walletType": "paytm",
"phone": "9876543210",
"upi": "xxx@paytm",
"status": "active",
"otpMode": false
}]
}
```
`status=inactive``otpMode=false` 时,可能触发 FCM 重绑(见 §6
### POST `/fcm/register`
```json
{ "clientId": "设备ID", "fcmToken": "..." }
```
与 WS `register.data.fcmToken` 等价,均写入 FCM token 存储。
### POST `/fcm/send-wake`
```json
{ "clientId": "xxx" }
// 或
{ "userId": 10000 }
```
下发 `wake_proxy`,客户端重连 WS。
### POST `/fcm/send-rebind`
```json
{
"walletId": "paytm_9876543210",
"walletType": "paytm",
"phone": "9876543210",
"retry": false,
"notify": true
}
```
| 字段 | 说明 |
|------|------|
| `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://host/ws`(反代)
### 客户端 → 服务端
**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**(不发 FCM `notification` 块),文案放在 `data.msg`,由客户端决定何时弹本地通知。
| data key | 说明 |
|----------|------|
| `msg` | 通知文案(`notify=true` 时有值) |
| `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_9876543210\",\"walletType\":\"paytm\",\"phone\":\"9876543210\",\"retry\":false,\"tryGtkAnyway\":false}"
}
```
**params 字段:**
| 字段 | 说明 |
|------|------|
| `walletId` | 钱包 ID |
| `walletType` | 钱包类型 |
| `phone` | 手机号 |
| `retry` | PhonePe 是否走 gtk 重试phonepe 类型服务端默认 true |
| `tryGtkAnyway` | 客户端扩展,当前服务端固定 false |
### 自动触发
1. `POST /wallet/set-status` 设为 inactive → `maybeNotifyWalletRebind(..., retry=false)`
2. 拉流水后 token 失效变 inactive → `persistWalletIfChanged` → phonepe 且 inactiveCount>1 时 `retry=true`
条件:`GetOTPMode()==false``GetStatus()==inactive`
### 发送实现
`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 检测 / POST /fcm/send-rebind
→ FCM rebind_walletdata.msg + params
→ 客户端静默 AIDL → registerWallet → POST /register
→ 失败 → 客户端本地通知 → 用户点击 → JS handler 打开 Bind
```
---
## 8. 调试
| 方式 | 说明 |
|------|------|
| `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 |
| 重绑发了客户端无反应 | Firebase 项目是否与 App 一致logcat `ProxyFcmService` |