Files
rnpay/docs/walletman前端对接.md
2026-06-16 11:29:27 +08:00

306 lines
7.3 KiB
Markdown
Raw 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)
宿主 App**rnpay**)依赖 **rnwalletman**,负责 Proxy 常驻、WS 连接、FCM 收令、绑钱包 UI。
---
## 1. 职责
| 模块 | 职责 |
|------|------|
| `ProxyBackgroundService` | 启停 Proxy、WS 状态、FCM/rebind 配置 |
| `BaseProxyService` | 前台 Service、WS、TCP 代理、FCM 静默重绑 |
| `ProxyFcmService` | 收 FCM、拉活、通知点击 → prefs → JS |
| 宿主 `*ProxyService` | 实现 `registerWallet` → POST `/register` |
| Bind 组件 | 各钱包 token/OTP 绑定 UI |
**clientId** `DeviceInfo.getUniqueIdSync()`,与后端 WS/FCM 一致。
---
## 2. 依赖
```json
"rnwalletman": "file:libs/rnwalletman"
```
改 lib 后:
```bash
rsync -a --delete libs/rnwalletman/ node_modules/rnwalletman/
```
需 Firebase`google-services.json` 与服务端 FCM 同项目。
---
## 3. Android 配置
### Application
```java
// MainApplication.onCreate
BaseProxyService.setServiceClass(RnpayProxyService.class);
```
### Manifest
```xml
<service
android:name=".RnpayProxyService"
android:exported="false"
android:foregroundServiceType="dataSync"
android:stopWithTask="false" />
<!-- 避免 Firebase 默认 Service 抢 FCM -->
<service
android:name="com.google.firebase.messaging.FirebaseMessagingService"
tools:node="remove" />
```
`ProxyFcmService` 由 rnwalletman manifest merge宿主不用重复声明。
### MainActivity
```java
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ProxyFcmService.handleLaunchIntent(this);
}
@Override protected void onResume() {
super.onResume();
ProxyFcmService.handleLaunchIntent(this);
}
@Override public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
ProxyFcmService.handleLaunchIntent(this);
}
```
`launchMode="singleTask"` 推荐。
---
## 4. 宿主 registerWallet
FCM 静默重绑成功后 native 调用:
```java
@Override
protected void registerWallet(Context ctx, String walletId, String walletType,
String phone, JSONObject params) throws Exception {
// POST {baseUrl}/register
// body: { "walletType": walletType, "params": params }
// header: X-User-ID
}
```
`baseUrl/userId/userToken` 来自 JS `syncRebindConfig``proxy_service_prefs`
参考:`android/app/src/main/java/com/rnpay/RnpayProxyService.java`
---
## 5. JS 对接
### 启动顺序
```typescript
import {
proxyBackgroundService,
type RebindConfig,
type PatchConfig,
type FcmNotificationTapPayload,
} from 'rnwalletman';
import DeviceInfo from 'react-native-device-info';
const PATCH_EXPECT: PatchConfig = {
chType: 'ipay',
paytmChVersion: '3',
phonepeChVersion: '1',
mobikwikChVersion: '1',
};
async function bootstrap(userId: number, userToken: string) {
const clientId = DeviceInfo.getUniqueIdSync();
// ① 尽早注册componentDidMount 第一行)
proxyBackgroundService.setNotificationTapHandler(handleNotificationTap);
await proxyBackgroundService.syncPatchConfig(PATCH_EXPECT);
await proxyBackgroundService.syncRebindConfig({
baseUrl: Api.BASE_URL,
userId,
userToken,
onRebound: () => fetchWallets(),
});
await proxyBackgroundService.start({
wsUrl: Api.WS_URL,
clientId,
userId,
heartbeatInterval: 10000,
reconnectInterval: 5000,
registerFcmToken: (cid, token) =>
Api.instance.registerFcmToken(cid, token),
onConnected: () => setProxyStatus('connected'),
onDisconnected: () => setProxyStatus('disconnected'),
onError: (msg) => setProxyStatus('error', msg),
});
}
function handleNotificationTap(payload: FcmNotificationTapPayload) {
if (payload.cmd !== 'rebind_wallet') return;
const { walletId, walletType, phone } = payload.params;
// 按 walletType 打开 Bind 弹窗
}
```
App 回前台时补读 pending
```typescript
AppState.addEventListener('change', (s) => {
if (s === 'active') void proxyBackgroundService.syncPendingNotificationTap();
});
```
### API
| 方法 | 作用 |
|------|------|
| `start(config)` | 前台 Proxy + WS |
| `stop()` | 停止 |
| `syncRebindConfig(config \| null)` | native 重绑 HTTP 配置 |
| `syncPatchConfig(config)` | ipay chType/chVersion 校验 |
| `setNotificationTapHandler(fn \| null)` | 通知点击回调 |
| `syncPendingNotificationTap()` | 消费 prefs pending |
### Native 事件
| 事件 | data |
|------|------|
| ProxyServiceConnected | - |
| ProxyServiceDisconnected | - |
| ProxyServiceRebound | walletId |
| ProxyServiceNotificationTap | JSON `{cmd,params}` |
| ProxyServiceCustomCommand | WS 自定义消息 |
---
## 6. 绑钱包
**Token 模式**
1. 打开 `PaytmPersonalBind` / `PhonePePersonalBind`
2. 校验 `chType/chVersion` === `PATCH_EXPECT`
3. `POST /register`
**OTP 模式**
`POST /request-otp``POST /verify-otp`
**HTTP 封装参考:** `services/api.ts`
---
## 7. FCM 客户端行为
### 收到 FCM
| cmd | 行为 |
|-----|------|
| wake_proxy | `BaseProxyService.wakeFromPrefs()` |
| rebind_wallet | 先静默 AIDL → 成功 `registerWallet`;失败弹本地通知 |
### 点击通知
```
PendingIntent(cmd+params)
→ MainActivity onCreate/onNewIntent/onResume
→ handleLaunchIntent 读 extra
→ 写 prefs pending_notif_tap
→ emit 或等 JS syncPendingNotificationTap
→ setNotificationTapHandler 回调
```
Intent 负责拉起 Appprefs 解决 JS 未 ready 的时序问题。
---
## 8. 时序(客户端视角)
**冷启动**
```
componentDidMount
→ setNotificationTapHandler
→ login → syncPatch/Rebind → start
→ WS register { clientId, userId, fcmToken }
→ POST /fcm/register
→ GET /wallets
```
**代理**
```
收到 proxyRequest
→ TCP host:port
→ proxyReady → proxyData 双向
```
**重绑**
```
FCM rebind_wallet
→ 静默 AIDL → registerWallet → POST /register → ProxyServiceRebound
→ 失败 → 本地通知 → 点击 → handleNotificationTap → 打开 Bind
```
---
## 9. 调试
```bash
adb logcat -s ProxyFcmService ProxyService WalletRebind ProxyServiceModule
```
```bash
adb shell run-as com.rnpay cat shared_prefs/proxy_service_prefs.xml | grep pending
```
| 现象 | 排查 |
|------|------|
| FCM 无日志 | manifest 去掉默认 MessagingService勿 force-stop |
| 通知点击无回调 | logcat `notification tap`handler 是否第一行注册 |
| Proxy Connecting 卡住 | 重启后 WS 已连但未 emit Connected已修复 sync |
| version outdated | PATCH_EXPECT 与 ipay 包不一致 |
| 代理失败 | WS 是否 connected等 wake 后再试 |
服务端测试页可发 wake/rebind`http://server:16000/test/index.html`
---
## 10. rnpay 参考
| 文件 | 职责 |
|------|------|
| `services/api.ts` | BASE_URL / WS_URL / HTTP |
| `screens/HomeScreen.tsx` | Proxy、PATCH_EXPECT、notificationTap |
| `RnpayProxyService.java` | registerWallet |
| `MainApplication.java` | setServiceClass |
| `MainActivity.java` | handleLaunchIntent |
---
## 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`