9.0 KiB
WalletMan 前端对接文档
后端协议见 walletman后端对接.md
宿主 App(如 rnpay)依赖 rnwalletman,负责 Proxy 常驻、WS 连接、FCM 收令、绑钱包 UI。
1. 职责分工
| 模块 | 职责 |
|---|---|
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 一致。
2. 依赖
"rnwalletman": "file:libs/rnwalletman"
修改 lib 后同步:
rsync -a --delete libs/rnwalletman/ node_modules/rnwalletman/
需配置 Firebase:google-services.json 与服务端 FCM 同一项目。
Personal 绑钱包需安装对应 ipay 魔改包(chType/chVersion 与 PATCH_EXPECT 一致)。
3. Android 配置
Application
// MainApplication.onCreate
BaseProxyService.setServiceClass(RnpayProxyService.class);
Manifest
<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
@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 调用宿主实现的 HTTP:
@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 对接
启动顺序
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, // 如 wss://domain/ws
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 通知点击:
AppState.addEventListener('change', (s) => {
if (s === 'active') void proxyBackgroundService.syncPendingNotificationTap();
});
ProxyBackgroundService API
| 方法 | 作用 |
|---|---|
start(config) |
启动前台 Proxy + WS |
stop() |
停止 |
syncRebindConfig(config | null) |
写入 native 重绑 HTTP 配置 |
syncPatchConfig(config) |
ipay chType/chVersion 校验(FCM 重绑共用) |
setNotificationTapHandler(fn | null) |
通知点击回调 |
syncPendingNotificationTap() |
消费 prefs 中 pending 点击 |
其他导出
| 导出 | 作用 |
|---|---|
proxySendMessage / onProxyMessage |
WS 自定义消息(ProxyBridge) |
startActivity / IntentFlags |
拉起第三方 App deeplink |
openPaytmPayToBank 等 |
银行转账 deeplink 封装 |
Native → JS 事件
| 事件 | data |
|---|---|
| ProxyServiceConnected | - |
| ProxyServiceDisconnected | - |
| ProxyServiceRebound | walletId |
| ProxyServiceNotificationTap | JSON {cmd, params} |
| ProxyServiceCustomCommand | WS 自定义消息 |
| ProxyServiceFcmToken | FCM token 刷新 |
6. 绑钱包
Token 模式(Personal,ipay AIDL)
| 组件 | walletType |
|---|---|
PaytmPersonalBind |
paytm |
PhonePePersonalBind |
phonepe(需传 userToken) |
MobikwikPersonalBind |
mobikwik(需传 userToken) |
FreechargePersonalBind |
freecharge |
流程:打开 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
7. FCM 客户端行为
收到 FCM
| cmd | 行为 |
|---|---|
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
→ 写 prefs pending_notif_tap
→ emit 或等 JS syncPendingNotificationTap
→ setNotificationTapHandler 回调 → 打开 Bind UI
Intent 负责拉起 App;prefs 解决 JS 未 ready 的时序问题。
8. 时序(客户端)
冷启动
componentDidMount
→ setNotificationTapHandler
→ login → syncPatch / syncRebind → start
→ WS register { clientId, userId, fcmToken }
→ POST /fcm/register
→ GET /wallets
TCP 代理
收到 proxyRequest → TCP host:port → proxyReady → proxyData 双向
重绑
FCM rebind_wallet
→ 静默 AIDL → registerWallet → POST /register → ProxyServiceRebound
→ 失败 → 本地通知 → 点击 → handleNotificationTap → 手动 Bind
9. 调试
adb logcat -s ProxyFcmService ProxyService WalletRebind ProxyServiceModule
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 是否可达;syncConnectionState |
| version outdated | PATCH_EXPECT 与 ipay 包 chVersion 不一致 |
| 代理失败 | WS 是否 connected;等 wake 后再试 |
| PhonePe 重绑 gtk 无效 | 后台 BAL 限制;需 App 在前台或用户点通知进 App |
服务端测试页:http://server:16000/test/index.html
10. rnpay 参考文件
| 文件 | 职责 |
|---|---|
services/api.ts |
BASE_URL / WS_URL / HTTP |
screens/HomeScreen.tsx |
Proxy、PATCH_EXPECT、notificationTap、Bind 弹窗 |
RnpayProxyService.java |
registerWallet |
MainApplication.java |
setServiceClass |
MainActivity.java |
handleLaunchIntent |
11. 新宿主 Checklist
- 依赖 rnwalletman + Firebase(同项目 service account)
setServiceClass+ Manifest Service + 移除默认 FCM MessagingService- MainActivity 三处
handleLaunchIntent - 继承
BaseProxyService,实现registerWallet - JS:
setNotificationTapHandler→syncPatchConfig→syncRebindConfig→start - 各钱包 Bind UI +
POST /register(或 OTP 流程) - 安装 ipay 魔改包,PATCH 版本与服务端/文档一致