# 批量付款到户有密 接入设计 日期:2026-08-25 状态:已与用户确认(2026-08-25) ## 1. 背景与目标 将支付宝邀测产品「**批量付款到户有密**」(批量转账到支付宝账户)接入 xjz-payment-platform。 **业务目标**:赋能企业客户批量付款 —— 平台代企业制单,企业财务在自己的支付宝端内输入一次支付密码确认,批量付款给外部支付宝账号(发薪、报销、佣金、供应商结算等)。资金**不经过平台账户**,付款方(企业)掌控资金,安全性高于现有记账本直付链路。 **与现有体系的关系**:现有资金链路是「支付宝安全发」(签约→进件→开通记账本→充值→单笔转账),与本产品是**两个独立产品**,并行共存,互不影响。 **接入角色**:付款方仅限平台入驻的企业客户(企业码场景);平台以企业绑定的服务商 AppID 调用 API(沿用现有 `AlipayClientFactory.getClient(enterpriseId, bizType)` 模式,资金接口证书加签)。 **操作渠道**:PC 网页版(制单授权 channel=pc、支付页 PC 版),每批最多 1000 笔。 **收款方来源**:仅外部支付宝账号(手填或 Excel 导入),不关联平台员工体系。 ## 2. 产品关键约束(来自支付宝邀测文档) ### 2.1 API 清单 | 环节 | 接口 | 用途 | |---|---|---| | 制单授权 | `alipay.fund.authorize.uni.apply` | 生成授权短链接(PC/小程序),付款方端内授权 | | 授权查询 | `alipay.fund.authorize.uni.query` | 查询单一协议授权状态 | | 授权通知 | `alipay.fund.authorize.status.notify` | 签约/解约异步通知(订阅 from 蚂蚁消息) | | 批次下单 | `alipay.fund.batch.create` | 创建批量付款单据 | | 渲染支付 | `alipay.fund.trans.render.pay` | 生成 PC/小程序支付页,付款方输密码完成 | | 批次通知 | `alipay.fund.batch.order.changed` | 批次状态异步通知 | | 批次查询 | `alipay.fund.batch.detail.query` | 批次+明细状态查询(兜底) | | 批次关单 | `alipay.fund.batch.close` | 主动关闭未支付批次 | | 电子回单(**二期**) | `alipay.data.bill.ereceiptagent.apply` / `alipay.data.bill.accountbookereceipt.query` | 申请/查询回单,本期不做 ### 2.2 关键参数 **授权申请(uni.apply)**: - `product_code` = `TRANSFER_API_STANDARD_AUTHORIZATION`(固定) - `biz_scene` = `STANDARD_CREATE_FUND_ORDER`(固定) - `authorize_link_type` = `SHORT_URL`(固定) - `channel` = `pc`(PC 网页版) - `principal_info.participant_id` = 付款方支付宝 uid;`participant_id_type` = `ALIPAY_USER_ID` - 出参:`authorize_link`(短链接) **批次下单(batch.create)**: - `product_code` = `BATCH_API_TO_ACC`(固定);`biz_scene` = `STANDARD_MESSAGE_BATCH_PAY`(固定) - `out_batch_no` 商户唯一;`total_trans_amount`、`total_count` - `order_title` 必填(展示在付款方账单);`time_expire`(绝对时间,格式 `yyyy-MM-dd HH:mm`) - `payer_info`:付款方 uid + `ext_info.agreement_no`(制单授权协议号,传则校验该协议) - `trans_order_list[]`:每笔 `out_biz_no`(批内唯一)、`trans_amount`(≥1 元)、`payee_info`(identity + identity_type,LOGON_ID 时 name 必填且校验姓名一致)、`remark` - **转账场景报备(26 年新接入商户必传)**:`transfer_scene_name` + `transfer_scene_report_infos[]`(`info_type` + `info_content`) - 出参:`batch_trans_id`(支付宝批次号,有效期 30 天) **渲染支付(render.pay)**:入参 `batch_trans_id`,出参支付 URL(PC 版浏览器打开)。 ### 2.3 额度与限制 - 默认:单笔 5000 / 日 20000 / 月 600000 元;自助提额上限:单笔 50000 / 日 100000 / 月 1000000 元 - 每批最多 **1000 笔**(PC 渠道),明细金额 ≥1 元 - 部分明细因收款账户问题转账失败时,支付宝原路退回该明细金额(明细级别状态跟踪) ### 2.4 授权特性 - 授权一旦完成**永久生效**;终止只能付款方在支付宝 APP 端手动解约 - 授权链接**一次有效**,重新授权需换外部单号 - 同一付款方与平台间已存在授权协议则不允许重复新增 - 付款方和请求方一致时,跳过制单授权校验 ### 2.5 接入前置条件 - 邀测产品,需联系支付宝业务人员开通「批量付款到户有密」;商家平台开通「商家转账」产品 - 应用网关:指向平台统一通知入口(现有 `/payment/notify`) - 订阅 from 蚂蚁消息:`alipay.fund.authorize.status.notify`、`alipay.fund.batch.order.changed` - 资金支出接口强制证书加签(现有服务商资金类客户端已是证书模式) - 不支持服务商代开发 —— 由企业绑定服务商 AppID 模式接入(平台代持,已确认) ## 3. 业务流程 ``` 企业财务(PC) 平台 (Java 后端) 支付宝 │ │ │ │ ① 打开「制单授权」页面 │ │ │◄─ 授权短链(PC) ──────────────────│─ fund.authorize.uni.apply ──────────►│ │ 浏览器打开链接 │ │ │◄════════════ 支付宝授权页 ════════╪══════════════════════════════════════►│ │ 输密码授权(永久生效,一次一链) │ │ │◄════════ 授权完成 ────────────────│─ status.notify 异步通知 ────────────►│ │ ② 创建批次(≤1000笔外部账号) │ fund.batch.create ─────────────────►│ │◄─ 支付URL ────────────────────── │─ fund.trans.render.pay(PC) ─────────►│ │ ③ 新窗口打开支付页 │ │ │◄══════════ 支付宝端输一次密码 ════╪══════════════════════════════════════►│ │ ④ 查批次列表/明细 │◄─ fund.batch.order.changed 通知 ─────│ │ │◄─ fund.batch.detail.query 兜底 ──────│ ``` 状态流转:批次 `INIT`(已受理)→ 支付后 `SUCCESS`(终态)/ 部分明细失败原路退回;`DISUSE`(关闭,终态,换外部单号重试);批次创建后 30 天未支付自动关单(或 `time_expire` 控制)。 ## 4. 数据模型(3 张新表) 沿用现有 base entity 体系(`PaymentEnterpriseBaseEntity`:tenant_id、enterprise_id 等)。 ### 4.1 `pay_batch_authorize` — 制单授权记录 | 字段 | 类型 | 说明 | |---|---|---| | out_biz_no | varchar(64) | 授权外部单号(雪花),唯一 | | enterprise_id | varchar(64) | 所属企业 | | participant_id | varchar(64) | 付款方支付宝 uid | | participant_id_type | varchar(16) | ALIPAY_USER_ID | | agreement_no | varchar(64) | 制单授权协议号(授权完成后回填) | | status | varchar(32) | AUTHING / AUTHED / UNBIND | | authorize_link | varchar(512) | 生成的短链接 | | authorize_expire_time | datetime | 链接超时时间 | ### 4.2 `pay_batch_order` — 批次主表 | 字段 | 类型 | 说明 | |---|---|---| | out_batch_no | varchar(32) | 商户批次唯一单号 | | batch_trans_id | varchar(32) | 支付宝批次订单号(唯一索引) | | total_amount | decimal(16,2) | 批次总金额 | | total_count | int | 总笔数 | | order_title | varchar(64) | 批次标题(账单展示) | | status | varchar(32) | INIT / SUCCESS / DISUSE / FAIL | | time_expire | datetime | 超时关单时间 | | payer_uid | varchar(64) | 付款方 uid | | agreement_no | varchar(64) | 制单授权协议号 | | transfer_scene_name | varchar(64) | 转账场景(26 年新接入必传) | | transfer_scene_report_infos | jsonb | 场景报备信息 [{info_type, info_content}] | | remark | varchar(200) | 业务备注 | | pay_url | varchar(1024) | render.pay 生成的支付 URL | | receipt_file_id / receipt_status | — | 电子回单(二期可做) | | error_code / error_msg | varchar | 错误信息 | ### 4.3 `pay_batch_detail` — 批次明细表 | 字段 | 类型 | 说明 | |---|---|---| | batch_id | bigint | 关联批次主键 | | out_biz_no | varchar(64) | 明细外部单号(批内唯一) | | amount | decimal(16,2) | 明细金额(≥1 元) | | remark | varchar(100) | 转账备注(展示在收款方账单) | | payee_identity | varchar(64) | 收款方账号(uid/账号/openid) | | payee_identity_type | varchar(32) | ALIPAY_LOGON_ID / ALIPAY_USER_ID / ALIPAY_OPEN_ID | | payee_name | varchar(256) | 收款方姓名(LOGON_ID 必填,校验姓名一致) | | status | varchar(32) | INIT / SUCCESS / FAIL | | error_code / error_msg | varchar | 明细级错误 | ## 5. 后端设计(Java,`module/payment/batch/` 新包) ### 5.1 `AlipayBatchPayService` 复用 `AlipayClientFactory.getClient(enterpriseId, "BATCH_PAY")`(服务商证书模式,bizType 走 `pay_service_provider_profile` 专属凭证,无专属配置回退服务商默认)。 - `authorizeApply(enterpriseId, participantId)` → 调 uni.apply,落授权记录,返回短链接 - `queryAuthorize(enterpriseId, outBizNo)` → 调 uni.query,更新授权状态 - `batchCreate(BatchCreateDTO)` → 校验(授权存在、笔数 ≤1000、金额 ≥1 元、场景报备)→ 调 batch.create → 落批次+明细 - 幂等:`out_batch_no` 唯一;返回 `UNIQUE_VIOLATION` 时查询批次后再决定 - `renderPay(outBatchNo)` → 调 render.pay,回填 pay_url - `batchQuery(outBatchNo)` → 调 batch.detail.query,同步批次+明细状态(定时/手动兜底,沿用现有 transferSyncAllService 定时同步模式) - `batchClose(outBatchNo)` → 调 batch.close - 通知处理(供 handler 调用):授权状态更新、批次状态更新、明细逐笔更新 ### 5.2 `BatchPayController` — `/payment/account/batch/*`(挂在现有资金账户模块下) | 接口 | 权限 | 说明 | |---|---|---| | POST `/authorize/apply` | module_payment:account:authorize | 生成授权链接 | | GET `/authorize/query` | module_payment:account:authorize | 查询授权状态 | | GET `/authorize/list` | module_payment:account:transfer:list | 授权列表 | | POST `/create` | module_payment:account:transfer | 创建批次 | | POST `/pay` | module_payment:account:transfer | 生成支付 URL | | GET `/list` | module_payment:account:transfer:list | 批次列表(分页/状态/时间筛选) | | GET `/detail` | module_payment:account:transfer:detail | 批次详情+明细 | | POST `/close` | module_payment:account:transfer | 关闭批次 | | GET `/export` | module_payment:account:transfer:list | 导出(复用现有 transferExport 模式) | ### 5.3 异步通知(复用现有入口,零新增端点) - `AlipayNotifyMethod` 枚举新增:`FUND_AUTHORIZE_STATUS_NOTIFY("alipay.fund.authorize.status.notify")`、`FUND_BATCH_ORDER_CHANGED("alipay.fund.batch.order.changed")` - 新增 `BatchPayHandler extends BaseNotifyHandler`,注册上述两个 method,通过 `NotifyContext` 注入 batch mapper;处理:授权签约/解约更新授权表、批次状态更新 + 明细逐笔更新 + 记 `pay_alipay_notify_log` - 验签沿用现有 `/payment/notify` 入口的验签机制(实现时确认 NotificationService 按 app_id 解析公钥的细节) - 通知处理幂等:同一 notify_id / 终态不重复更新 ## 6. 前端设计(资金账户模块新增「批量付款」) - **制单授权页**:生成授权短链接(PC 复制/二维码),授权状态展示,重新授权按钮 - **批次列表页**:分页、状态筛选、时间筛选、导出;行操作:支付(打开支付 URL)、查看详情、关闭 - **创建批次页**:手填明细 / Excel 导入(模板下载:收款账号、类型、姓名、金额、备注);转账场景选择(现金营销/企业退款/佣金报酬/二手回收/业务结算/公益补助/行政补贴退款/保险理赔)+ 报备信息录入 - **批次详情页**:批次信息 + 明细表格(逐笔状态)+ 支付按钮 - 支付页:`pay_url` 新窗口打开(支付宝 PC 支付页) ## 7. 错误处理与幂等 - 批次创建:`out_batch_no` 唯一索引;系统异常/未明确错误码时保持原单号重试;`UNIQUE_VIOLATION` → 查批次状态,已受理则不再重复创建 - 通知重复:以终态和 notify_id 判重 - 明细部分失败:支付宝原路退回,明细级状态跟踪,批次状态为 SUCCESS(部分明细 FAIL),列表展示明细级失败原因 - 授权链接过期/重复授权:提示重新生成(换 out_biz_no) - 批次超时:`time_expire` 控制,DISUSE 终态提示换单重试 ## 8. 测试 - **Java 单测**:`AlipayBatchPayService`(Mock 支付宝 SDK 响应):授权申请/查询、批次创建幂等(UNIQUE_VIOLATION)、状态同步、通知处理幂等 - **前端 Playwright**:走通「生成授权链接 → 创建批次(导入明细)→ 生成支付 URL」链路,批次列表/详情渲染 - 通知 handler 单测:两个新 msg_method 分发正确 ## 9. 前置条件与风险 - **邀测产品**:需联系支付宝业务人员开通;未开通前联调不可行 → 联调用沙箱/文档响应 Mock - 转账场景报备为 26 年新接入必传 —— 前端录入 + 后端校验必填 - 服务商需配置应用网关 + 订阅 from 蚂蚁消息(运营操作,需与客户确认) - 授权为永久性协议,平台不可解约 —— 授权管理页需明确提示 - 额度限制(单笔 5000 默认)可能导致大额批次失败 —— 页面提示查看/调整转账额度