Files
jsowell-charger-web/docs/汇付开户V2流程计划.md
2026-05-29 13:33:16 +08:00

24 KiB
Raw Permalink Blame History

汇付开户 V2 流程与落地计划

背景

现有开户页已经从旧组件切到了 jsowell-charge-ui/src/views/financial/accountUserInfo.vue,但整体仍偏向“资料展示 + 操作按钮”。新版开户流程需要按汇付官方账户体系重新梳理为状态驱动的流程,明确个人用户、企业用户、结算账户三个对象之间的关系。

官方文档来源:

官方约束摘要

个人用户 Member

  • 创建用户对象用于将商户侧 member_id 与 Adapay 系统关联,member_id 需在同一个 app_id 下唯一。
  • 创建成功后可创建结算账户,用于用户分账。
  • 若创建用户对象用于分账功能,官方文档说明 tel_nouser_namecert_typecert_id 不应上送。
  • 当前项目个人开户只上送 locationemailgendernickname,符合分账场景的轻量开户模型。

企业用户 CorpMember

  • 创建企业用户对象本质是提交企业开户申请,同步返回 pending 表示申请已受理。
  • 企业开户结果由异步通知返回。审核成功后企业用户创建成功;审核失败后可根据失败原因重新提交。
  • 企业开户若同时上送 bank_codebank_acct_typecard_nocard_name,审核成功后汇付会自动创建结算账户。
  • 若企业开户未上送银行要素,不会自动创建结算账户,需要后续单独调用结算账户创建接口。
  • 企业更新同样同步返回 pending,审核结果通过异步通知返回。
  • 企业更新不支持修改银行代码、银行账户类型、银行卡号、银行卡开户姓名。结算账户信息变更应先删除结算账户,再使用原 member_id 重新创建。

结算账户 SettleAccount

  • 结算账户用于承接用户分账金额结算,目前仅支持银行卡。
  • 创建对私结算账户时会校验银行卡号、银行卡开户姓名、身份证号三要素。
  • 结算账户由汇付生成唯一 id,用于查询和删除。
  • 银行账户类型为 2-对私 时,建议绑定 I 类银行卡,否则单次结算超过 10000 元可能失败。
  • 22:30 后创建成功的结算账户,结算会延后一个结算日。
  • 更新银行卡必须删除原结算账户后重新创建,且需与原结算账户使用的身份证和银行卡户名保持一致。

V2 产品流程

页面入口

入口保持不变:

  • 路由:/financial/merchant/index/:merchantId
  • 页面:jsowell-charge-ui/src/views/financial/accountUserInfo.vue

页面进入后只关心一个业务状态:当前运营商的汇付开户进度。

状态机

V2 状态 来源判断 页面主状态 允许操作
NONE 无有效 adapay_member_account 未开户 创建个人用户、创建企业用户
PERSONAL_OPENED_NO_SETTLE 个人用户已创建,未查询到结算账户 个人已开户,待创建结算账户 更新个人资料、创建对私结算账户
PERSONAL_COMPLETED 个人用户已创建,且存在结算账户 个人开户完成 更新个人资料、查看/删除结算账户
CORP_AUDITING 企业用户提交后,汇付 audit_state=A 或本地状态待审核 企业审核中 刷新、查看资料
CORP_FAILED 汇付 audit_state=B/C 或本地状态失败 企业开户失败 查看失败原因、重新提交企业开户
CORP_OPENED_NO_SETTLE 企业 audit_state=D,或企业审核通过但无结算账户 企业已开户,待创建结算账户 更新企业资料、创建对公结算账户
CORP_COMPLETED 企业 audit_state=E 或存在结算账户 企业开户完成 更新企业资料、查看/删除结算账户

企业审核状态沿用项目已有枚举:

汇付 audit_state 含义 V2 归类
A 待审核 CORP_AUDITING
B 审核失败 CORP_FAILED
C 开户失败 CORP_FAILED
D 开户成功但未创建结算账户 CORP_OPENED_NO_SETTLE
E 开户和创建结算账户成功 CORP_COMPLETED

