diff --git a/docs/walletman前端对接.md b/docs/walletman前端对接.md index 559a3af..3f29a7c 100644 --- a/docs/walletman前端对接.md +++ b/docs/walletman前端对接.md @@ -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 模式(Personal,ipay 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 负责拉起 App;prefs 解决 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 版本与服务端/文档一致 diff --git a/docs/walletman后端对接.md b/docs/walletman后端对接.md index 45f662a..059d690 100644 --- a/docs/walletman后端对接.md +++ b/docs/walletman后端对接.md @@ -1,25 +1,23 @@ # WalletMan 后端对接文档 -> 前端对接见 [walletman前端对接.md](./walletman前端对接.md) - -Android 宿主通过 **HTTP + WebSocket + FCM** 与 walletman 服务端通信。服务端默认 `:16000`。 +> 前端集成见 [walletman前端对接.md](./walletman前端对接.md) --- ## 1. 架构 ``` -Android 宿主 ◄── HTTP/WS ──► walletman :16000 ◄── 代理 TCP ──► 钱包 API - ◄── FCM data-only ──► Firebase +Android 宿主 ◄── HTTP / WS ──► walletman (:16000 或反代) ◄── 代理 TCP ──► 钱包 API + ◄── FCM data ──► Firebase ``` | 链路 | 用途 | |------|------| -| HTTP REST | 登录、绑钱包、业务 | +| HTTP REST | 登录、绑钱包、查流水等业务 | | 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 ``` -测试页:`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 ``` -或 `FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App `google-services.json` **同一 Firebase 项目**。 +或环境变量 `FCM_SERVICE_ACCOUNT=/path/to/json`。须与宿主 App 的 `google-services.json` **同一 Firebase 项目**。 --- ## 3. 通用约定 -**响应:** +**响应格式:** ```json { "success": true, "message": "说明", "data": {} } @@ -72,7 +72,7 @@ X-User-ID: 10000 ### POST `/register` -Token 绑钱包。 +Token 模式绑钱包 / 静默重绑落库。 ```json { @@ -81,7 +81,7 @@ Token 绑钱包。 "type": "paytm", "token": "...", "refreshToken": "...", - "mobile": "9xxxxxxxxx", + "mobile": "9876543210", "deviceId": "...", "chType": "ipay", "chVersion": "3" @@ -91,16 +91,30 @@ Token 绑钱包。 ```json // data -{ "walletId": "paytm_9xxxxxxxxx", "walletType": "paytm" } +{ "walletId": "paytm_9876543210", "walletType": "paytm" } ``` `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` -OTP 绑钱包,见 `http_handler.go`。 +OTP 绑钱包。重绑场景若钱包已存在,会复用已有 params(保留 deviceId 等),见 `requestOTPHTTP`。 ### GET `/wallets` @@ -117,7 +131,7 @@ OTP 绑钱包,见 `http_handler.go`。 } ``` -`inactive` + 非 OTP → 可能触发 FCM 重绑(`maybeNotifyWalletRebind`)。 +`status=inactive` 且 `otpMode=false` 时,可能触发 FCM 重绑(见 §6)。 ### POST `/fcm/register` @@ -125,15 +139,18 @@ OTP 绑钱包,见 `http_handler.go`。 { "clientId": "设备ID", "fcmToken": "..." } ``` -与 WS `register.data.fcmToken` 等价,均 `storeFcmToken`。 +与 WS `register.data.fcmToken` 等价,均写入 FCM token 存储。 ### POST `/fcm/send-wake` ```json { "clientId": "xxx" } -// 或 { "userId": 10000 } +// 或 +{ "userId": 10000 } ``` +下发 `wake_proxy`,客户端重连 WS。 + ### POST `/fcm/send-rebind` ```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` 已注册 FCM 的 clientId 列表。 +### POST `/wallet/set-status` + +手动改钱包状态;设为 inactive 时会 `maybeNotifyWalletRebind`。 + --- ## 5. WebSocket `/ws` -URL:`ws://host:16000/ws` 或 `wss://...` +URL:`ws://host:16000/ws` 或 `wss://host/ws`(反代) ### 客户端 → 服务端 -**register(连上即发)** +**register(连接后立即发送)** ```json { @@ -171,7 +196,7 @@ URL:`ws://host:16000/ws` 或 `wss://...` } ``` -- 存 `clientId → Client` +- 绑定 `clientId → Client` - `userManager.SetProxy(userId, clientId, ...)` - 同 clientId 新连接踢旧连接 @@ -186,8 +211,8 @@ URL:`ws://host:16000/ws` 或 `wss://...` | type | 说明 | |------|------| | proxyReady | TCP 已连 host:port | -| proxyData | data.data = base64 | -| proxyClose | 结束 | +| proxyData | `data.data` = base64 | +| 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)` | | 45s 无活动 | 心跳超时清理 + 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 | 说明 | |----------|------| -| msg | 文案(客户端 AIDL 失败才本地弹) | -| cmd | `wake_proxy` \| `rebind_wallet` | -| params | JSON 字符串 | +| `msg` | 通知文案(`notify=true` 时有值) | +| `cmd` | `wake_proxy` \| `rebind_wallet` | +| `params` | JSON 字符串 | ### wake_proxy @@ -238,15 +263,26 @@ URL:`ws://host:16000/ws` 或 `wss://...` { "cmd": "rebind_wallet", "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 - → FCM rebind_wallet - → 客户端静默 AIDL → POST /register(宿主实现) - → 失败则客户端弹通知,用户点击后走前端 handler +inactive 检测 / POST /fcm/send-rebind + → FCM rebind_wallet(data.msg + params) + → 客户端静默 AIDL → registerWallet → POST /register + → 失败 → 客户端本地通知 → 用户点击 → JS handler 打开 Bind ``` --- ## 8. 调试 -```bash -# 服务端日志 -go run . -tls android - -# 测试页 -http://localhost:16000/test/index.html -``` - -| 接口 | 用途 | +| 方式 | 说明 | |------|------| -| GET /fcm/clients | 看已注册设备 | -| POST /fcm/send-wake | 手动拉活 | -| POST /fcm/send-rebind | 手动重绑 | +| `http://localhost:16000/test/index.html` | 发 wake / rebind、看 WS | +| 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 项目是否一致 | +| no FCM token for clientId | App 未 POST `/fcm/register` 且 WS 未带 fcmToken | +| Client not connected | WS 未连;先发 wake | +| 重绑发了客户端无反应 | Firebase 项目是否与 App 一致;logcat `ProxyFcmService` | diff --git a/docs/walletman对接文档.md b/docs/walletman对接文档.md index 3e9cd74..abff675 100644 --- a/docs/walletman对接文档.md +++ b/docs/walletman对接文档.md @@ -1,4 +1,40 @@ -# WalletMan 对接文档索引 +# WalletMan 对接文档 -- [后端对接](./walletman后端对接.md) — HTTP / WS / FCM 下发、服务端配置与调试 -- [前端对接](./walletman前端对接.md) — rnwalletman 集成、Android 配置、JS API +Android 宿主 App 通过 **HTTP + WebSocket + FCM** 连接 walletman 服务端,完成钱包绑定、TCP 代理出网、token 过期静默重绑。 + +## 文档索引 + +| 文档 | 读者 | 内容 | +|------|------|------| +| [后端对接](./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 / 绑钱包 / 通知点击示例 |