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

@@ -6,15 +6,15 @@
--- ---
## 1. 职责 ## 1. 职责分工
| 模块 | 职责 | | 模块 | 职责 |
|------|------| |------|------|
| `ProxyBackgroundService` | 启停 Proxy、WS 状态、FCM/rebind 配置 | | `ProxyBackgroundService` | JS 层启停 Proxy、WS 状态、rebind/patch 配置、通知点击 |
| `BaseProxyService` | 前台 Service、WS、TCP 代理、FCM 静默重绑 | | `BaseProxyService` | Android 前台 Service、WS、TCP 代理、FCM 静默重绑 |
| `ProxyFcmService` | 收 FCM、拉活、通知点击 → prefs → JS | | `ProxyFcmService` | 收 FCM、拉活、静默重绑、失败弹通知点击 → prefs → JS |
| 宿主 `*ProxyService` | 实现 `registerWallet` → POST `/register` | | 宿主 `*ProxyService` | 继承 `BaseProxyService`实现 `registerWallet` → POST `/register` |
| Bind 组件 | 各钱包 token/OTP 绑定 UI | | Bind 组件 | 各钱包 token / webview / OTP 绑定 UI |
**clientId** `DeviceInfo.getUniqueIdSync()`,与后端 WS/FCM 一致。 **clientId** `DeviceInfo.getUniqueIdSync()`,与后端 WS/FCM 一致。
@@ -26,13 +26,15 @@
"rnwalletman": "file:libs/rnwalletman" "rnwalletman": "file:libs/rnwalletman"
``` ```
改 lib 后: 改 lib 后同步
```bash ```bash
rsync -a --delete libs/rnwalletman/ node_modules/rnwalletman/ rsync -a --delete libs/rnwalletman/ node_modules/rnwalletman/
``` ```
需 Firebase`google-services.json` 与服务端 FCM 同项目 配置 Firebase`google-services.json` 与服务端 FCM **同一项目**
Personal 绑钱包需安装对应 **ipay 魔改包**chType/chVersion 与 `PATCH_EXPECT` 一致)。
--- ---
@@ -59,7 +61,7 @@ BaseProxyService.setServiceClass(RnpayProxyService.class);
tools:node="remove" /> tools:node="remove" />
``` ```
`ProxyFcmService` 由 rnwalletman manifest merge宿主不用重复声明。 `ProxyFcmService` 由 rnwalletman manifest merge宿主无需重复声明。
### MainActivity ### MainActivity
@@ -79,13 +81,13 @@ BaseProxyService.setServiceClass(RnpayProxyService.class);
} }
``` ```
`launchMode="singleTask"` 推荐 推荐 `launchMode="singleTask"`
--- ---
## 4. 宿主 registerWallet ## 4. 宿主 registerWallet
FCM 静默重绑成功后 native 调用: FCM 静默重绑成功后native 调用宿主实现的 HTTP
```java ```java
@Override @Override
@@ -97,7 +99,7 @@ protected void registerWallet(Context ctx, String walletId, String walletType,
} }
``` ```
`baseUrl/userId/userToken` 来自 JS `syncRebindConfig``proxy_service_prefs` `baseUrl` / `userId` / `userToken` 来自 JS `syncRebindConfig``proxy_service_prefs`
参考:`android/app/src/main/java/com/rnpay/RnpayProxyService.java` 参考:`android/app/src/main/java/com/rnpay/RnpayProxyService.java`
@@ -139,7 +141,7 @@ async function bootstrap(userId: number, userToken: string) {
}); });
await proxyBackgroundService.start({ await proxyBackgroundService.start({
wsUrl: Api.WS_URL, wsUrl: Api.WS_URL, // 如 wss://domain/ws
clientId, clientId,
userId, userId,
heartbeatInterval: 10000, heartbeatInterval: 10000,
@@ -155,11 +157,11 @@ async function bootstrap(userId: number, userToken: string) {
function handleNotificationTap(payload: FcmNotificationTapPayload) { function handleNotificationTap(payload: FcmNotificationTapPayload) {
if (payload.cmd !== 'rebind_wallet') return; if (payload.cmd !== 'rebind_wallet') return;
const { walletId, walletType, phone } = payload.params; const { walletId, walletType, phone } = payload.params;
// 按 walletType 打开 Bind 弹窗 // 按 walletType 打开对应 Bind 弹窗(手动重绑)
} }
``` ```
App 回前台补读 pending App 回前台补读 pending 通知点击
```typescript ```typescript
AppState.addEventListener('change', (s) => { AppState.addEventListener('change', (s) => {
@@ -167,18 +169,26 @@ AppState.addEventListener('change', (s) => {
}); });
``` ```
### API ### ProxyBackgroundService API
| 方法 | 作用 | | 方法 | 作用 |
|------|------| |------|------|
| `start(config)` | 前台 Proxy + WS | | `start(config)` | 启动前台 Proxy + WS |
| `stop()` | 停止 | | `stop()` | 停止 |
| `syncRebindConfig(config \| null)` | native 重绑 HTTP 配置 | | `syncRebindConfig(config \| null)` | 写入 native 重绑 HTTP 配置 |
| `syncPatchConfig(config)` | ipay chType/chVersion 校验 | | `syncPatchConfig(config)` | ipay chType/chVersion 校验FCM 重绑共用) |
| `setNotificationTapHandler(fn \| null)` | 通知点击回调 | | `setNotificationTapHandler(fn \| null)` | 通知点击回调 |
| `syncPendingNotificationTap()` | 消费 prefs pending | | `syncPendingNotificationTap()` | 消费 prefs pending 点击 |
### Native 事件 ### 其他导出
| 导出 | 作用 |
|------|------|
| `proxySendMessage` / `onProxyMessage` | WS 自定义消息(`ProxyBridge` |
| `startActivity` / `IntentFlags` | 拉起第三方 App deeplink |
| `openPaytmPayToBank` 等 | 银行转账 deeplink 封装 |
### Native → JS 事件
| 事件 | data | | 事件 | data |
|------|------| |------|------|
@@ -187,22 +197,37 @@ AppState.addEventListener('change', (s) => {
| ProxyServiceRebound | walletId | | ProxyServiceRebound | walletId |
| ProxyServiceNotificationTap | JSON `{cmd, params}` | | ProxyServiceNotificationTap | JSON `{cmd, params}` |
| ProxyServiceCustomCommand | WS 自定义消息 | | ProxyServiceCustomCommand | WS 自定义消息 |
| ProxyServiceFcmToken | FCM token 刷新 |
--- ---
## 6. 绑钱包 ## 6. 绑钱包
**Token 模式** ### Token 模式Personalipay AIDL
1. 打开 `PaytmPersonalBind` / `PhonePePersonalBind` | 组件 | walletType |
2. 校验 `chType/chVersion` === `PATCH_EXPECT` |------|------------|
3. `POST /register` | `PaytmPersonalBind` | `paytm` |
| `PhonePePersonalBind` | `phonepe`(需传 `userToken` |
| `MobikwikPersonalBind` | `mobikwik`(需传 `userToken` |
| `FreechargePersonalBind` | `freecharge` |
**OTP 模式** 流程:打开 Bind 组件 → native AIDL 取 token → 校验 `chType/chVersion` === `PATCH_EXPECT``POST /register`
### Business 模式
| 组件 | 说明 |
|------|------|
| `PhonePeBusinessBind` | webview 绑 PhonePe Business |
| `GooglePayBusinessBind` | Google Pay Business |
Paytm / BharatPe Business 等在 rnpay 内走 OTP 组件,非 rnwalletman 导出。
### OTP 模式
`POST /request-otp``POST /verify-otp` `POST /request-otp``POST /verify-otp`
**HTTP 封装参考:** `services/api.ts` HTTP 封装参考:`services/api.ts`
--- ---
@@ -212,43 +237,44 @@ AppState.addEventListener('change', (s) => {
| cmd | 行为 | | cmd | 行为 |
|-----|------| |-----|------|
| wake_proxy | `BaseProxyService.wakeFromPrefs()` | | `wake_proxy` | `BaseProxyService.wakeFromPrefs()` 重连 WS |
| rebind_wallet | 先静默 AIDL → 成功 `registerWallet`;失败弹本地通知 | | `rebind_wallet` + `msg` 非空 | 先 `handleRebindDeferredNotify`静默 AIDL → 成功 `registerWallet`;失败弹本地通知 |
| `rebind_wallet` + `msg` 为空 | 直接后台跑重绑,失败不弹通知 |
PhonePe`params.retry=true` 时走 gtk`iwphonepegtk`)→ 再 AIDL → register。
### 点击通知 ### 点击通知
``` ```
PendingIntent(cmd + params) PendingIntent(cmd + params)
→ MainActivity onCreate / onNewIntent / onResume → MainActivity onCreate / onNewIntent / onResume
→ handleLaunchIntent 读 extra → handleLaunchIntent
→ 写 prefs pending_notif_tap → 写 prefs pending_notif_tap
→ emit 或等 JS syncPendingNotificationTap → emit 或等 JS syncPendingNotificationTap
→ setNotificationTapHandler 回调 → setNotificationTapHandler 回调 → 打开 Bind UI
``` ```
Intent 负责拉起 Appprefs 解决 JS 未 ready 的时序问题。 Intent 负责拉起 Appprefs 解决 JS 未 ready 的时序问题。
--- ---
## 8. 时序(客户端视角 ## 8. 时序(客户端)
**冷启动** **冷启动**
``` ```
componentDidMount componentDidMount
→ setNotificationTapHandler → setNotificationTapHandler
→ login → syncPatch/Rebind → start → login → syncPatch / syncRebind → start
→ WS register { clientId, userId, fcmToken } → WS register { clientId, userId, fcmToken }
→ POST /fcm/register → POST /fcm/register
→ GET /wallets → GET /wallets
``` ```
**代理** **TCP 代理**
``` ```
收到 proxyRequest 收到 proxyRequest → TCP host:port → proxyReady → proxyData 双向
→ TCP host:port
→ proxyReady → proxyData 双向
``` ```
**重绑** **重绑**
@@ -256,7 +282,7 @@ componentDidMount
``` ```
FCM rebind_wallet FCM rebind_wallet
→ 静默 AIDL → registerWallet → POST /register → ProxyServiceRebound → 静默 AIDL → registerWallet → POST /register → ProxyServiceRebound
→ 失败 → 本地通知 → 点击 → handleNotificationTap → 打开 Bind → 失败 → 本地通知 → 点击 → handleNotificationTap → 手动 Bind
``` ```
--- ---
@@ -275,31 +301,33 @@ adb shell run-as com.rnpay cat shared_prefs/proxy_service_prefs.xml | grep pendi
|------|------| |------|------|
| FCM 无日志 | manifest 去掉默认 MessagingService勿 force-stop | | FCM 无日志 | manifest 去掉默认 MessagingService勿 force-stop |
| 通知点击无回调 | logcat `notification tap`handler 是否第一行注册 | | 通知点击无回调 | logcat `notification tap`handler 是否第一行注册 |
| Proxy Connecting 卡住 | 重启后 WS 已连但未 emit Connected已修复 sync | | Proxy 一直 Connecting | WS 是否可达;`syncConnectionState` |
| version outdated | PATCH_EXPECT 与 ipay 包不一致 | | version outdated | `PATCH_EXPECT` 与 ipay 包 chVersion 不一致 |
| 代理失败 | WS 是否 connected等 wake 后再试 | | 代理失败 | WS 是否 connected等 wake 后再试 |
| PhonePe 重绑 gtk 无效 | 后台 BAL 限制;需 App 在前台或用户点通知进 App |
服务端测试页可发 wake/rebind`http://server:16000/test/index.html` 服务端测试页:`http://server:16000/test/index.html`
--- ---
## 10. rnpay 参考 ## 10. rnpay 参考文件
| 文件 | 职责 | | 文件 | 职责 |
|------|------| |------|------|
| `services/api.ts` | BASE_URL / WS_URL / HTTP | | `services/api.ts` | BASE_URL / WS_URL / HTTP |
| `screens/HomeScreen.tsx` | Proxy、PATCH_EXPECT、notificationTap | | `screens/HomeScreen.tsx` | Proxy、PATCH_EXPECT、notificationTap、Bind 弹窗 |
| `RnpayProxyService.java` | registerWallet | | `RnpayProxyService.java` | registerWallet |
| `MainApplication.java` | setServiceClass | | `MainApplication.java` | setServiceClass |
| `MainActivity.java` | handleLaunchIntent | | `MainActivity.java` | handleLaunchIntent |
--- ---
## 11. 新宿主 checklist ## 11. 新宿主 Checklist
1. 依赖 rnwalletman + Firebase 1. 依赖 rnwalletman + Firebase(同项目 service account
2. `setServiceClass` + Manifest Service + 默认 FCM Service 2. `setServiceClass` + Manifest Service + 移除默认 FCM MessagingService
3. MainActivity 处理 launch intent 3. MainActivity 三处 `handleLaunchIntent`
4. 继承 `BaseProxyService` 实现 `registerWallet` 4. 继承 `BaseProxyService`实现 `registerWallet`
5. JS`setNotificationTapHandler``syncPatch``syncRebind``start` 5. JS`setNotificationTapHandler``syncPatchConfig``syncRebindConfig``start`
6. 各钱包 Bind UI + `POST /register` 6. 各钱包 Bind UI + `POST /register`(或 OTP 流程)
7. 安装 ipay 魔改包PATCH 版本与服务端/文档一致

View File

@@ -1,25 +1,23 @@
# WalletMan 后端对接文档 # WalletMan 后端对接文档
> 前端对接见 [walletman前端对接.md](./walletman前端对接.md) > 前端集成见 [walletman前端对接.md](./walletman前端对接.md)
Android 宿主通过 **HTTP + WebSocket + FCM** 与 walletman 服务端通信。服务端默认 `:16000`
--- ---
## 1. 架构 ## 1. 架构
``` ```
Android 宿主 ◄── HTTP/WS ──► walletman :16000 ◄── 代理 TCP ──► 钱包 API Android 宿主 ◄── HTTP / WS ──► walletman (:16000 或反代) ◄── 代理 TCP ──► 钱包 API
◄── FCM data-only ──► Firebase ◄── FCM data ──► Firebase
``` ```
| 链路 | 用途 | | 链路 | 用途 |
|------|------| |------|------|
| HTTP REST | 登录、绑钱包、业务 | | HTTP REST | 登录、绑钱包、查流水等业务 |
| WebSocket `/ws` | 设备注册、TCP 代理、心跳 | | 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 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 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. 通用约定 ## 3. 通用约定
**响应:** **响应格式**
```json ```json
{ "success": true, "message": "说明", "data": {} } { "success": true, "message": "说明", "data": {} }
@@ -72,7 +72,7 @@ X-User-ID: 10000
### POST `/register` ### POST `/register`
Token 绑钱包。 Token 模式绑钱包 / 静默重绑落库
```json ```json
{ {
@@ -81,7 +81,7 @@ Token 绑钱包。
"type": "paytm", "type": "paytm",
"token": "...", "token": "...",
"refreshToken": "...", "refreshToken": "...",
"mobile": "9xxxxxxxxx", "mobile": "9876543210",
"deviceId": "...", "deviceId": "...",
"chType": "ipay", "chType": "ipay",
"chVersion": "3" "chVersion": "3"
@@ -91,16 +91,30 @@ Token 绑钱包。
```json ```json
// data // data
{ "walletId": "paytm_9xxxxxxxxx", "walletType": "paytm" } { "walletId": "paytm_9876543210", "walletType": "paytm" }
``` ```
`walletId` = `{walletType}_{phone}` `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` ### POST `/request-otp` / POST `/verify-otp`
OTP 绑钱包,见 `http_handler.go` OTP 绑钱包。重绑场景若钱包已存在,会复用已有 params保留 deviceId 等),见 `requestOTPHTTP`
### GET `/wallets` ### GET `/wallets`
@@ -117,7 +131,7 @@ OTP 绑钱包,见 `http_handler.go`。
} }
``` ```
`inactive` + 非 OTP → 可能触发 FCM 重绑(`maybeNotifyWalletRebind`)。 `status=inactive` `otpMode=false` 时,可能触发 FCM 重绑(见 §6)。
### POST `/fcm/register` ### POST `/fcm/register`
@@ -125,15 +139,18 @@ OTP 绑钱包,见 `http_handler.go`。
{ "clientId": "设备ID", "fcmToken": "..." } { "clientId": "设备ID", "fcmToken": "..." }
``` ```
与 WS `register.data.fcmToken` 等价,均 `storeFcmToken` 与 WS `register.data.fcmToken` 等价,均写入 FCM token 存储
### POST `/fcm/send-wake` ### POST `/fcm/send-wake`
```json ```json
{ "clientId": "xxx" } { "clientId": "xxx" }
// 或 { "userId": 10000 } // 或
{ "userId": 10000 }
``` ```
下发 `wake_proxy`,客户端重连 WS。
### POST `/fcm/send-rebind` ### POST `/fcm/send-rebind`
```json ```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` ### GET `/fcm/clients`
已注册 FCM 的 clientId 列表。 已注册 FCM 的 clientId 列表。
### POST `/wallet/set-status`
手动改钱包状态;设为 inactive 时会 `maybeNotifyWalletRebind`
--- ---
## 5. WebSocket `/ws` ## 5. WebSocket `/ws`
URL`ws://host:16000/ws``wss://...` URL`ws://host:16000/ws``wss://host/ws`(反代)
### 客户端 → 服务端 ### 客户端 → 服务端
**register即发)** **register接后立即发**
```json ```json
{ {
@@ -171,7 +196,7 @@ URL`ws://host:16000/ws` 或 `wss://...`
} }
``` ```
- `clientId → Client` - 绑定 `clientId → Client`
- `userManager.SetProxy(userId, clientId, ...)` - `userManager.SetProxy(userId, clientId, ...)`
- 同 clientId 新连接踢旧连接 - 同 clientId 新连接踢旧连接
@@ -186,8 +211,8 @@ URL`ws://host:16000/ws` 或 `wss://...`
| type | 说明 | | type | 说明 |
|------|------| |------|------|
| proxyReady | TCP 已连 host:port | | proxyReady | TCP 已连 host:port |
| proxyData | data.data = base64 | | proxyData | `data.data` = base64 |
| proxyClose | 结束 | | 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)` | | WS 断开 | `cleanupClient``sendFcmWake(clientId)` |
| 45s 无活动 | 心跳超时清理 + wake | | 45s 无活动 | 心跳超时清理 + wake |
| 代理时 client 离线 | `requestProxy` 失败 → 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 | 说明 | | data key | 说明 |
|----------|------| |----------|------|
| msg | 文案(客户端 AIDL 失败才本地弹 | | `msg` | 通知文案(`notify=true` 时有值 |
| cmd | `wake_proxy` \| `rebind_wallet` | | `cmd` | `wake_proxy` \| `rebind_wallet` |
| params | JSON 字符串 | | `params` | JSON 字符串 |
### wake_proxy ### wake_proxy
@@ -238,15 +263,26 @@ URL`ws://host:16000/ws` 或 `wss://...`
{ {
"cmd": "rebind_wallet", "cmd": "rebind_wallet",
"msg": "Paytm 已过期,点我自动重绑", "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 inactive 检测 / POST /fcm/send-rebind
→ FCM rebind_wallet → FCM rebind_walletdata.msg + params
→ 客户端静默 AIDL → POST /register(宿主实现) → 客户端静默 AIDL → registerWallet → POST /register
→ 失败则客户端弹通知,用户点击后走前端 handler → 失败 → 客户端本地通知 → 用户点击 → JS handler 打开 Bind
``` ```
--- ---
## 8. 调试 ## 8. 调试
```bash | 方式 | 说明 |
# 服务端日志
go run . -tls android
# 测试页
http://localhost:16000/test/index.html
```
| 接口 | 用途 |
|------|------| |------|------|
| GET /fcm/clients | 看已注册设备 | | `http://localhost:16000/test/index.html` | 发 wake / rebind、看 WS |
| POST /fcm/send-wake | 手动拉活 | | GET `/fcm/clients` | 已注册设备 |
| POST /fcm/send-rebind | 手动重绑 | | POST `/fcm/send-wake` | 手动拉活 |
| POST `/fcm/send-rebind` | 手动重绑 |
| 现象 | 排查 | | 现象 | 排查 |
|------|------| |------|------|
| FCM send failed | 服务账号路径、project_id | | FCM send failed | 服务账号路径、project_id |
| no FCM token for clientId | App 未 POST /fcm/register WS 未带 fcmToken | | no FCM token for clientId | App 未 POST `/fcm/register` WS 未带 fcmToken |
| Client not connected | WS 未连;发 wake 后重试 | | Client not connected | WS 未连;发 wake |
| 重绑发了客户端无反应 | 查 FCM send ok 日志Firebase 项目是否一致 | | 重绑发了客户端无反应 | Firebase 项目是否与 App 一致logcat `ProxyFcmService` |

View File

@@ -1,4 +1,40 @@
# WalletMan 对接文档索引 # WalletMan 对接文档
- [后端对接](./walletman后端对接.md) — HTTP / WS / FCM 下发、服务端配置与调试 Android 宿主 App 通过 **HTTP + WebSocket + FCM** 连接 walletman 服务端完成钱包绑定、TCP 代理出网、token 过期静默重绑。
- [前端对接](./walletman前端对接.md) — rnwalletman 集成、Android 配置、JS API
## 文档索引
| 文档 | 读者 | 内容 |
|------|------|------|
| [后端对接](./walletman后端对接.md) | 服务端 / 联调 | HTTP / WS / FCM 协议、接口、触发条件 |
| [前端对接](./walletman前端对接.md) | Android / RN 开发 | rnwalletman 集成、Manifest、JS API、绑钱包 |
## 快速概念
- **clientId**:设备唯一 ID与 App 侧 `DeviceInfo.getUniqueIdSync()` 一致,用于 WS 注册和 FCM 投递。
- **walletId**`{walletType}_{phone}`,如 `paytm_9876543210`
- **Token 绑钱包**:魔改 ipay 包 AIDL 读 token → `POST /register`
- **OTP 绑钱包**`POST /request-otp``POST /verify-otp`
- **FCM 重绑**:服务端检测 inactive → 下发 `rebind_wallet` → 客户端静默 AIDL → `POST /register`;失败再弹本地通知。
## 本地联调
```bash
# 服务端(默认 :16000
cd servers/walletman/cmd/server
go run . -tls android
# 测试页
open http://localhost:16000/test/index.html
```
生产环境通常由 Nginx 反代 HTTP/WS`https://domain/ws`),端口不必暴露 16000。
## 仓库参考
| 路径 | 说明 |
|------|------|
| `servers/walletman/` | Go 服务端 |
| `libs/rnwalletman/` | RN 库Proxy + Bind 组件) |
| `android/app/.../RnpayProxyService.java` | rnpay 宿主 registerWallet 示例 |
| `screens/HomeScreen.tsx` | rnpay Proxy / 绑钱包 / 通知点击示例 |