feat(security): 三端闭环加固 + 微信提现打款/订阅消息落地

后端: 身份与归属鉴权收口; 资金安全(结算闸门/支付防篡改/报名费真实扣款/赠金/结算唯一入账/提现打款); 数据泄露与隐私; 登录与密钥安全(默认密钥闸门/登录限流); 可观测性; 启动自检.

微信: 商家转账到零钱提现打款闭环(含回调/查单/失败退回佣金); 订阅消息真实下发闭环与后台模板字段对齐.

后台: 财务/微信支付配置读取失败锁定; 看板/待办/走势去假数据; 打款与测试推送文案修正.

小程序: 请求层错误透传与登录过期提示; 多页失败反馈与乐观回滚; 帮助中心FAQ页面化.

测试: 新增 40+ 回归护栏与真实 MySQL 冒烟(75 套件/599 用例全绿).
这个提交包含在:
xcbyy
2026-10-17 18:00:09 +08:00
父节点 78117bb4e1
当前提交 2599dce315
共有 70 个文件被更改,包括 3806 次插入 和 330 次删除

查看文件

@@ -0,0 +1,76 @@
# 陪玩平台 安全与稳定性加固清单(本轮)
> 覆盖范围:后端 API(Express + MySQL)、后台(arco-design-pro-vite)、小程序(pwuniapp)。
> 交付形态:源码改动 + 回归测试(源码契约 + 真实 MySQL 行为冒烟 + 真实 HTTP 端到端鉴权冒烟)。
> 当前基线:后端全量测试 **69 套件 / 583 用例全绿**;`npm run build` 通过。
---
## 一、身份与归属鉴权(防越权/伪造身份)
- **用户会话 token**:资金/敏感写接口在生产环境 fail-closed(无有效 token 返回 401);非生产保留客户端身份以兼容本地联调。`resolveSecureUserId` 统一收口。
- **管理员特权**:仅认服务端 HMAC 签名 admin token(`isVerifiedAdminRequest`),严禁信任 `operatorRole`/`x-user-role`。
- **订单生命周期**:`complete/cancel/reject/apply-complete/refund/claim/pay` 均做归属校验(买家/承接大神/管理员)与状态闸门。
- **大神资料/服务/评价回复**:`PUT /companions/:id`、`/services/:id/status`、`/reviews/:id/reply` 仅本人或管理员。
- **其余写接口收口**:点赞、评论、举报、收藏、分销绑定、入驻申请、组局发起/解散、抢单、搭子上车、用户设置、个人资料、充值、提现、动态发布/删除、订单评价等全部以服务端会话身份为准。
- **读接口越权收口**:订单列表(本人相关)、我的组局、提现记录、财务流水、分销团队/明细、大神收益统计/走势、我的举报、收藏列表、上车申请名单、用户设置、管理端用户/风控/订单/财务/仲裁/评价/举报 等。
- **管理端命名空间统一鉴权**:`/api/admin`、`/api/dashboard`、`/api/finance`、`/api/arbitrations` → `requireAdmin`;逐路由补齐 `requireAdmin`(用户列表/删除、360画像、风控日志、场所增删改、订单超时扫描、评价下架等)。
## 二、资金安全(防套现/重复入账/篡改)
- **结算状态闸门**:`completeOrderWithSettlement` 拒绝 `待支付/待付款/待接单/已取消/已退款/已全额退款`。
- **订单初始状态服务端决定**:`createOrder` 按支付方式(BALANCE→履约中,其它→待付款),杜绝客户端自造"已支付/已完成"订单。
- **订单金额服务端权威**:单价按大神档案/服务包校验;金额=单价×时长−有效优惠券(服务端校验券),杜绝伪造低价。
- **支付防篡改**:
- 预支付订单场景金额以订单为准、仅买家可发起;
- 入账校验微信实收金额与支付意图一致(`amountMismatch`)。
- **充值赠金服务端按阶梯计算**:忽略客户端 `bonusAmount`,杜绝套取赠金(并修复 LIVE 充值此前不发赠金)。
- **拒单/申请完成状态闸门**:仅 `待接单` 可拒单;仅已履约订单可申请完成(防未付款→待确认→结算套现)。
- **买家取消订单**:`待支付/待付款`→已取消;`待接单`(已付款)→**原路退回买家余额**;已履约等→409 引导退款申诉。
- **组局报名费服务端权威并真实扣款**:AA 取活动人均、房主请客 0;退款/清算基于实付,杜绝"免费报名→退款/清算套现"。
- **房主改价**:金额校验(非负整数+上限)且仅按差额记账。
- **结算收款人唯一化**:移除 `id OR nickname` 模糊入账,按 `companions.user_id`(未绑定时按昵称唯一解析)精确入账,杜绝改名重复领钱。
- **提现**:金额/门槛校验 + 行锁 + 佣金余额校验,防超额与并发双花。
## 三、数据泄露与隐私
- **订单**:普通用户仅可取与自己相关订单;抢单池排除终态/已认领订单并脱敏客户名。
- **用户**:全量列表/360画像/风控日志仅管理员;用户详情对外仅公开字段(剥离手机号/余额/佣金/邀请关系)。
- **大神**:公开目录仅"已通过"且剥离内部审核备注;公开详情剥离 `rejectReason`。
- **组局**:详情仅返回**本人**核销码(他人剥离);列表不下发核销码。
- **核销码反查**:生产环境需登录且限门票归属方,防 4 位码枚举。
- **财务流水**:个人流水按 `user_id` 精确归属(移除 counterparty LIKE 模糊匹配)。
- **静态文件**:用户设置文件迁出对外可读的 `/uploads` 目录。
## 四、登录与密钥安全
- **管理端登录**:失败限流(IP+账号,5 次/10 分钟)+ 常量时间口令比较。
- **生产默认密钥闸门**:若未配置 `ADMIN_TOKEN_SECRET`,生产环境改用进程内随机密钥(默认密钥签发的 token 一律失效)并告警。
- **手机号登录**:优先按微信 `openid` 精确匹配;联系方式唯一性校验,防登录身份碰撞。
- **jscode2session**:不再返回 `session_key`;openid 绑定需会话一致(防账号劫持)。
- **上传**:生产环境要求登录。
## 五、可观测性与失败反馈
- **后端**:结算/支付/退款/充值/提现/openid 持久化等关键路径空 `catch` 改为 `logServerError`;看板经营指标改为真实聚合(移除硬编码假数据);分销团队/返佣明细移除硬编码假数据;收益统计只按 `expert_id`(去跨大神串算)。
- **小程序请求层**:错误附带 `code`/`statusCode`;401/40100 清理过期会话并提示。
- **小程序"假成功"清理**:取消订单、确认验收、抢单、拒单、提交完成、接单状态切换、社区删除/上车申请、点赞、设置同步 等失败均**不再伪装成功**(并回滚乐观状态)。
- **后台**:财务/微信支付配置读取失败锁定保存(防默认值覆盖线上)、看板与待办失败显式告警、走势图去假数据。
---
## 六、部署与配置注意事项(重要)
1. **必需环境变量(生产)**
- `ADMIN_TOKEN_SECRET=<强随机长串>`
- `USER_TOKEN_SECRET=<另一强随机长串>`(建议单独设置)
- 未设置时本方案会每次重启更换随机密钥:后台/小程序登录态会失效,且**多实例/集群必须设置固定密钥**。
2. **微信支付 LIVE**
- 后台填齐 `mchId` / `apiV3Key`(32位) / `serialNo` / `privateKey`(或 `certPem`)/ 平台公钥或平台证书;`mode=LIVE` 或留空(AUTO)。
- 商户平台配置 `notifyUrl`(默认 `https://peiww.gynhc.com/api/pay/wechat/notify`),确保 Nginx 放行、公网 HTTPS 可达。
- 生产设 `NODE_ENV=production`;生产默认禁用沙箱直接入账(除非显式 `ALLOW_SANDBOX_PAY=1`)。
- 建议先真实支付 1 分钱跑通:`prepay → 支付 → 回调/查单 → 到账 → 重复请求不重复加钱`。
3. **内容安全**:如需微信真实审核,后台填真实小程序 `appId` 与 32 位 `appSecret`;未配置时自动跳过远端、仅跑本地词库。
4. **数据订正(可选)**:历史遗留的 `finance_records.user_id=0` 旧流水不再展示;如需回填,可另跑一次性订正脚本。
> 本轮改动无破坏性数据库结构变更(仅运行时幂等 DDL 与既有字段使用)。