用户可见流程

flowchart TD
  A["进入开户页"] --> B["查询开户聚合详情"]
  B --> C{"是否已开户"}
  C -->|否| D["选择个人/企业开户"]
  D -->|个人| E["提交个人资料"]
  E --> F["个人开户成功"]
  F --> G["创建对私结算账户"]
  G --> H["开户完成"]
  D -->|企业| I["提交企业资料和附件"]
  I --> J["企业审核中"]
  J --> K{"审核结果"}
  K -->|失败| L["展示失败原因并重新提交"]
  L --> J
  K -->|成功| M["创建对公结算账户"]
  M --> H
  C -->|是| N["按状态展示下一步动作"]

字段映射

个人用户创建

当前接口:

  • 前端:createAdapayMember
  • 后端:POST /adapay/member/createAdapayMember
  • DTOCreatePersonalMemberDTO
  • 汇付 SDKMember.create
页面字段 后端 DTO 汇付字段 必填 说明
运营商 ID merchantId 不直接传汇付 Y 后端根据运营商查询 wechatAppIdapp_id
昵称 nickname nickname N 当前页面设为必填,便于运营识别
性别 gender gender N 取值 MALE / FEMALE
邮箱 email email N 当前页面校验邮箱格式
地址 location location N 当前页面设为必填
汇付会员 ID 后端生成 member_id Y 当前使用 Constants.ADAPAY_MEMBER_PREFIX + IdUtils.getMemberId()
应用 ID 后端配置 app_id Y AdapayConfigFactory 获取

V2 处理原则:

  • 保持轻量个人开户,不新增实名字段。
  • 个人用户创建成功后,本地 adapay_member_account.status 记为成功。
  • 页面下一步引导创建对私结算账户。

企业用户创建

当前接口:

  • 前端:createCorpMember
  • 后端:POST /adapay/member/createCorpMember
  • DTOCreateCorpMemberDTO
  • 汇付 SDKCorpMember.create
页面字段 后端 DTO 汇付字段 必填 说明
运营商 ID merchantId 不直接传汇付 Y 后端根据运营商查询配置
企业名称 name name Y 长度不超过 50
省份编码 provCode prov_code Y 四位省市编码
地区编码 areaCode area_code Y 四位省市编码
统一社会信用码 socialCreditCode social_credit_code Y 长度 18
统一社会信用证有效期 socialCreditCodeExpires social_credit_code_expires Y YYYYMMDD
经营范围 businessScope business_scope Y 长度不超过 200
法人姓名 legalPerson legal_person Y 长度不超过 20
法人身份证号 legalCertId legal_cert_id Y 长度不超过 20
法人身份证有效期 legalCertIdExpires legal_cert_id_expires Y YYYYMMDD
法人手机号 legalMp legal_mp Y 11 位手机号
企业地址 address address Y 长度不超过 256
邮编 zipCode zip_code N 长度 6
企业电话 telphone telphone N 长度不超过 30
企业邮箱 email email N 长度不超过 40
附件列表 imgList attach_file Y 后端压缩为 zip 上传,最大 9 MB
汇付会员 ID 后端生成 member_id Y 当前使用 Constants.ADAPAY_CORP_MEMBER_PREFIX + IdUtils.getMemberId()
请求订单号 后端生成 order_no Y 需保证同 app_id 下唯一
异步通知地址 后端配置 notify_url N 当前使用 ADAPAY_CALLBACK_URL

企业附件要求:

附件 当前页面 upload key 汇付要求
法人身份证正面 idCardFace 法人身份证正面照
法人身份证反面 idCardBack 法人身份证反面照
开户许可证 bankLicense 开户银行许可证照
营业执照 businessLicense 三证合一证件照

V2 处理原则:

  • 企业开户阶段不随开户上送银行字段,避免审核成功后自动创建结算账户,统一改为审核通过后手动创建结算账户。
  • 提交成功后页面展示“审核中”,不提示开户完成。
  • 异步回调成功后再进入“待创建结算账户”或“已完成”状态。

