fix
This commit is contained in:
@@ -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 模式(Personal,ipay 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 负责拉起 App;prefs 解决 JS 未 ready 的时序问题。
|
Intent 负责拉起 App;prefs 解决 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 版本与服务端/文档一致
|
||||||
|
|||||||
@@ -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:** 设备唯一 ID,WS 注册 key + FCM 目标 key,需与 App 侧 `DeviceInfo.getUniqueIdSync()` 一致。
|
**clientId**:设备唯一 ID,WS 注册 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 Personal(ipay AIDL) |
|
||||||
|
| `phonepe` | PhonePe Personal(ipay 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_wallet(data.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` |
|
||||||
|
|||||||
@@ -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 / 绑钱包 / 通知点击示例 |
|
||||||
|
|||||||
Reference in New Issue
Block a user