1.Overview/背景介绍
对账是支付系统中重要的一环,确认在固定周期内和第三方支付的交易、资金的正确性,保证双方的交易、资金一致正确。
它又包括信息流对账和资金流对账:
- 信息流对账一般用在自己内部系统的对账,比如支付系统的支付数据和业务系统的业务数据进行对账,保证资金交易和业务交易的一致性。
- 资金流对账是支付系统和第三方支付系统之间的资金交易对账。
术语介绍
扎帐:发现对账系统中的差异记录。
平账:通过人工或自动的方式,解决上述的差异。
平台长款:平台有该订单,但第三方支付无此订单。
平台漏单:第三方支付有该订单,但平台无此订单。
2.Goals and Product Requirements/目标和产品需求
业务目标
- 财务人员 | 隔天自动对账,支持差错处理 | 减少人工对账的成本 |
- 业务方 | 支持和财务系统进行双向对账 | 避免财务系统已收,但是业务系统未收的异常情况 |
技术目标
- 技术人员 | 支持自动对账;运用多种设计模式,动态支持多渠道多商户的对账|
- DBA | 能够对已对账的表数据进行归档,提升数据库的操作效率 |
3.ASR/关键架构需求
技术约束
业务约束
- 微信和杭州银行的对账单都是以商户号维度来生成的
- 杭州银行生成对账单的时间不明确,建议我们平台于次日12点后获取
- 微信的对账单是在次日9点启动生成前一天的对账单,平台最早于10点后才能获取
- 微信的对账单接口只能下载三个月以内的账单
4.Out of Scope/范围
本方案中的双向对账,是针对业务订单在交易中台中的业务,像智通云缴费的业务订单跟支付服务的对账不包括在内。
5.Open Questions/开放性问题
随着交易量的上升,对账的优化后期可使用redis或spark技术。
目前对账模块放在支付服务中,后期考虑单独部署。
6.Design/设计实现
总体设计:
整体流程
核心对账流程
- 查询平台所有交易成功的订单
- 查询平台所有的交易订单
- 查询平台缓存池中的数据
- 查询第三方支付交易成功的订单
- 开始以平台的数据为准对账,平台长款记入缓冲池
- 开始以第三方支付通道的数据为准对账,当第三方支付订单不存在于平台未成功的订单列表中,需进一步查询是否存在于平台缓冲池中
下面是关键的两个对账流程示意图:
流程一:
流程二:
流程三:
对账结果:
// 银行不存在该订单
BANK_MISS("银行漏单"),
// 平台不存在该订单
PLATFORM_MISS("平台漏单"),
// 银行支付成功,平台支付不成功(比较常见) PLATFORM_SHORT_STATUS_MISMATCH("平台短款,状态不符"),
// 平台需支付金额比银行实际支付金额少(基本不会出现) PLATFORM_SHORT_CASH_MISMATCH("平台短款,金额不符"),
// 银行实际支付金额比平台需支付金额少 PLATFORM_OVER_CASH_MISMATCH("平台长款,金额不符"),
// 平台支付成功,银行支付不成功(基本不会出现) PLATFORM_OVER_STATUS_MISMATCH("平台长款,状态不符"),
// 目前未实现该项对账
FEE_MISMATCH("手续费不匹配");
数模设计
数模脚本
`CREATE` `TABLE` ``bill_info` (`
``id` ``int``(10) ``NOT` `NULL` `COMMENT ``'主键ID'``,`
``channel_type` ``varchar``(20) ``NOT` `NULL` `COMMENT ``'渠道类型: WX,HzBank,ALIPAY'``,`
``mch_id` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'商户ID,对应表channel_account.mch_id'``,`
``batch_no` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'对账批次号'``,`
``bill_ymd` ``varchar``(8) ``NOT` `NULL` `COMMENT ``'账单日期,格式YYYYMMDD'``,`
``status` ``varchar``(16) ``NOT` `NULL` `COMMENT ``'状态:SUCCESS,FAIL;'``,`
``error_msg` ``varchar``(500) ``DEFAULT` `NULL` `COMMENT ``'错误信息'``,`
``platform_amt` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台交易金额(单位:分)'``,`
``platform_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台交易笔数'``,`
``platform_refund_amt` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台退款金额(单位:分)'``,`
``platform_refund_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台退款笔数'``,`
``platform_fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台手续费(单位:分)'``,`
``platform_refund_fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'平台退款手续费(单位:分)'``,`
``bank_amt` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行交易金额(单位:分)'``,`
``bank_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行交易笔数'``,`
``bank_refund_amt` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行退款金额(单位:分)'``,`
``bank_refund_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行退款笔数'``,`
``bank_fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行总手续费(单位:分)'``,`
``bank_refund_fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行退款手续费(单位:分)'``,`
``pay_check_pool_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'支付缓冲池笔数'``,`
``refund_check_pool_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'退款缓冲池笔数'``,`
``mistake_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'差错笔数'``,`
``unhandle_mistake_count` ``int``(10) ``DEFAULT` `0 COMMENT ``'未处理的差错笔数'``,`
``create_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'创建人'``,`
``create_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'创建时间'``,`
``modified_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'更新人'``,`
``modified_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'更新时间'`
`) ENGINE=InnoDB ``DEFAULT` `CHARSET=utf8mb4 COMMENT=``'对账统计表(渠道下的各个商户每天对账记录)'``;`
`CREATE` `TABLE` ``bill_mistake` (`
``id` ``int``(10) ``NOT` `NULL` `COMMENT ``'主键ID'``,`
``channel_type` ``varchar``(20) ``NOT` `NULL` `COMMENT ``'渠道类型: WX,HzBank,ALIPAY'``,`
``mch_id` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'商户ID,对应表channel_account.mch_id'``,`
``batch_no` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'对账批次号'``,`
``bill_type` ``varchar``(16) ``NOT` `NULL` `COMMENT ``'账单类型:PAY,REFUND'``,`
``bill_ymd` ``varchar``(8) ``NOT` `NULL` `COMMENT ``'账单日期,格式YYYYMMDD'``,`
``status` ``varchar``(16) ``NOT` `NULL` `DEFAULT` `'NOHANDLE'` `COMMENT ``'状态:HANDLED--已处理,NOHANDLE--未处理'``,`
``error_type` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'差错类别:BANK_MISS--银行漏单;PLATFORM_MISS--平台漏单;PLATFORM_SHORT_STATUS_MISMATCH--平台短款,状态不符;PLATFORM_SHORT_CASH_MISMATCH--平台短款,金额不符;PLATFORM_OVER_CASH_MISMATCH--平台长款,金额不符;PLATFORM_OVER_STATUS_MISMATCH--平台长款,状态不符;FEE_MISMATCH--手续费不匹配'``,`
``handle_date` datetime ``DEFAULT` `NULL` `COMMENT ``'处理日期'``,`
``handle_user` ``varchar``(32) ``DEFAULT` `NULL` `COMMENT ``'处理人'``,`
``handle_result` ``varchar``(32) ``DEFAULT` `NULL` `COMMENT ``'处理结果'``,`
``handle_remark` ``varchar``(100) ``DEFAULT` `NULL` `COMMENT ``'处理备注'``,`
``trade_no` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'平台支付/退款流水号'``,`
``trade_ok_date` datetime ``DEFAULT` `NULL` `COMMENT ``'平台支付/退款成功时间'``,`
``trade_amt` ``int``(11) ``DEFAULT` `0 COMMENT ``'平台支付/退款金额(单位:分)'``,`
``fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'手续费(单位:分)'``,`
``bank_trade_no` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'银行方交易订单号'``,`
``bank_out_trade_no` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'银行方外部流水号,即商户订单号'``,`
``bank_trade_time` datetime ``DEFAULT` `NULL` `COMMENT ``'银行方交易时间'``,`
``bank_amt` ``int``(11) ``DEFAULT` `0 COMMENT ``'银行方支付金额(单位:分)'``,`
``bank_fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'银行方手续费(单位:分)'``,`
``create_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'创建人'``,`
``create_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'创建时间'``,`
``modified_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'更新人'``,`
``modified_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'更新时间'`
`) ENGINE=InnoDB ``DEFAULT` `CHARSET=utf8mb4 COMMENT=``'账单差错表'``;`
`CREATE` `TABLE` ``bill_check_pool` (`
``id` ``int``(10) ``NOT` `NULL` `COMMENT ``'主键ID'``,`
``channel_type` ``varchar``(20) ``NOT` `NULL` `COMMENT ``'渠道类型: WX,HzBank,ALIPAY'``,`
``mch_id` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'商户ID,对应表channel_account.mch_id'``,`
``batch_no` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'对账批次号'``,`
``bill_type` ``varchar``(16) ``NOT` `NULL` `COMMENT ``'账单类型:PAY,REFUND'``,`
``bill_ymd` ``varchar``(8) ``NOT` `NULL` `COMMENT ``'账单日期,格式YYYYMMDD'``,`
``trade_no` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'平台支付/退款流水号'``,`
``out_trade_no` ``varchar``(64) ``NOT` `NULL` `COMMENT ``'第三方支付/退款流水号'``,`
``trade_ok_date` datetime ``NOT` `NULL` `COMMENT ``'平台支付/退款成功时间'``,`
``trade_amt` ``int``(11) ``NOT` `NULL` `COMMENT ``'交易金额, 支付或退款金额(单位:分)'``,`
``fee` ``int``(10) ``DEFAULT` `0 COMMENT ``'手续费(单位:分)'``,`
``deleted` ``smallint``(4) ``NOT` `NULL` `DEFAULT` `0 COMMENT ``'逻辑删除,默认0--正常;1--已删除'``,`
``create_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'创建人'``,`
``create_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'创建时间'``,`
``modified_by` ``varchar``(64) ``DEFAULT` `NULL` `COMMENT ``'更新人'``,`
``modified_gmt` datetime ``DEFAULT` `current_timestamp``() COMMENT ``'更新时间'`
`) ENGINE=InnoDB ``DEFAULT` `CHARSET=utf8mb4 COMMENT=``'对账缓冲池表(平台已支付的订单不存在于当日账单中)'``;`
接口设计
提供给业务系统查询交易结果接口
定时任务
不同的支付渠道,生成对账单的时间点不一。所以我们的定时任务需要分开执行。
1)微信对账
遍历所有的微信商户,逐个商户进行对账。
2)杭州银行对账
遍历所有的杭州银行商户,逐个进行对账。
7.Deployment/部署
无
8.Monitoring and Logging/监控和日志埋点
打印各渠道发起对账的详情
监控未对账或对账失败的情况
9.Metrics/业务指标
对账结果的统计(按渠道类型、按商户号、按对账结果)
对账异常的统计(按渠道类型、按商户号)
10.Timeline and Components/任务计划和组件
xxl-job :
- wxCheckBillHandler
- hzBankCheckBillHandler
附录
关于支付,特别是本文说的对账,参考了龙果学院的源码,感谢!
https://github.com/roncoo/roncoo-pay