结算账户创建

当前接口:

  • 前端:createSettleAccount
  • 后端:POST /adapay/member/createSettleAccount
  • DTOCreateSettleAccountDTO
  • 汇付 SDKSettleAccount.create
页面字段 后端 DTO 汇付字段 必填 说明
运营商 ID merchantId 不直接传汇付 Y 后端查询当前汇付会员
账户类型 bankAcctType account_info.bank_acct_type Y 1-对公2-对私
银行卡号 cardId account_info.card_id Y 当前后端同时保留 cardNoV2 统一使用 cardId
户名 cardName account_info.card_name Y 对公时必须与企业名称一致
身份证号 certId account_info.cert_id 对私必填 对私三要素认证使用
证件类型 certType account_info.cert_type N 默认 00-身份证
手机号 telNo account_info.tel_no Y 银行预留手机号
银行编码 bankCode account_info.bank_code Y 取银行编码列表
省份编码 provCode account_info.prov_code Y 开户银行省份
地区编码 areaCode account_info.area_code Y 开户银行地区
渠道 后端固定 channel Y 固定 bank_account
汇付会员 ID 后端查询 member_id Y 当前运营商绑定的汇付会员
应用 ID 后端配置 app_id Y 从汇付配置获取

V2 处理原则:

  • 个人用户默认创建 2-对私 结算账户。
  • 企业用户默认创建 1-对公 结算账户。
  • 按官方 account_info 字段说明V2 前端应收集 bankCodeprovCodeareaCode,包括对私账户也建议保留,减少生产环境参数不完整风险。
  • 创建前后端都校验“当前会员不能已有结算账户”。

企业用户更新

当前接口:

  • 前端:updateAdapayMember
  • 后端:POST /adapay/member/updateAdapayMember
  • DTOUpdateCorpMemberDTO
  • 汇付功能号:corp_members.update
页面字段 汇付字段 必填 V2 说明
企业基础信息 nameprov_codearea_codebusiness_scope N 仅用于企业资料变更
法人信息 legal_personlegal_cert_idlegal_cert_id_expireslegal_mp N 更新后进入审核流程
附件 attach_file N 当前实现要求重新上传四张附件V2 可继续要求,降低资料不一致风险
通知地址 notify_url N 建议补齐,确保更新审核结果也能回调

V2 处理原则:

  • 企业资料更新不处理银行卡信息。
  • 若要换银行卡,走“删除结算账户 -> 重新创建结算账户”流程。
  • 企业更新提交成功后页面进入 CORP_AUDITING,等待回调或主动刷新。

个人用户更新

当前接口:

  • 前端:updateAdapayMember
  • 后端根据 adapayMemberId 前缀判断后调用 Member.update
页面字段 汇付字段 V2 说明
昵称 nickname 保留
性别 gender 保留
邮箱 email 保留
地址 location 保留

V2 处理原则:

  • 个人更新不改变开户状态。
  • 若未来引入禁用用户,需单独设计 disabled 操作,不混入普通资料更新。

后端落地计划

1. 新增 V2 聚合响应模型

建议新增:

  • AdapayMemberOpenV2VO
  • AdapayOpenStatusEnum
  • AdapayOpenActionEnum

响应结构建议:

{
  "merchantId": "1",
  "status": "CORP_OPENED_NO_SETTLE",
  "memberType": "CORP",
  "adapayMemberId": "ACM29102732",
  "auditState": "D",
  "auditDesc": "",
  "settleAccountStatus": "NONE",
  "primaryAction": "CREATE_SETTLE_ACCOUNT",
  "actions": [
    "REFRESH",
    "UPDATE_CORP_MEMBER",
    "CREATE_SETTLE_ACCOUNT"
  ],
  "member": {},
  "settleAccount": null,
  "tips": []
}

2. 新增 V2 查询接口

建议新增接口,不破坏现有页面调用:

  • POST /adapay/member/v2/detail