查看文件

@@ -0,0 +1,118 @@
# 部署验收与「1 分钱支付」闭环步骤
> 目标:在生产环境完成配置自检,并以真实微信支付跑通「下单 → 支付 → 到账 → 幂等」闭环。
## 一、后端环境变量(生产)
在启动后端的环境(pm2/systemd/.env,注意 dotenv 不覆盖已存在的系统环境变量)中设置:
```
NODE_ENV=production
PORT=3000
# 会话/管理端签名密钥(务必设置强随机长串)
ADMIN_TOKEN_SECRET=<强随机长串>
USER_TOKEN_SECRET=<另一个强随机长串>
# 数据库
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=...
MYSQL_PASSWORD=...
MYSQL_DATABASE=peiwan
# 可选:微信(若已在后台"微信支付配置"页填写,则无需在这里重复;后台配置优先)
# WX_APP_ID=...
# WX_APP_SECRET=...
# WX_MCH_ID=...
# WX_API_V3_KEY=...
# WX_SERIAL_NO=...
# WX_PRIVATE_KEY=...
```
> ⚠️ 未设置 `ADMIN_TOKEN_SECRET` 时,生产会启用**进程内随机密钥**:后台/小程序登录态在每次重启后失效;多实例部署会出现实例间登录态不一致。**务必设置固定密钥。**
## 二、查看启动自检日志(部署后第一时间确认)
后端启动会打印(不含任何密钥明文):
```
[Peiwan Config] 运行模式=production ALLOW_SANDBOX_PAY=false
[Peiwan Config] ADMIN_TOKEN_SECRET=已设置 USER_TOKEN_SECRET=已设置
[Peiwan Config] 微信支付: 模式=LIVE 实盘就绪=是 notifyUrl=https://peiww.gynhc.com/api/pay/wechat/notify
```
- 若出现 `[WARN] 微信支付未达 LIVE 就绪,待确认项: ...`,按提示补齐后在后台"微信支付配置"页保存。
- 若出现 `[WARN] 未设置 ADMIN_TOKEN_SECRET ...`,请立即补上环境变量并重启。
## 三、微信支付商户配置(后台)
登录后台 → 财务与分销配置 → 微信支付商户号与 APIv3 证书配置:
1. 填写 `appId`、`appSecret`(32位)、`mchId`、`apiV3Key`(32位)、`serialNo`、`privateKey`(或上传 `certPem`)、平台公钥或平台证书序列号。
2. 确认 `notifyUrl = https://peiww.gynhc.com/api/pay/wechat/notify`,并在微信商户平台配置该回调地址。
3. 保存后点击「配置自检与签名验证」,确认状态标签为 **LIVE**。
确保:
- Nginx 已放行 `/api/pay/wechat/notify`,公网 HTTPS 可达、不拦截微信回调(无鉴权、可被外网 POST)。
- 校验 `apiV3Key` 为 32 位;`serialNo` 与上传的商户证书匹配。
## 四、「1 分钱」支付闭环验收
在微信开发者工具/真机(或 H5)执行:
1. **下单**:下单任意订单(或充值),进入收银台。后台日志可见 `/api/pay/wechat/prepay` 调用(`mode=LIVE`)。
- 预期:返回真实 JSAPI 支付参数并拉起微信支付。
2. **支付**:完成(可用 1 分钱金额的商品/充值档)。
3. **到账(回调)**:微信异步回调 `/api/pay/wechat/notify`。
- 预期:订单状态流转为「履约中」;或钱包充值到账(余额/流水更新)。
4. **主动查单兜底**:若回调延迟,前端"确认支付结果"会触发服务端主动查单(3 次重试)。
5. **幂等**:重复点击支付结果/重复回调,**不得重复加钱/重复结算**。
- 预期:`payment_intents` 幂等(`status=SUCCESS` 后不再入账);重复回调返回成功但不二次入账。
6. **金额一致性**:人为构造金额不一致(仅测试环境)应被拒绝(日志 `[SERVER_ERROR][applyPaymentSuccess.amountMismatch]`)。
验收通过判据:
- 后台订单/资金流水正确记录;
- 钱包/余额变化与实付一致;
- 重复请求不产生重复入账;
- 生产环境未登录不能触发资金操作(HTTP 401/50008)。
## 五、提现打款(微信商家转账到零钱)配置与验收
> 重要:提现审核"通过"会**真实发起微信商家转账到零钱**。前置条件:
> 1. 商户号已开通**"商家转账到零钱"**产品(微信商户平台申请,通常需 1-2 个工作日);
> 2. 在商户平台配置**转账场景ID**(如"佣金报酬"场景);
> 3. 收款大神需有 **openid**(通过微信登录授权产生,`users.wechat_openid`)。
后台配置(财务与分销配置 → 微信支付商户号与 APIv3 证书配置)新增:
- **转账场景ID (`transfer_scene_id`)**:必填才视为"打款就绪";
- **转账备注 (`transfer_remark`)**:默认"佣金提现";
- **收款人姓名加密**:部分转账场景要求提交收款人姓名,启用后按平台公钥加密提交(需已配置平台公钥);
- **转账结果回调地址 (`transfer_notify_url`)**:默认 `https://peiww.gynhc.com/api/pay/wechat/transfer-notify`。
行为:
- 支付模式为 **LIVE 且"打款就绪"**:审核通过即调用微信商家转账;受理后工单置"处理中",最终由**回调/查单**确认 → 成功置"已打款"、失败置"打款失败"并**自动退回预扣佣金**。
- 若该大神**缺少 openid**:打款被拒并提示"请其重新微信登录授权"。
- **生产环境未达"打款就绪"**:审核通过会被**拒绝(503)**,不会出现"只改状态、不发钱"的误导(联调环境保留标记已打款)。
- 启动自检会打印:`商家转账到零钱(提现打款): 就绪=是/否 转账场景ID=已配置/未配置`。
验收:提现 1 元 → 后台通过 → 观察工单由"处理中"→"已打款"、大神微信零钱到账;人为失败(如场景ID 错误)应置"打款失败"并退回佣金。
## 六、微信订阅消息(服务通知)配置与验收
后台「平台配置与协议规则 → 微信服务通知与模板」维护:总开关 `enablePush` + 4 类模板ID(订单状态 / 组局报名 / 提现到账 / 核销码)。
接入说明:
- **模板创建**:在微信公众平台「订阅消息」创建 4 个模板,把真实模板ID填入后台。各模板的字段(如 `phrase1`/`thing2`/`time3`/`character_string1`/`amount1`)**需与代码下发时的 data 键一致**(不一致会返回 47003 等错误,日志可见)。若你的模板字段名不同,把字段占位符发我,我按模板对齐 `data` 键。
- **触发点**:① 订单进入"履约中"→ 给买家下发订单状态 + 4位核销码通知;② 组局报名成功 → 给报名者下发通知;③ 提现"打款成功"(受理/回调确认到账)→ 给提现大神下达到账通知。
- **就绪条件**:`enablePush=1` + 对应模板ID已填 + 小程序 `appId/appSecret` 已配置 + 目标用户有 `openid`。任一缺失则静默跳过(不阻塞业务)。
- **后台测试推送**:管理端"测试推送"在就绪时**真实下发**;未就绪时返回模拟结果并说明原因(mode=REAL/SIMULATED)。目标用户填其 UID,且该用户须有 openid。
- **小程序授权**:设置页开启通知时用**真实模板ID** 调 `wx.requestSubscribeMessage` 收集授权。订阅为**一次性**,需在关键动作前再次请求(后续可在下单/报名/提现前补请求)。
- **启动自检**:不额外打印;如需可在后台测试推送处验证。
## 七、内容安全(可选)
后台填入真实小程序 `appId` 与 32 位 `appSecret` 后,内容检测会真实调用微信 `msgSecCheck`;未配置时自动跳过远端、仅跑本地敏感词库(fail-open,不影响业务)。
## 八、回滚
本轮改动遵循"边界内最小改动",无破坏性数据库结构变更。如需回滚,按部署前的文件/库备份还原即可。