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 配置 |
| `BaseProxyService` | 前台 Service、WS、TCP 代理、FCM 静默重绑 |
| `ProxyFcmService` | 收 FCM、拉活、通知点击 → prefs → JS |
| 宿主 `*ProxyService` | 实现 `registerWallet` → POST `/register` |
| Bind 组件 | 各钱包 token/OTP 绑定 UI |
| `ProxyBackgroundService` | JS 层启停 Proxy、WS 状态、rebind/patch 配置、通知点击 |
| `BaseProxyService` | Android 前台 Service、WS、TCP 代理、FCM 静默重绑 |
| `ProxyFcmService` | 收 FCM、拉活、静默重绑、失败弹通知点击 → prefs → JS |
| 宿主 `*ProxyService` | 继承 `BaseProxyService`实现 `registerWallet` → POST `/register` |
| Bind 组件 | 各钱包 token / webview / OTP 绑定 UI |
**clientId** `DeviceInfo.getUniqueIdSync()`,与后端 WS/FCM 一致。
@@ -26,13 +26,15 @@
"rnwalletman": "file:libs/rnwalletman"
```
改 lib 后:
改 lib 后同步
```bash
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" />
```
`ProxyFcmService` 由 rnwalletman manifest merge宿主不用重复声明。
`ProxyFcmService` 由 rnwalletman manifest merge宿主无需重复声明。
### MainActivity
@@ -79,13 +81,13 @@ BaseProxyService.setServiceClass(RnpayProxyService.class);
}
```
`launchMode="singleTask"` 推荐
推荐 `launchMode="singleTask"`
---
## 4. 宿主 registerWallet
FCM 静默重绑成功后 native 调用:
FCM 静默重绑成功后native 调用宿主实现的 HTTP
```java
@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`
@@ -139,7 +141,7 @@ async function bootstrap(userId: number, userToken: string) {
});
await proxyBackgroundService.start({
wsUrl: Api.WS_URL,
wsUrl: Api.WS_URL, // 如 wss://domain/ws
clientId,
userId,
heartbeatInterval: 10000,
@@ -155,11 +157,11 @@ async function bootstrap(userId: number, userToken: string) {
function handleNotificationTap(payload: FcmNotificationTapPayload) {
if (payload.cmd !== 'rebind_wallet') return;
const { walletId, walletType, phone } = payload.params;
// 按 walletType 打开 Bind 弹窗
// 按 walletType 打开对应 Bind 弹窗(手动重绑)
}
```
App 回前台补读 pending
App 回前台补读 pending 通知点击
```typescript
AppState.addEventListener('change', (s) => {
@@ -167,42 +169,65 @@ AppState.addEventListener('change', (s) => {
});
```
### API
### ProxyBackgroundService API
| 方法 | 作用 |
|------|------|
| `start(config)` | 前台 Proxy + WS |
| `start(config)` | 启动前台 Proxy + WS |
| `stop()` | 停止 |
| `syncRebindConfig(config \| null)` | native 重绑 HTTP 配置 |
| `syncPatchConfig(config)` | ipay chType/chVersion 校验 |
| `syncRebindConfig(config \| null)` | 写入 native 重绑 HTTP 配置 |
| `syncPatchConfig(config)` | ipay chType/chVersion 校验FCM 重绑共用) |
| `setNotificationTapHandler(fn \| null)` | 通知点击回调 |
| `syncPendingNotificationTap()` | 消费 prefs pending |
| `syncPendingNotificationTap()` | 消费 prefs pending 点击 |
### Native 事件
### 其他导出
| 导出 | 作用 |
|------|------|
| `proxySendMessage` / `onProxyMessage` | WS 自定义消息(`ProxyBridge` |
| `startActivity` / `IntentFlags` | 拉起第三方 App deeplink |
| `openPaytmPayToBank` 等 | 银行转账 deeplink 封装 |
### Native → JS 事件
| 事件 | data |
|------|------|
| ProxyServiceConnected | - |
| ProxyServiceDisconnected | - |
| ProxyServiceRebound | walletId |
| ProxyServiceNotificationTap | JSON `{cmd,params}` |
| ProxyServiceNotificationTap | JSON `{cmd, params}` |
| ProxyServiceCustomCommand | WS 自定义消息 |
| ProxyServiceFcmToken | FCM token 刷新 |
---
## 6. 绑钱包
**Token 模式**
### Token 模式Personalipay AIDL
1. 打开 `PaytmPersonalBind` / `PhonePePersonalBind`
2. 校验 `chType/chVersion` === `PATCH_EXPECT`
3. `POST /register`
| 组件 | walletType |
|------|------------|
| `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`
**HTTP 封装参考:** `services/api.ts`
HTTP 封装参考:`services/api.ts`
---
@@ -212,43 +237,44 @@ AppState.addEventListener('change', (s) => {
| cmd | 行为 |
|-----|------|
| wake_proxy | `BaseProxyService.wakeFromPrefs()` |
| rebind_wallet | 先静默 AIDL → 成功 `registerWallet`;失败弹本地通知 |
| `wake_proxy` | `BaseProxyService.wakeFromPrefs()` 重连 WS |
| `rebind_wallet` + `msg` 非空 | 先 `handleRebindDeferredNotify`静默 AIDL → 成功 `registerWallet`;失败弹本地通知 |
| `rebind_wallet` + `msg` 为空 | 直接后台跑重绑,失败不弹通知 |
PhonePe`params.retry=true` 时走 gtk`iwphonepegtk`)→ 再 AIDL → register。
### 点击通知
```
PendingIntent(cmd+params)
→ MainActivity onCreate/onNewIntent/onResume
→ handleLaunchIntent 读 extra
PendingIntent(cmd + params)
→ MainActivity onCreate / onNewIntent / onResume
→ handleLaunchIntent
→ 写 prefs pending_notif_tap
→ emit 或等 JS syncPendingNotificationTap
→ setNotificationTapHandler 回调
→ setNotificationTapHandler 回调 → 打开 Bind UI
```
Intent 负责拉起 Appprefs 解决 JS 未 ready 的时序问题。
---
## 8. 时序(客户端视角
## 8. 时序(客户端)
**冷启动**
```
componentDidMount
→ setNotificationTapHandler
→ login → syncPatch/Rebind → start
→ login → syncPatch / syncRebind → start
→ WS register { clientId, userId, fcmToken }
→ POST /fcm/register
→ GET /wallets
```
**代理**
**TCP 代理**
```
收到 proxyRequest
→ TCP host:port
→ proxyReady → proxyData 双向
收到 proxyRequest → TCP host:port → proxyReady → proxyData 双向
```
**重绑**
@@ -256,7 +282,7 @@ componentDidMount
```
FCM rebind_wallet
→ 静默 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 |
| 通知点击无回调 | logcat `notification tap`handler 是否第一行注册 |
| Proxy Connecting 卡住 | 重启后 WS 已连但未 emit Connected已修复 sync |
| version outdated | PATCH_EXPECT 与 ipay 包不一致 |
| Proxy 一直 Connecting | WS 是否可达;`syncConnectionState` |
| version outdated | `PATCH_EXPECT` 与 ipay 包 chVersion 不一致 |
| 代理失败 | 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 |
| `screens/HomeScreen.tsx` | Proxy、PATCH_EXPECT、notificationTap |
| `screens/HomeScreen.tsx` | Proxy、PATCH_EXPECT、notificationTap、Bind 弹窗 |
| `RnpayProxyService.java` | registerWallet |
| `MainApplication.java` | setServiceClass |
| `MainActivity.java` | handleLaunchIntent |
---
## 11. 新宿主 checklist
## 11. 新宿主 Checklist
1. 依赖 rnwalletman + Firebase
2. `setServiceClass` + Manifest Service + 默认 FCM Service
3. MainActivity 处理 launch intent
4. 继承 `BaseProxyService` 实现 `registerWallet`
5. JS`setNotificationTapHandler``syncPatch``syncRebind``start`
6. 各钱包 Bind UI + `POST /register`
1. 依赖 rnwalletman + Firebase(同项目 service account
2. `setServiceClass` + Manifest Service + 移除默认 FCM MessagingService
3. MainActivity 三处 `handleLaunchIntent`
4. 继承 `BaseProxyService`实现 `registerWallet`
5. JS`setNotificationTapHandler``syncPatchConfig``syncRebindConfig``start`
6. 各钱包 Bind UI + `POST /register`(或 OTP 流程)
7. 安装 ipay 魔改包PATCH 版本与服务端/文档一致