职责:

  • 查询 adapay_member_account 当前有效记录。
  • 判断个人/企业用户类型。
  • 查询汇付会员详情。
  • 查询结算账户详情。
  • 汇总 V2 状态、主操作、可用动作、提示信息。

3. 状态计算集中到后端

当前前端通过 memberTypeauditStatesettleAccountList 自行判断V2 改为后端统一计算。

建议规则:

  • 无本地记录:NONE
  • 个人 + 无结算账户:PERSONAL_OPENED_NO_SETTLE
  • 个人 + 有结算账户:PERSONAL_COMPLETED
  • 企业 + audit_state=ACORP_AUDITING
  • 企业 + audit_state=B/C 或本地 status=2CORP_FAILED
  • 企业 + audit_state=D 或审核通过但无结算账户:CORP_OPENED_NO_SETTLE
  • 企业 + audit_state=E 或有结算账户:CORP_COMPLETED

4. 本地表状态增强

当前 adapay_member_account.status 只有:

  • 0 待审核
  • 1 审核通过
  • 2 创建失败

V2 建议新增字段,避免只靠 remark 存失败信息:

alter table adapay_member_account
  add column audit_state varchar(8) null comment '汇付企业审核状态 A/B/C/D/E',
  add column audit_desc varchar(512) null comment '汇付企业审核说明',
  add column last_order_no varchar(64) null comment '最近一次企业开户/更新请求订单号';

兼容策略:

  • 不强制迁移历史数据。
  • 查询时优先使用汇付实时查询结果。
  • 若汇付查询失败,再使用本地 statusremark 兜底。

5. 企业回调更新

现有回调处理在 OrderService.corpMemberFailedOrderService.corpMemberSucceeded

V2 调整:

  • 成功回调:写入 audit_stateaudit_descstatus=1
  • 失败回调:写入 audit_stateaudit_descstatus=2
  • 若回调中包含结算账户 ID同步写入 settle_account_id
  • 清理 ADAPAY_MEMBER_ACCOUNT + merchantId 缓存。

6. 创建/更新动作校验

后端服务层统一校验:

  • CREATE_PERSONAL_MEMBER:仅 NONE 可执行。
  • CREATE_CORP_MEMBER:仅 NONECORP_FAILED 可执行。
  • UPDATE_PERSONAL_MEMBER:仅个人已开户状态可执行。
  • UPDATE_CORP_MEMBER:仅企业非审核中状态可执行。
  • CREATE_SETTLE_ACCOUNT:仅已开户且无结算账户可执行;企业必须非审核中、非失败。
  • DELETE_SETTLE_ACCOUNT:仅已有结算账户可执行,且需确认不会影响未结算分账。

前端落地计划

1. API 封装

jsowell-charge-ui/src/api/adapayMember/adapayMember.js 增加:

export function getAdapayOpenDetailV2(data) {
  return request({
    url: '/adapay/member/v2/detail',
    method: 'post',
    data
  })
}

2. 页面结构

accountUserInfo.vue 调整为:

  1. 顶部状态总览:显示当前 V2 状态、汇付会员 ID、审核状态、结算账户状态。
  2. 流程步骤条:未开户、提交资料、审核、结算账户、完成。
  3. 主操作区:只渲染后端返回的 primaryActionactions
  4. 会员资料区:个人/企业按字段分组展示。
  5. 结算账户区:独立展示,包含删除后重建提示。
  6. 失败原因区:企业失败时展示 auditDesc,并给出重新提交入口。

3. 表单调整

个人开户表单:

  • 昵称
  • 性别
  • 邮箱
  • 地址

企业开户表单:

  • 企业主体
  • 法人信息
  • 联系信息
  • 附件上传

结算账户表单:

  • 账户类型
  • 银行卡号
  • 户名
  • 身份证号
  • 手机号
  • 银行编码
  • 省份/地区

