Files
biz-api-cloud-zax/pjcloud-thirdparty/docs/aggregate-h5.md
T
2026-10-28 18:49:22 +08:00

109 lines
14 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.
# 双场景聚合支付部署与联调
## 配置
在 third 服务的 Nacos 配置中增加以下配置。H5 域名由配置提供,不编入 Java 源码;签名密钥需要由部署环境注入至少 32 字节的随机值,不要使用本文或测试中的字符串。
```yaml
app-plus:
pay:
# 网页/移动应用:聚合支付宝 WAP 签名及付款通知;原 App 继续使用此组。
ali-pay-config:
app-id: ${ALIPAY_WEB_APP_ID}
app-private-key: ${ALIPAY_WEB_APP_PRIVATE_KEY}
alipay-public-key: ${ALIPAY_WEB_PUBLIC_KEY}
base-callback-url: ${THIRD_PUBLIC_BASE_URL} # 网关公网地址,包含实际 /api 子路径
pay-callback-url: /third/third-app-pay/app/pay-callback
refund-callback-url: /third/third-app-pay/app/refund-callback
# 小程序及历史小程序交易凭据:当前 H5 新下单不读取此组;已有相关流水时保留原凭据。
ali-pay-mini-config:
app-id: ${ALIPAY_MINI_APP_ID}
app-private-key: ${ALIPAY_MINI_APP_PRIVATE_KEY}
alipay-public-key: ${ALIPAY_MINI_PUBLIC_KEY}
base-callback-url: ${THIRD_PUBLIC_BASE_URL}
pay-callback-url: /third/third-app-pay/app/pay-callback
refund-callback-url: /third/third-app-pay/app/refund-callback
# 第三方应用独立预留,当前不参与 App/聚合新下单;不用时整组省略。
# ali-pay-third-party-config:
# app-id: ${ALIPAY_THIRD_PARTY_APP_ID}
# app-private-key: ${ALIPAY_THIRD_PARTY_APP_PRIVATE_KEY}
# alipay-public-key: ${ALIPAY_THIRD_PARTY_PUBLIC_KEY}
# base-callback-url: ${THIRD_PUBLIC_BASE_URL}
# pay-callback-url: /third/third-app-pay/app/pay-callback
# refund-callback-url: /third/third-app-pay/app/refund-callback
aggregate:
h5-base-url: https://baac9a029ea84f36ba28c023fed4fc5f.hd.takin.cc
public-route-prefix: /third # 直接使用 Boot 服务时设为 ""
merchant-signing-key: ${MERCHANT_COLLECTION_SIGNING_KEY}
offline-max-amount: 999999 # 整数分,9,999.99 元;提高上限时同时调整 H5 的输入限制
query-limit-per-minute: 120
create-limit-per-minute: 30
trusted-proxy-addresses: []
```
`h5-base-url` 只接受 HTTPS 基础地址,不能带查询参数、Hash 或用户信息。可以包含实际部署子路径;H5 的路由 base 需对应配置。
支付宝按网页/移动应用、小程序、第三方应用分类保存配套 AppID、应用私钥和支付宝公钥。新支付宝聚合付款使用现有 `ali-pay-config` 的 `alipay.trade.wap.pay` 手机网站支付能力,产品码为 `QUICK_WAP_WAY`,不再先取网页 OAuth 身份,也不调用 `alipay.trade.create`。网页/移动应用需要已开通手机网站支付及实际直付通商户收单权限,切换凭据不会自动开通产品。没有新增 `ali-pay-h5-config`。原 App 默认配置与 `app.pay` 路径保持兼容。聚合支付宝旧 OAuth、`alipay.trade.create` 和 H5 `tradeNO` 调起代码已删除。
新支付单在已有 `mch_pay_setting_json` 中保存网页应用的非敏感 `alipayAppId`,查单、关单、退款、退款查询及结算/分账按这个 AppID 选择原应用;支付与退款回调先按已登记 AppID 验签,再校验是否与支付单一致。历史小程序流水仍按其快照选择原小程序凭据;旧快照没有该标识时继续使用原网页/移动应用。只启用微信的商户不要求支付宝配置。更新凭据或切换下单应用后需重启 third 的全部实例。本次不增加数据库字段,不改写历史订单。
仅在网关/反向代理**覆盖客户端传入的 X-Real-IP**时,将其直连地址加入 `trusted-proxy-addresses`。默认使用直连 IP 限流。Redis 业务键继续由项目基础设施附加租户前缀,不手工拼接租户编号。
H5 只使用项目根目录一个 `.env`,变量命名沿用参考 App:开发请求走 `VITE_API_URL`(默认 `/api`),由 Vite 代理到 `VITE_APP_DEV_SERVICE`;生产读取 `VITE_APP_PROD_SERVICE`。这些后端地址与 H5 页面地址分别配置,不重复附加 `/third`。`VITE_IS_MICRO=true` 保留 `/third`;`false` 移除前缀,此时后端 `public-route-prefix` 同时设为 `""`,确保二维码、OAuth 回调与状态地址也使用 Boot 路径。若线上部署已提供同源接口,生产后端地址可留空。本次未修改实际 Nacos、网关部署或平台控制台。
后端仅为配置的 H5 精确 Origin 开放匿名商户、创建、上下文和状态 JSON 接口的跨域访问,不开放凭据跨域;其他接口保持既有安全规则。
## 平台配置
- 微信沿用服务商统一公众号 OAuth 与 JSAPI 下单配置,确认公众号网页授权域名对应现有后端 OAuth 回调域名。
- 将新 H5 的实际页面目录核对加入微信支付 JSAPI 支付授权目录;Hash 部分不作为目录填写。当前根目录部署对应 `https://baac9a029ea84f36ba28c023fed4fc5f.hd.takin.cc/`。
- 支付宝新付款使用网页/移动应用组 `ali-pay-config` 的手机网站支付产品及直付通权限,异步支付和退款通知也读取此组。核对 `base-callback-url + pay-callback-url` 指向公网可达的原验签通知接口。
- WAP 支付完成或退出地址为服务端生成的 `base-callback-url + public-route-prefix + /aggregate-order-pay/return/alipay/{tenantCode}/{ticket}`。这一入口只做固定 302,回到同一 H5 票据并附加 `returned=1`;不能把它填作异步通知,更不能凭浏览器回跳参数判断付款成功。按当前应用平台要求配置实际网站域名及返回地址。
- 聚合支付宝不再提供 `/third/aggregate-order-pay/oauth/alipay` 授权回调,也不读取买家 OAuth 身份;微信 OAuth/JSAPI 保持原路径与语义。
- 两种渠道继续使用商户既有支付配置、二级商户和默认分佣关系快照,不因新增 WAP 路径改变收款方或结算归属。
### 支付宝手机网站支付流程
1. 用户在支付宝扫码后进入商户 H5,输入金额或查看订单固定金额。原聚合 `/cashier/{tenantCode}/{token}` 入口识别支付宝客户端,对新会话使用无 OAuth 的 WAP 平台绑定;同一会话的 CAS 只分配一个支付单号,不伪造付款人 ID。已绑定付款人摘要的历史支付宝会话只签发只读结果票据,不创建第二笔 WAP,也不再输出旧 tradeNO 调起参数。
2. 后端通过 SDK `pageExecute(request, "GET")` 本地签名 `alipay.trade.wap.pay`,先保存本地流水及请求/调起快照,再交付收银票据。完整签名地址使用现有统一加密器保存,仅在短期票据上下文中解密返回,避免 SQL 日志和后台流水明文泄漏。这一步不会远程创建支付宝交易,不返回业务 HTML。H5 的 `launchParams` 为 `{ mode: "ALIPAY_WAP", payUrl: "https://openapi.alipay.com/gateway.do?..." }`;微信调起字段保持兼容,支付宝不再接受 `tradeNO` 旧调起模式。
3. H5 首次进入票据页后只自动调起一次,严格校验官方网关的协议、主机、路径和 API,再导航支付宝收银台。签名 URL 只保存在内存,不写入二维码、H5 Hash 或浏览器恢复存储。手动继续会先重新读取上下文,确认原单仍可支付。
4. 支付完成、退出或浏览器返回后查询原单,返回提示只用于抑制重复自动导航;成功仍由验签通知或后端查单确认。网络失败、取消、刷新均保留金额和 requestId,不能新建第二个可支付请求。
WAP 的直付通结算参数沿用原 App 路径:`sub_merchant.merchant_id`、`settle_info.settle_period_time=1d` 及交易全额的 `defaultSettle` 明细,金额为元,`trans_in` 留空;仅开启平台抽佣且商户配置了代调用授权时携带 `app_auth_token`。关闭抽佣也必须保留二级商户和全额结算参数。签名日志仅记录配置组、AppID、API、产品码和业务支付单号,不记录签名 URL、授权令牌或用户身份。
会话和签名请求共用原两分钟绝对截止时间。WAP 使用 `time_expire`,不使用最小五分钟的 `timeout_express`;首次签名余期不足一分钟时拒绝请求,不能续期。有效期内查无此单只表示用户尚未访问网关,不能确认关闭。到期且支付宝明确返回 `ACQ.TRADE_NOT_EXIST` 才可按未建单结束;网络、权限及未知错误继续阻止新交易。已存在的 WAP 未支付交易使用 `alipay.trade.close`,关单失败再次查单以处理付款竞态,避免用 `cancel` 自动退款已成功交易。历史 JSAPI 的处理保持原逻辑。
回调快照分类同时接受 `QUICK_WAP_WAY`、历史 `JSAPI_PAY` 和 `FACE_TO_FACE_PAYMENT`,仍严格验证原应用、会话、业务、商户和金额。WAP 按到期查无单本地关闭时,在原响应 JSON 中记录 `wapClosedWithoutTrade=true`;只有这一类本地关闭允许迟到的已验真成功通知原子恢复成功,渠道真实关闭、退款等终态不能据此恢复。原 App 不改用 WAP,退款和分账仍按原交易应用及商户关系处理。
参考:[手机网站支付接入流程](https://developer.alibaba.com/docs/doc.htm?articleId=105285&docType=1&treeId=193)、[手机网站支付产品及辅助接口](https://developer.alibaba.com/docs/doc.htm?articleId=105288&docType=1&treeId=203)。签名请求不代表已付款,实际权限、直付通结算及微信/支付宝真机行为仍须集中联调确认。
## 发布顺序与兼容
1. 先确认统一支付基线迁移 `db/20260825_third_payment_schema.sql` 已完成;已完成的环境无需重跑。双场景升级单独执行新文件 [`db/20261020_add_aggregate_pay_scene.sql`](../../db/20261020_add_aggregate_pay_scene.sql),不得只靠修改历史迁移升级现有库。
2. 新迁移仅新增两个 `aggregate_scene` 字段,先查字段是否存在,可重复执行;不包含存储过程、字典写入或历史流水回填。业务类型字典 `70 = 商户线下收款` 可通过管理端配置。
3. 配置 Nacos 的 H5 地址与随机签名密钥,配套发布 third、wallet 与 H5,再发布后台页面。完整测试与构建由集中验证指令执行。
4. 商户至少启用并配置完成微信或支付宝其中一个渠道后,即可通过原刷新二维码操作生成新的 URL 收款码。允许仅微信、仅支付宝或双渠道;只校验已启用渠道。新增商户仍先生成原 App Base64 码;没有可用渠道时不生成 H5 码。手动刷新失败会明确反馈并保留原图。
5. 消费机原 `AGGREGATE_ORDER_PAY` 类型、二维码路径及内部创建/状态接口保持兼容,OAuth/WAP 收银出口均为 302 到新 H5。已删除无人调用的旧 HTML 收银渲染器及其支付宝 JSAPI 专用测试,Controller 统一 302 到独立 H5。
数据库字段分别为:聚合会话 `aggregate_scene varchar(32) NOT NULL DEFAULT 'ORDER'`,支付流水 `aggregate_scene varchar(32) DEFAULT NULL`。线下业务编码是 `70`,收款请求号由商户和稳定 requestId 派生;不会创建硬件、商城或钱包充值业务订单。
历史聚合会话通过新字段默认值标记为 `ORDER`;历史支付流水的新字段保持 `NULL`,本次不做数据回填。
静态 URL 收款码长期有效,HMAC 绑定用途、版本、租户和商户;更换域名或密钥后需重新生成。`collectionCode` 继续保留 App 协议,`collectionUrlCode` 仍保存二维码图片地址。
## 接口与故障恢复
### 历史支付宝交易
移除旧下单入口不删除历史支付流水、应用快照或小程序凭据。`JSAPI_PAY`、`FACE_TO_FACE_PAYMENT` 等已有聚合流水仍参与严格回调验签、金额/商户/会话匹配,并按原应用进行查单、撤销、退款和分账。旧会话不会改为新 WAP 交易,也不会因删除创建接口而直接写成功或关闭。历史支付宝 OAuth 回调地址不再对外提供,新聚合付款无需网页授权。
- 匿名查询:`GET /third/aggregate-merchant-pay/merchant?id=...&tenantId=...&v=1&sign=...`,返回商户 id/name/logoPic 和 `supportedPlatforms`(10 微信、20 支付宝),不返回支付配置。
- 线下创建:`POST /third/aggregate-merchant-pay/create`,提交相同签名参数、整数分 amount 和 UUID requestId。重试不能修改金额或生成新 requestId。
- 现有聚合入口:`/third/aggregate-order-pay/cashier/{tenantCode}/{token}`。支付宝新会话进入 WAP,微信继续 OAuth,历史已绑定身份的支付宝会话仅查询原支付结果;已确认终态的旧请求直接恢复结果页,不再下单。
- 收银回跳只携带短期 ticket(WAP 返回额外带无状态语义的 `returned=1`),H5 用 `/context/{tenantCode}/{ticket}` 读取最小渠道参数,用 `/status/{tenantCode}/{ticket}` 确认结果。票据有效期沿用 5 分钟,付款会话沿用 2 分钟;本机倒计时不能代替服务端查单/关单结果。
- 取消付款可继续原单;客户端返回成功、未知或网络异常时仍等待服务端确认,不能自行写成功或新建另一笔交易。
- 缺少 H5 配置时不输出无签名链接;收银入口返回无敏感正文的不可用响应。配置齐全后失败跳固定 H5 错误入口,不反射平台原始异常。
- H5 扫码客户端不在商户已启用渠道中时,展示专门的提示状态并引导换用已开通 App 扫码,隐藏金额和付款键盘;渠道全部停用时提示联系商户。付款创建和进入渠道收银前再次检查渠道,前端展示不能替代后端校验。已有渠道子订单继续依据原绑定渠道和配置快照恢复。
本轮未执行实际迁移、修改平台配置或发起真实扣款。集中验证需覆盖微信/支付宝真机、退款和分账、回调验签与重复通知、延迟成功、限流、跨租户及旧消费机状态同步。