mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-11 10:49:52 +08:00
24 KiB
24 KiB
汇付开户 V2 流程与落地计划
背景
现有开户页已经从旧组件切到了 jsowell-charge-ui/src/views/financial/accountUserInfo.vue,但整体仍偏向“资料展示 + 操作按钮”。新版开户流程需要按汇付官方账户体系重新梳理为状态驱动的流程,明确个人用户、企业用户、结算账户三个对象之间的关系。
官方文档来源:
- 个人用户:https://docs.adapay.tech/api/trade.html#member-create
- 企业用户:https://docs.adapay.tech/api/trade.html#corpmember
- 结算账户:https://docs.adapay.tech/api/trade.html#settle-account-create
官方约束摘要
个人用户 Member
- 创建用户对象用于将商户侧
member_id与 Adapay 系统关联,member_id需在同一个app_id下唯一。 - 创建成功后可创建结算账户,用于用户分账。
- 若创建用户对象用于分账功能,官方文档说明
tel_no、user_name、cert_type、cert_id不应上送。 - 当前项目个人开户只上送
location、email、gender、nickname,符合分账场景的轻量开户模型。
企业用户 CorpMember
- 创建企业用户对象本质是提交企业开户申请,同步返回
pending表示申请已受理。 - 企业开户结果由异步通知返回。审核成功后企业用户创建成功;审核失败后可根据失败原因重新提交。
- 企业开户若同时上送
bank_code、bank_acct_type、card_no、card_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 - DTO:
CreatePersonalMemberDTO - 汇付 SDK:
Member.create
| 页面字段 | 后端 DTO | 汇付字段 | 必填 | 说明 |
|---|---|---|---|---|
| 运营商 ID | merchantId |
不直接传汇付 | Y | 后端根据运营商查询 wechatAppId 和 app_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 - DTO:
CreateCorpMemberDTO - 汇付 SDK:
CorpMember.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 - DTO:
CreateSettleAccountDTO - 汇付 SDK:
SettleAccount.create
| 页面字段 | 后端 DTO | 汇付字段 | 必填 | 说明 |
|---|---|---|---|---|
| 运营商 ID | merchantId |
不直接传汇付 | Y | 后端查询当前汇付会员 |
| 账户类型 | bankAcctType |
account_info.bank_acct_type |
Y | 1-对公,2-对私 |
| 银行卡号 | cardId |
account_info.card_id |
Y | 当前后端同时保留 cardNo,V2 统一使用 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 前端应收集
bankCode、provCode、areaCode,包括对私账户也建议保留,减少生产环境参数不完整风险。 - 创建前后端都校验“当前会员不能已有结算账户”。
企业用户更新
当前接口:
- 前端:
updateAdapayMember - 后端:
POST /adapay/member/updateAdapayMember - DTO:
UpdateCorpMemberDTO - 汇付功能号:
corp_members.update
| 页面字段 | 汇付字段 | 必填 | V2 说明 |
|---|---|---|---|
| 企业基础信息 | name、prov_code、area_code、business_scope 等 |
N | 仅用于企业资料变更 |
| 法人信息 | legal_person、legal_cert_id、legal_cert_id_expires、legal_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 聚合响应模型
建议新增:
AdapayMemberOpenV2VOAdapayOpenStatusEnumAdapayOpenActionEnum
响应结构建议:
{
"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. 状态计算集中到后端
当前前端通过 memberType、auditState、settleAccountList 自行判断,V2 改为后端统一计算。
建议规则:
- 无本地记录:
NONE - 个人 + 无结算账户:
PERSONAL_OPENED_NO_SETTLE - 个人 + 有结算账户:
PERSONAL_COMPLETED - 企业 +
audit_state=A:CORP_AUDITING - 企业 +
audit_state=B/C或本地status=2:CORP_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 '最近一次企业开户/更新请求订单号';
兼容策略:
- 不强制迁移历史数据。
- 查询时优先使用汇付实时查询结果。
- 若汇付查询失败,再使用本地
status、remark兜底。
5. 企业回调更新
现有回调处理在 OrderService.corpMemberFailed、OrderService.corpMemberSucceeded。
V2 调整:
- 成功回调:写入
audit_state、audit_desc、status=1。 - 失败回调:写入
audit_state、audit_desc、status=2。 - 若回调中包含结算账户 ID,同步写入
settle_account_id。 - 清理
ADAPAY_MEMBER_ACCOUNT + merchantId缓存。
6. 创建/更新动作校验
后端服务层统一校验:
CREATE_PERSONAL_MEMBER:仅NONE可执行。CREATE_CORP_MEMBER:仅NONE或CORP_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 调整为:
- 顶部状态总览:显示当前 V2 状态、汇付会员 ID、审核状态、结算账户状态。
- 流程步骤条:未开户、提交资料、审核、结算账户、完成。
- 主操作区:只渲染后端返回的
primaryAction和actions。 - 会员资料区:个人/企业按字段分组展示。
- 结算账户区:独立展示,包含删除后重建提示。
- 失败原因区:企业失败时展示
auditDesc,并给出重新提交入口。
3. 表单调整
个人开户表单:
- 昵称
- 性别
- 邮箱
- 地址
企业开户表单:
- 企业主体
- 法人信息
- 联系信息
- 附件上传
结算账户表单:
- 账户类型
- 银行卡号
- 户名
- 身份证号
- 手机号
- 银行编码
- 省份/地区
4. 交互规则
- 状态由后端响应驱动,前端不再手写复杂判断。
- 企业审核中:禁用更新、结算账户创建、重复提交。
- 企业失败:主按钮为“重新提交企业开户”。
- 企业成功但无结算账户:主按钮为“创建对公结算账户”。
- 个人无结算账户:主按钮为“创建对私结算账户”。
- 结算账户删除前提示:删除后支付分账将不可用,需重新创建后恢复。
Review 待修复计划
问题一:结算账户创建成功后缓存未失效
风险:
createSettleAccount成功写入settle_account_id后,当前使用updateAdapayMemberAccountByMemberId更新本地记录。updateAdapayMemberAccountByMemberId只更新数据库,没有清理ADAPAY_MEMBER_ACCOUNT + merchantId缓存。/adapay/member/v2/detail继续读取旧缓存时,页面可能仍显示“未创建结算账户”,导致状态停留在PERSONAL_OPENED_NO_SETTLE或CORP_OPENED_NO_SETTLE。
修改计划:
- 在
AdapayMemberAccountServiceImpl.updateAdapayMemberAccountByMemberId中补齐缓存清理。 - 优先使用传入对象的
merchantId清理缓存。 - 若调用方只传
adapayMemberId,则先按adapayMemberId查询当前记录,拿到merchantId后清理缓存。 - 调整
AdapayService.createSettleAccount、changeBankCard等调用点,尽量在更新对象中补充merchantId,减少额外查询。
验收标准:
- 创建结算账户成功后,立即刷新 V2 页面,状态应变为
PERSONAL_COMPLETED或CORP_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_SETTLECORP_FAILED
- 若后续业务允许更多状态删除,再由状态机统一扩展,不在接口中散落判断。
验收标准:
- 企业审核中状态调用删除汇付用户接口,应返回“不允许执行删除”类业务错误。
- 企业失败且无结算账户时,可以删除本地失败记录。
- 个人已开户但未创建结算账户时,可以删除本地会员记录。
- 已有结算账户时仍必须先删除结算账户。
分阶段实施清单
阶段一:后端状态与接口
- 新增
AdapayOpenStatusEnum、AdapayOpenActionEnum。 - 新增 V2 聚合 VO。
- 新增
/adapay/member/v2/detail接口。 - 在
AdapayService中增加状态计算方法。 - 后端统一动作校验,避免前端绕过状态限制。
阶段二:企业审核状态持久化
- 新增数据库字段
audit_state、audit_desc、last_order_no。 - 更新
AdapayMemberAccountdomain、mapper、XML。 - 调整企业创建、企业更新逻辑,记录
last_order_no。 - 调整回调处理,写入审核状态和说明。
- 清理相关 Redis 缓存。
阶段三:前端 V2 页面
- 新增 V2 聚合查询 API。
- 改造
accountUserInfo.vue数据源为 V2 detail。 - 增加状态步骤条和主操作区。
- 按
actions渲染按钮。 - 调整结算账户表单,补齐银行编码、省份、地区采集。
- 企业失败态展示审核说明并支持重新提交。
阶段四:回归与联调
- 无开户记录 -> 创建个人用户 -> 创建对私结算账户。
- 无开户记录 -> 创建企业用户 -> 审核中展示。
- 企业审核失败 -> 展示失败原因 -> 重新提交。
- 企业审核成功但无结算账户 -> 创建对公结算账户。
- 已有结算账户 -> 禁止重复创建。
- 删除结算账户 -> 重新创建结算账户。
- 个人资料更新后刷新展示一致。
- 企业资料更新后进入审核中,回调后状态正确。
需要确认的问题
- 企业开户 V2 是否确定不使用汇付“开户时自动创建结算账户”的能力?本计划建议不使用,统一手动创建,流程更清晰。 回答:确定不使用。
- 企业资料更新后是否必须重新上传四张附件?官方为非必填,但当前系统要求重传;本计划建议继续重传。 回答:确定继续重传。
- 个人结算账户是否也强制填写银行编码、省份、地区?官方 account_info 字段说明为非空字段,本计划建议 V2 补齐采集。 回答:确定补齐采集。
- 删除结算账户前是否需要校验未结算分账/在途提现?本计划建议后续补一层业务校验。 回答:需要校验 。