4. 交互规则

  • 状态由后端响应驱动,前端不再手写复杂判断。
  • 企业审核中:禁用更新、结算账户创建、重复提交。
  • 企业失败:主按钮为“重新提交企业开户”。
  • 企业成功但无结算账户:主按钮为“创建对公结算账户”。
  • 个人无结算账户:主按钮为“创建对私结算账户”。
  • 结算账户删除前提示:删除后支付分账将不可用,需重新创建后恢复。

Review 待修复计划

问题一:结算账户创建成功后缓存未失效

风险:

  • createSettleAccount 成功写入 settle_account_id 后,当前使用 updateAdapayMemberAccountByMemberId 更新本地记录。
  • updateAdapayMemberAccountByMemberId 只更新数据库,没有清理 ADAPAY_MEMBER_ACCOUNT + merchantId 缓存。
  • /adapay/member/v2/detail 继续读取旧缓存时,页面可能仍显示“未创建结算账户”,导致状态停留在 PERSONAL_OPENED_NO_SETTLECORP_OPENED_NO_SETTLE

修改计划:

  • AdapayMemberAccountServiceImpl.updateAdapayMemberAccountByMemberId 中补齐缓存清理。
  • 优先使用传入对象的 merchantId 清理缓存。
  • 若调用方只传 adapayMemberId,则先按 adapayMemberId 查询当前记录,拿到 merchantId 后清理缓存。
  • 调整 AdapayService.createSettleAccountchangeBankCard 等调用点,尽量在更新对象中补充 merchantId,减少额外查询。

验收标准:

  • 创建结算账户成功后,立即刷新 V2 页面,状态应变为 PERSONAL_COMPLETEDCORP_COMPLETED
  • 结算账户 ID、银行卡信息能立即展示不依赖 Redis 过期。

问题二:删除结算账户缺少业务阻断校验

风险:

  • 文档已确认删除结算账户前需要校验未结算分账/在途提现。
  • 当前 deleteSettleAccount 只校验 V2 action 和结算账户是否存在,随后直接调用汇付删除并清空本地结算账户。
  • 若存在未结算分账或在途提现,删除结算账户会影响后续结算、提现或分账状态追踪。

修改计划:

  • AdapayService.deleteSettleAccount 调用汇付删除前增加业务校验方法,例如 assertSettleAccountCanDelete(merchantId)
  • 校验维度先按现有数据能力落地,明确数据源和阻断状态:
    • 在途提现:查询 clearing_withdraw_info,若存在 withdraw_status = '0' 的记录,阻断删除。
    • 清分账单:查询 clearing_bill_info,若存在 bill_status in ('0', '1', '3', '5') 的记录,阻断删除,分别对应未清分、清分在途、提现申请中、等待处理。
    • 未分账订单:查询 order_unsplit_record,若存在 status = 'unsplit' 且订单归属当前 merchantId 的记录,阻断删除。
  • 为未分账订单补齐按商户查询能力:
    • OrderUnsplitRecordMapper 新增 countUnsplitByMerchantId(merchantId)
    • SQL 通过 order_unsplit_record.order_code 关联 order_basic_info.order_code,按 order_basic_info.merchant_id 过滤当前商户。
    • OrderUnsplitRecordService 增加同名方法,供 AdapayService.assertSettleAccountCanDelete 调用。
  • 为提现和清分账单尽量使用轻量查询:
    • 优先新增 ClearingWithdrawInfoService.countProcessingByMerchantId(merchantId),只统计 withdraw_status = '0'
    • 优先新增 ClearingBillInfoService.countBlockingByMerchantId(merchantId),只统计 bill_status in ('0', '1', '3', '5')
    • 若已有列表查询能满足,可先复用现有方法,但最终实现应避免在删除前加载大量历史记录。
  • 阻断时返回明确错误信息,例如“存在在途提现,请完成后再删除结算账户”或“存在未结算分账,请完成结算后再删除结算账户”。
  • 前端保留现有确认弹窗,但以后端校验结果作为最终准入。

