文档
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
@@ -49,5 +50,9 @@
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="dataSync"
|
||||
android:stopWithTask="false" />
|
||||
<!-- 避免 Firebase SDK 默认 Service 抢收 data-only FCM -->
|
||||
<service
|
||||
android:name="com.google.firebase.messaging.FirebaseMessagingService"
|
||||
tools:node="remove" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -22,6 +22,7 @@ public class MainActivity extends ReactActivity {
|
||||
@Override
|
||||
protected void onCreate(android.os.Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
ProxyFcmService.handleLaunchIntent(this);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
|
||||
305
docs/walletman前端对接.md
Normal file
305
docs/walletman前端对接.md
Normal file
@@ -0,0 +1,305 @@
|
||||
# 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 负责拉起 App;prefs 解决 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`
|
||||
309
docs/walletman后端对接.md
Normal file
309
docs/walletman后端对接.md
Normal file
@@ -0,0 +1,309 @@
|
||||
# WalletMan 后端对接文档
|
||||
|
||||
> 前端对接见 [walletman前端对接.md](./walletman前端对接.md)
|
||||
|
||||
Android 宿主通过 **HTTP + WebSocket + FCM** 与 walletman 服务端通信。服务端默认 `:16000`。
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构
|
||||
|
||||
```
|
||||
Android 宿主 ◄── HTTP/WS ──► walletman :16000 ◄── 代理 TCP ──► 钱包 API
|
||||
◄── FCM data-only ──► Firebase
|
||||
```
|
||||
|
||||
| 链路 | 用途 |
|
||||
|------|------|
|
||||
| HTTP REST | 登录、绑钱包、业务 |
|
||||
| WebSocket `/ws` | 设备注册、TCP 代理、心跳 |
|
||||
| FCM | WS 断线拉活、token 过期重绑 |
|
||||
|
||||
**clientId:** 设备唯一 ID,WS 注册 key + FCM 目标 key,需与 App 侧 `DeviceInfo.getUniqueIdSync()` 一致。
|
||||
|
||||
---
|
||||
|
||||
## 2. 启动与配置
|
||||
|
||||
```bash
|
||||
cd servers/walletman/cmd/server
|
||||
go run . -tls android
|
||||
```
|
||||
|
||||
测试页:`http://localhost:16000/test/index.html`
|
||||
|
||||
**FCM 服务账号:**
|
||||
|
||||
```
|
||||
servers/walletman/cmd/server/config/fcm-service-account.json
|
||||
```
|
||||
|
||||
或 `FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App `google-services.json` **同一 Firebase 项目**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 通用约定
|
||||
|
||||
**响应:**
|
||||
|
||||
```json
|
||||
{ "success": true, "message": "说明", "data": {} }
|
||||
```
|
||||
|
||||
**需登录接口请求头:**
|
||||
|
||||
```
|
||||
Content-Type: application/json
|
||||
X-User-ID: 10000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. HTTP 接口
|
||||
|
||||
### POST `/login`
|
||||
|
||||
```json
|
||||
// req
|
||||
{ "username": "test123", "password": "123456" }
|
||||
// data
|
||||
{ "userId": 10000, "userToken": "10000" }
|
||||
```
|
||||
|
||||
### POST `/register`
|
||||
|
||||
Token 绑钱包。
|
||||
|
||||
```json
|
||||
{
|
||||
"walletType": "paytm",
|
||||
"params": {
|
||||
"type": "paytm",
|
||||
"token": "...",
|
||||
"refreshToken": "...",
|
||||
"mobile": "9xxxxxxxxx",
|
||||
"deviceId": "...",
|
||||
"chType": "ipay",
|
||||
"chVersion": "3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// data
|
||||
{ "walletId": "paytm_9xxxxxxxxx", "walletType": "paytm" }
|
||||
```
|
||||
|
||||
`walletId` = `{walletType}_{phone}`。
|
||||
|
||||
常用 `walletType`:`paytm` | `phonepe` | `mobikwik` | `freecharge` | `paytm business` | `phonepe business`。
|
||||
|
||||
### POST `/request-otp` / POST `/verify-otp`
|
||||
|
||||
OTP 绑钱包,见 `http_handler.go`。
|
||||
|
||||
### GET `/wallets`
|
||||
|
||||
```json
|
||||
{
|
||||
"wallets": [{
|
||||
"id": "paytm_9876543210",
|
||||
"walletType": "paytm",
|
||||
"phone": "9876543210",
|
||||
"upi": "xxx@paytm",
|
||||
"status": "active",
|
||||
"otpMode": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
`inactive` + 非 OTP → 可能触发 FCM 重绑(`maybeNotifyWalletRebind`)。
|
||||
|
||||
### POST `/fcm/register`
|
||||
|
||||
```json
|
||||
{ "clientId": "设备ID", "fcmToken": "..." }
|
||||
```
|
||||
|
||||
与 WS `register.data.fcmToken` 等价,均 `storeFcmToken`。
|
||||
|
||||
### POST `/fcm/send-wake`
|
||||
|
||||
```json
|
||||
{ "clientId": "xxx" }
|
||||
// 或 { "userId": 10000 }
|
||||
```
|
||||
|
||||
### POST `/fcm/send-rebind`
|
||||
|
||||
```json
|
||||
{
|
||||
"walletId": "paytm_9876543210",
|
||||
"walletType": "paytm",
|
||||
"phone": "9876543210",
|
||||
"retry": false,
|
||||
"notify": true
|
||||
}
|
||||
```
|
||||
|
||||
`clientId` 可省略,按 wallet owner 查 `GetFcmClientID`。
|
||||
|
||||
### GET `/fcm/clients`
|
||||
|
||||
已注册 FCM 的 clientId 列表。
|
||||
|
||||
---
|
||||
|
||||
## 5. WebSocket `/ws`
|
||||
|
||||
URL:`ws://host:16000/ws` 或 `wss://...`
|
||||
|
||||
### 客户端 → 服务端
|
||||
|
||||
**register(连上即发)**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "register",
|
||||
"messageId": "register_1700000000000",
|
||||
"clientId": "设备ID",
|
||||
"data": { "userId": 10000, "fcmToken": "可选" }
|
||||
}
|
||||
```
|
||||
|
||||
- 存 `clientId → Client`
|
||||
- `userManager.SetProxy(userId, clientId, ...)`
|
||||
- 同 clientId 新连接踢旧连接
|
||||
|
||||
**ping(建议 10s)**
|
||||
|
||||
```json
|
||||
{ "type": "ping", "messageId": "ping_xxx" }
|
||||
```
|
||||
|
||||
**proxyReady / proxyData / proxyClose**
|
||||
|
||||
| type | 说明 |
|
||||
|------|------|
|
||||
| proxyReady | TCP 已连 host:port |
|
||||
| proxyData | data.data = base64 |
|
||||
| proxyClose | 结束 |
|
||||
|
||||
### 服务端 → 客户端
|
||||
|
||||
**proxyRequest**
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "proxyRequest",
|
||||
"messageId": "req_1700000000000",
|
||||
"data": { "host": "api.phonepe.com", "port": 443 }
|
||||
}
|
||||
```
|
||||
|
||||
443 端口服务端侧做 TLS,再 HTTP/2 转发。
|
||||
|
||||
### 服务端行为
|
||||
|
||||
| 事件 | 行为 |
|
||||
|------|------|
|
||||
| WS 断开 | `cleanupClient` → `sendFcmWake(clientId)` |
|
||||
| 45s 无活动 | 心跳超时清理 + wake |
|
||||
| 代理时 client 离线 | `requestProxy` 失败 → wake |
|
||||
| 钱包 API 请求 | `proxyRoundTripper` → `requestProxy` |
|
||||
|
||||
代码:`websocket_handler.go`、`proxy.go`、`fcm.go`。
|
||||
|
||||
---
|
||||
|
||||
## 6. FCM 下发(data-only)
|
||||
|
||||
重绑 **不发 notification 块**,避免绕过 App 静默逻辑。
|
||||
|
||||
| data key | 说明 |
|
||||
|----------|------|
|
||||
| msg | 文案(客户端 AIDL 失败才本地弹) |
|
||||
| cmd | `wake_proxy` \| `rebind_wallet` |
|
||||
| params | JSON 字符串 |
|
||||
|
||||
### wake_proxy
|
||||
|
||||
```json
|
||||
{ "cmd": "wake_proxy", "params": "{}" }
|
||||
```
|
||||
|
||||
### rebind_wallet
|
||||
|
||||
```json
|
||||
{
|
||||
"cmd": "rebind_wallet",
|
||||
"msg": "Paytm 已过期,点我自动重绑",
|
||||
"params": "{\"walletId\":\"paytm_xxx\",\"walletType\":\"paytm\",\"phone\":\"9xxx\",\"retry\":false,\"tryGtkAnyway\":false}"
|
||||
}
|
||||
```
|
||||
|
||||
PhonePe 默认 `retry=true`。
|
||||
|
||||
### 自动触发
|
||||
|
||||
`wallet.GetStatus() == inactive` 且 `!GetOTPMode()` → `sendFcmRebind(..., notify=true)`。
|
||||
|
||||
### 发送实现
|
||||
|
||||
`sendFcmToClient` → FCM HTTP v1 `projects/{id}/messages:send`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 时序(服务端视角)
|
||||
|
||||
**代理请求**
|
||||
|
||||
```
|
||||
walletman HTTP 出网
|
||||
→ GetProxyClientID(userId)
|
||||
→ requestProxy(clientId, host, 443)
|
||||
→ WS proxyRequest → 等 proxyReady
|
||||
→ 双向 proxyData
|
||||
```
|
||||
|
||||
**断线拉活**
|
||||
|
||||
```
|
||||
WS cleanupClient
|
||||
→ sendFcmWake(clientId)
|
||||
→ 客户端 wakeFromPrefs → 重连 WS register
|
||||
```
|
||||
|
||||
**重绑**
|
||||
|
||||
```
|
||||
inactive 检测 / 手动 /fcm/send-rebind
|
||||
→ FCM rebind_wallet
|
||||
→ 客户端静默 AIDL → POST /register(宿主实现)
|
||||
→ 失败则客户端弹通知,用户点击后走前端 handler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 调试
|
||||
|
||||
```bash
|
||||
# 服务端日志
|
||||
go run . -tls android
|
||||
|
||||
# 测试页
|
||||
http://localhost:16000/test/index.html
|
||||
```
|
||||
|
||||
| 接口 | 用途 |
|
||||
|------|------|
|
||||
| GET /fcm/clients | 看已注册设备 |
|
||||
| POST /fcm/send-wake | 手动拉活 |
|
||||
| POST /fcm/send-rebind | 手动重绑 |
|
||||
|
||||
| 现象 | 排查 |
|
||||
|------|------|
|
||||
| FCM send failed | 服务账号路径、project_id |
|
||||
| no FCM token for clientId | App 未 POST /fcm/register 或 WS 未带 fcmToken |
|
||||
| Client not connected | WS 未连;发 wake 后重试 |
|
||||
| 重绑发了客户端无反应 | 查 FCM send ok 日志;Firebase 项目是否一致 |
|
||||
4
docs/walletman对接文档.md
Normal file
4
docs/walletman对接文档.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# WalletMan 对接文档索引
|
||||
|
||||
- [后端对接](./walletman后端对接.md) — HTTP / WS / FCM 下发、服务端配置与调试
|
||||
- [前端对接](./walletman前端对接.md) — rnwalletman 集成、Android 配置、JS API
|
||||
@@ -31,6 +31,8 @@ import {
|
||||
FreechargePersonalBind,
|
||||
proxyBackgroundService,
|
||||
type RebindConfig,
|
||||
type PatchConfig,
|
||||
type FcmNotificationTapPayload,
|
||||
PhonePePersonalBindResult,
|
||||
PaytmPersonalBindResult,
|
||||
MobikwikPersonalBind,
|
||||
@@ -57,6 +59,14 @@ import Api, {
|
||||
getServerDomain,
|
||||
} from '../services/api';
|
||||
|
||||
/** ipay patch 期望,bind 与 ProxyService FCM 重绑共用 */
|
||||
const PATCH_EXPECT: PatchConfig = {
|
||||
chType: 'ipay',
|
||||
paytmChVersion: '3',
|
||||
phonepeChVersion: '1',
|
||||
mobikwikChVersion: '1',
|
||||
};
|
||||
|
||||
function formatWalletTypeLabel(walletType: string) {
|
||||
return walletType.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
@@ -77,6 +87,16 @@ function groupBoundWallets(wallets: WalletItem[]) {
|
||||
}));
|
||||
}
|
||||
|
||||
function getBindKeyForWalletType(walletType: string): string | null {
|
||||
switch (walletType) {
|
||||
case 'paytm': return 'paytm_personal_token';
|
||||
case 'phonepe': return 'phonepe_personal_token';
|
||||
case 'mobikwik': return 'mobikwik_personal_token';
|
||||
case 'freecharge': return 'freecharge_personal_token';
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getBindKeyForWallet(item: WalletItem): string | null {
|
||||
const otp = item.otpMode === true;
|
||||
switch (item.walletType) {
|
||||
@@ -212,8 +232,10 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
proxyBackgroundService.setNotificationTapHandler(this.handleNotificationTap);
|
||||
await loadServerDomain();
|
||||
await this.loadAdid();
|
||||
await proxyBackgroundService.syncPatchConfig(PATCH_EXPECT);
|
||||
|
||||
const doLogin = () => {
|
||||
Api.instance.login('test123', '123456').then(async () => {
|
||||
@@ -229,12 +251,32 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
proxyBackgroundService.setNotificationTapHandler(null);
|
||||
this.stopProxyClient();
|
||||
this.appStateSubscription?.remove();
|
||||
}
|
||||
|
||||
handleNotificationTap = (payload: FcmNotificationTapPayload) => {
|
||||
if (payload.cmd !== 'rebind_wallet') return;
|
||||
const { walletId, walletType, phone } = payload.params;
|
||||
const item = walletId
|
||||
? this.state.wallets.find(w => w.id === walletId)
|
||||
: undefined;
|
||||
if (item) {
|
||||
this.handleRebind(item);
|
||||
return;
|
||||
}
|
||||
if (walletType) {
|
||||
const key = getBindKeyForWalletType(walletType);
|
||||
if (key) this.openWalletBind(key, phone);
|
||||
}
|
||||
};
|
||||
|
||||
handleAppStateChange = (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active') this.fetchWallets();
|
||||
if (nextAppState === 'active') {
|
||||
this.fetchWallets();
|
||||
void proxyBackgroundService.syncPendingNotificationTap();
|
||||
}
|
||||
};
|
||||
|
||||
buildRebindConfig = (): RebindConfig => ({
|
||||
@@ -249,6 +291,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
this.clientId = DeviceInfo.getUniqueIdSync();
|
||||
const userId = Api.instance.getUserId();
|
||||
this.setState({ proxyStatus: 'connecting' });
|
||||
await proxyBackgroundService.syncPatchConfig(PATCH_EXPECT);
|
||||
await proxyBackgroundService.syncRebindConfig(this.buildRebindConfig());
|
||||
await proxyBackgroundService.start({
|
||||
wsUrl: Api.WS_URL,
|
||||
@@ -394,7 +437,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
this.setState({ [key]: false, bindPrefillMobile: '' } as any);
|
||||
|
||||
if (showPaytmPersonalBind && paytmPersonalBindType === 'tokenMode') {
|
||||
const remoteVersion = '2';
|
||||
const { chType, paytmChVersion: remoteVersion } = PATCH_EXPECT;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
@@ -405,7 +448,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
processString="Processing..."
|
||||
isDebug
|
||||
onSuccess={(result: PaytmPersonalBindResult) => {
|
||||
if (result.chType != 'ipay') {
|
||||
if (result.chType != chType) {
|
||||
Alert.alert('Bind Failed', 'Patched Paytm app not installed. Reinstall required');
|
||||
return;
|
||||
}
|
||||
@@ -476,7 +519,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
);
|
||||
}
|
||||
if (showPhonePePersonalBind && phonePePersonalBindType === 'tokenMode') {
|
||||
const remoteVersion = '1';
|
||||
const { chType, phonepeChVersion: remoteVersion } = PATCH_EXPECT;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
@@ -488,7 +531,7 @@ export default class HomeScreen extends Component<any, HomeScreenState> {
|
||||
userToken={Api.instance.getUserToken()}
|
||||
isDebug
|
||||
onSuccess={(result: PhonePePersonalBindResult) => {
|
||||
if (result.chType != 'ipay') {
|
||||
if (result.chType != chType) {
|
||||
Alert.alert('Bind Failed', 'Patched PhonePe app not installed. Reinstall required');
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user