验收标准:

  • 无在途提现、无未结算分账时,可以删除结算账户并清空本地 settle_account_id
  • 存在 withdraw_status = '0' 的提现记录时,删除接口返回业务错误,汇付删除请求不会发起,本地结算账户不会清空。
  • 存在 bill_status in ('0', '1', '3', '5') 的清分账单时,删除接口返回业务错误,汇付删除请求不会发起,本地结算账户不会清空。
  • 存在 status = 'unsplit' 且归属当前商户的未分账订单时,删除接口返回业务错误,汇付删除请求不会发起,本地结算账户不会清空。

问题三:删除汇付用户未接入 V2 action 后端校验

风险:

  • V2 计划要求后端统一动作校验,避免绕过前端按钮限制。
  • 当前创建、更新、创建/删除结算账户已接入 assertActionAllowed
  • deleteAdapayMember 未校验 DELETE_MEMBER,直接调用接口时可能删除企业审核中的本地记录,导致后续汇付回调无法匹配记录。

修改计划:

  • AdapayService.deleteAdapayMember 开始处增加 assertActionAllowed(dto.getMerchantId(), AdapayOpenActionEnum.DELETE_MEMBER)
  • 保留现有 adapayMemberId 一致性校验和“需先删除结算账户”校验,作为二次保护。
  • 确认 resolveActions 中仅允许以下状态删除会员:
    • PERSONAL_OPENED_NO_SETTLE
    • CORP_FAILED
  • 若后续业务允许更多状态删除,再由状态机统一扩展,不在接口中散落判断。

验收标准:

  • 企业审核中状态调用删除汇付用户接口,应返回“不允许执行删除”类业务错误。
  • 企业失败且无结算账户时,可以删除本地失败记录。
  • 个人已开户但未创建结算账户时,可以删除本地会员记录。
  • 已有结算账户时仍必须先删除结算账户。

分阶段实施清单

阶段一:后端状态与接口

  • 新增 AdapayOpenStatusEnumAdapayOpenActionEnum
  • 新增 V2 聚合 VO。
  • 新增 /adapay/member/v2/detail 接口。
  • AdapayService 中增加状态计算方法。
  • 后端统一动作校验,避免前端绕过状态限制。

阶段二:企业审核状态持久化

  • 新增数据库字段 audit_stateaudit_desclast_order_no
  • 更新 AdapayMemberAccount domain、mapper、XML。
  • 调整企业创建、企业更新逻辑,记录 last_order_no
  • 调整回调处理,写入审核状态和说明。
  • 清理相关 Redis 缓存。

阶段三:前端 V2 页面

  • 新增 V2 聚合查询 API。
  • 改造 accountUserInfo.vue 数据源为 V2 detail。
  • 增加状态步骤条和主操作区。
  • actions 渲染按钮。
  • 调整结算账户表单,补齐银行编码、省份、地区采集。
  • 企业失败态展示审核说明并支持重新提交。

阶段四:回归与联调

  • 无开户记录 -> 创建个人用户 -> 创建对私结算账户。
  • 无开户记录 -> 创建企业用户 -> 审核中展示。
  • 企业审核失败 -> 展示失败原因 -> 重新提交。
  • 企业审核成功但无结算账户 -> 创建对公结算账户。
  • 已有结算账户 -> 禁止重复创建。
  • 删除结算账户 -> 重新创建结算账户。
  • 个人资料更新后刷新展示一致。
  • 企业资料更新后进入审核中,回调后状态正确。

需要确认的问题

  1. 企业开户 V2 是否确定不使用汇付“开户时自动创建结算账户”的能力?本计划建议不使用,统一手动创建,流程更清晰。 回答:确定不使用。
  2. 企业资料更新后是否必须重新上传四张附件?官方为非必填,但当前系统要求重传;本计划建议继续重传。 回答:确定继续重传。
  3. 个人结算账户是否也强制填写银行编码、省份、地区?官方 account_info 字段说明为非空字段,本计划建议 V2 补齐采集。 回答:确定补齐采集。
  4. 删除结算账户前是否需要校验未结算分账/在途提现?本计划建议后续补一层业务校验。 回答:需要校验 。