给彩票网站做代理违法吗,wordpress文章与页面关联,直播软件视频软件,wordpress注册直接输入密码转载自 支付对账系统怎么设计 支付对账系统是整个支付清结算体系中具体基础性意义的一个环节#xff0c;是确保支付平台与各类第三方支付渠道数据一致性的关键系统#xff0c;是商户资金结算、资金划拨、资金报表等逻辑准确运行的重要前提。 支付对账涉及账单下载处理、核心…转载自 支付对账系统怎么设计 支付对账系统是整个支付清结算体系中具体基础性意义的一个环节是确保支付平台与各类第三方支付渠道数据一致性的关键系统是商户资金结算、资金划拨、资金报表等逻辑准确运行的重要前提。 支付对账涉及账单下载处理、核心对账、差错处理等诸多细节逻辑同时根据交易量大小的不同需要处理的数据量规模也不尽相同需要在数据处理时进行一些比较细致地思考。在本文中作者以单渠道日成功交易订单量300W左右规模为背景以较少的系统资源占用为目标给大家介绍下系统的实现细节。同时对于数据不断增长的情况下支付对账系统该如何演进作者也会结合自身的实践与思考与大家一起交流探讨 账单下载处理
对于公司自建支付系统来说一般会根据业务的复杂程度不同对接多个支付渠道。对于互联网公司而言常见的渠道会对接支付宝、微信、ApplePay等而金融类的公司则更多会对接银联、易宝、快钱这类银行卡代收付通道也会有直接对接银行渠道的海外则是如Adyen、Stripe等这类国际支付公司。
各个渠道的账单接口下载形式账单数据格式等会存在不同的差异而如果完全按照第三方的原始账单格式存储对于后续的账单数据处理逻辑会比较复杂并且每增加一个新的支付渠道账单存储表都需要根据渠道账单结构新建增加开发工作量所以并不合理。
为了实现账单数据的统一格式化我们需要将各渠道原始的账单文件进行统一标准化转化同时也需要设计一张相对通用的渠道账单数据表来存储不同渠道账单格式化后的数据具体结构如下 另外在进行账单数据存储时为了提高效率需要将标准账单文件格式设计得与表结构一致这样在完成数据转换后可以直接将文件load/copy到数据库中这样速度会快很多而考虑数据规模会增长得超级大这张表也可以存储在Hive上只是对于大部分公司的交易量来说这么做会有一些技术实现上的成本目前作者采用的是Postgresql数据库版本为9.5.4作为账单存储库数据规模大概已在10亿条左右表示暂无压力。
此外对于账单的下载逻辑也需要考虑防重逻辑的即同一个渠道账号的同一天的账单数据不能重复下载和入库所以除了存储具体的账单数据外也需要设计一张账单下载记录表用于存储那个渠道账号哪一天的下载情况并在账单下载任务启动起根据该表进行防重复下载逻辑判断。这里还需要说明下在下载原始账单和转化标准账单时由于账单文件读写都是本地磁盘为了统一集中管理这些账单文件、也为了数据安全需要采用统一文件存储服务如可以采用腾讯云的CFS文件存储或者自己搭建一个文件夹共享服务。
账单下载记录中也需要存储原始账单文件及标准账单文件的下载位置平时基本上不会用到只是为方便日后用于数据问题排查需要检索原始文件时方便查找及数据重新加载。 核心对账逻辑
完成相应渠道的账单下载任务后系统就可以根据各个渠道的特点及对账平台任务系统的排班逻辑启动相应渠道的对账任务了例如微信账单在10点左右开始下载预计完成时间为10分钟以内那么就可以将微信的对账任务安排在11点开始执行以此类推各个渠道根据自身实际情况确定对账时点。
从逻辑上看对账的形式是为了完成A表与B表的集合如下图所示 一般情况下与第三方支付渠道进行对账时会以平台订单号作为关联条件将账单表中的数据与支付平台订单表的数据进行full join得到一个集合全量得到的集合会是一个交集、两个补集。处于交集部分的数据集说明根据订单号是可以对应上的但是我们还需要进行订单金额的比对如果一致则说明无差错对平的数据集按照结算数据要求取账单数据平台订单数据业务字段全集直接生成对账明细表而不一致的则需要生成差错类型为“金额不一致”的差错数据并记入对账差错数据表。
处于账单数据这一侧的补集属于长款差错即这部分数据在第三方账单中存在而在支付平台订单成功数据集中未找到导致这部分差错数据的原因可能有跨天交易的情况、也可能是线上测试数据导致、或者属于系统层面的订单掉单在后面差错处理中会详解介绍。
而处于支付平台订单这一侧的补集则属于短款差错即这部分数据在支付平台成功订单中存在而在渠道对账时点的账单中不存在造成这部分差错的原因有可能是跨天交易情况导致也有可能是第三方结算错误具体原因需要在设计差错处理逻辑是根据不同情况进行处理。
在了解到对账的逻辑后那么在具体进行系统编码时应该如何处理呢
按照上述逻辑我们需要将账单数据表与支付平台订单表进行full join但是由于账单表我们是存储在Postgresql上的而支付系统所采用的数据库可能是Mysql或Oracle总之从系统拆分的角度看一般是不会将对账处理与在线支付订单放在一个库中的即便在一个库直接关联账单表与支付订单表也是不明智的一方面这样可能会影响实时支付系统的稳定性另外这些表的数据都是不断增长的随着数据的积累会也会导致对账数据查询变慢。
所以在进行某个渠道对账时需要根据条件将账单数据、支付平台订单数据分别清洗到两张中间表中分别叫做账单待对账中间表A表、订单待对账中间表B表然后通过这两张表进行full join操作这样可以确保对账逻辑不影响别的业务同时这部分数据可以在日终完成对账任务后定期清理掉确保中间表数据规模处于可控状态。
在代码层面通过A表 full join B表后会得到一个结果集如果这个结果集数据比较大系统没有采用SparkHive这种方式话通过传统编程方式则需要对查询进行分页考虑到数据逐条对账处理速度较慢可以一页获取数据条数稍多一些例如一次取5W条然后在系统内部采用多线程方式对数据集分割后并行处理每个线程按照特定的对账逻辑执行得到对账明细结果集或差错结果集后批量存入对账数据库。
核心参考代码如下
public boolean execute(ShardingContext shardingContext) {boolean exeResult true;long startTime System.currentTimeMillis();String paramValue shardingContext.getJobParameter();CheckRequest checkRequest (CheckRequest) JsonUtils.json2Object(paramValue, MbkCheckRequest.class);//对账批次请求信息MapString, Object paramMap new HashMapString, Object();paramMap.put(batchNo, checkRequest.getBatchNo());//批次号paramMap.put(channel, checkRequest.getChannel());//渠道paramMap.put(tradeType, checkRequest.getTradeType());//交易类型//关闭PG执行计划解释(PG9问题)unionCheckOrderMapper.enableNestloopToOff();int totalCount unionCheckOrderMapper.countByMap(paramMap);//总数int pageNum 50000;//pageSize,每页5W条数据int fistPage 1;int offset 2;PostgreSQLPageModel pm PostgreSQLPageModel.newPageModel(pageNum, fistPage, totalCount);int page pm.getTotalPage();BlockPoolExecutor exec new BlockPoolExecutor();exec.init(); //初始化线程池ExecutorService pool exec.getMbkBlockPoolExecutor();ListFuture? results Collections.synchronizedList(new ArrayListFuture?());// 并行计算结果数据类型定义for (int i 1; i page; i) {//分页进行数据Fetchlong startTime2 System.currentTimeMillis();pm.setCurrentPage(i); //设置当前页码offset pm.getOffset();paramMap.put(pageSize, pageNum);paramMap.put(offset, offset);ListUnionCheckOrder outReconList unionCheckOrderMapper.selectByMap(paramMap);MapString, List? entityMap groupListByAvg(outReconList, 1000);CountDownLatch latch new CountDownLatch(entityMap.size());OutReconProcessTask[] outReconProcessTask new OutReconProcessTask[entityMap.size()];IteratorMap.EntryString, List? it entityMap.entrySet().iterator();try {int j 0;while (it.hasNext()) {ListUnionCheckOrder uList (ListUnionCheckOrder) it.next().getValue();outReconProcessTask[j] new OutReconProcessTask(latch, uList);results.add(pool.submit(outReconProcessTask[j]));j;}latch.await();} catch (InterruptedException e) {e.printStackTrace();}long endTime2 System.currentTimeMillis();}unionCheckOrderMapper.enableNestloopToOn();//开启PG执行计划解释exec.destory();long endTime System.currentTimeMillis();return exeResult;
}考虑完成对账、以及后面会涉及的处理差错其结果都是产生对账明细数据而对账明细数据是渠道vs平台后最为准确的资金数据对于后续的商户清分、资金清算、结算都具有重大意义所以复杂查询的频率会比较高并且数据的使用时间范围也比较大对于交易量比较大的公司一年的支付数据量可能达到数十亿规模在数据存储方面可以考虑采用TIDB这类分布式关系型数据库来存储对账结果相关的数据这样后续的数据处理逻辑效率会提高很多。可能有人会问为什么不直接使用Hive进行查询这是因为Hive的单条查询和批量查询的效率是一样的所以并不太适合实时查询而如果需要将对账、结算等数据通过管理系统进行管理涉及的查询场景比较多所以综合考虑使用分布式关系型数据库会更合适一些。
差错处理逻辑
对账逻辑执行完成后会产生一部分对账逻辑执行过程中系统无法匹配的对账差错数据这部分数据会在对账完成后记录在对账差错信息表中差错信息表根据差错类型记录该笔差错的详细信息除包括渠道类型、金额、交易时间等关键信息外还会对差错进行分类、定义特定的差错类型编码。
根据根据不同情况差错大概可以分为三类长款、短款、金额错误。其中长款根据对账处理方式的不同可以分为“渠道成功平台订单不存在”、“渠道成功、平台状态非成功”两种情况从生产实践上看因为支付系统中会存在比较多的支付失败订单而国内支付渠道的账单多数情况下只会提供用户支付成功的账单数据所以在实际进行对账时在A中间表、B中间表清洗的都是双方认为成功的订单数据在这种情况下产生的长款类型也就只有“渠道成功平台订单不存在”这种类型了。
而对于短款来说就是在当日的账单数据中没有匹配到差错类型也就是“平台成功账单数据不存在”。而“金额不一致”的情况相对少见主要出现在如微信、支付宝进行营销活动时造成的支付平台订单金额与第三方不一致的情况另外存在订单部分退款时如果支付系统订单模型没有很好地满足这类情况的话也会导致对账金额不一致的情况发生。
针对不同的差错类型情况需要根据根据系统实际情况设计相应地处理流程。例如对于长款差错需要识别其产生的原因如果是因为交易跨天导致的例如交易在平台时间为某日的23:59:59秒那么发送到第三方渠道的时间可能已经是第二天的00:00:01秒这样那么在第一天对账是会产生一笔短款差错此时只需要消除这笔差错、同时生成对应的对账明细即可。而如果是因为支付平台状态未处理成功则是系统掉单问题导致除了正常消除这笔差错、产生对应的对账明细数据外还需要通知支付系统进行状态更新操作其涉及的业务逻辑还需要根据整个支付平台的流程设计触发商户回调、或者还需要通知账务系统进行补记账操作。
总之需要根据具体的差错类型及原因结合整个支付系统的流程来保证系统间数据的一致性以下是作者根据通用场景设计、根据不同差错类型设计的处理流程供大家参考
一、长款差错通用处理流程 在以上长款处理流程中关于跨天交易情况的区分这里有一个细节的设计在判断完支付订单状态为成功后之所以在判断是否在T-1天或者T-N天是否存在同一笔匹配的短款差错之前判断是否存在对账明细的情况是因为在系统设计时考虑订单结算的实时性允许对于短款差错采取T1日的处理方式具体方式会在短款处理流程中说明。
二、短款差错通用处理流程 在以上短款差错处理流程中大部分公司为了解决跨天交易的问题会要求对短款差错挂账一天也就是说要求在T2日对短款进行处理因为在T2日时账单和对账出现的长款差错可以正常抵消处理。在这里的设计中允许在T1日处理即在没有第三方账单信息的情况下通过订单查询接口进行对账并默认将这笔交易的渠道结算时间设置为T2对于支付订单国内大部分渠道这么设置是正好可以匹配的而对于退款可能渠道的结算时间为T3甚至更长这种情况会导致对账明细中的渠道结算时间与实际渠道结算时间存在一定的偏差而在后面T3或更长的账单日时产生的长款差错处理可以采取更新策略即根据实际的渠道结算时间更新对账明细表的结算时间这样就确保了数据渠道结算时间的准确性。
上面的情况如果考虑商户结算逻辑可能需要对这种情况做点特殊标记如设置渠道结算时间、商户结算时间、或渠道实际结算时间来处理当然如果为了确保资金结算的稳妥性也可以采取挂账T2/TN处理的方式这一点由流程和规则决定系统只是额外提供了一种逻辑处理方式而已。
三、金额不一致差错处理
正常情况下金额不一致一般以第三方渠道账单为准从账务一致性的角度考虑可能也需要在流程中加入调账逻辑具体的流程可根据具体的产品规则设计。
系统演进化方向
对于对账系统的演变主要需要从考虑数据的增长、任务资源的合理配置以及系统监控这几个方向去考虑。如果数据量持续增长到传统方式已无法处理可以采用Spark StremingHiveTidb等组合技术方案进行改进。
此外对账系统是一个以定时任务为主的系统对于定时任务处理框架的选择可以采用分布式任务框架推荐elasticjob/saturn自定义任务逻辑的方式综合处理如有些任务存在先后顺序如果框架本身不提供这类处理功能则需要通过业务规则限制。
而从系统监控角度由于任务系统不同于实时交易流程具有执行时间长、数据操作范围广泛的特点除了进行正常的进程级别的监控外对于各个任务的执行情况也需要进行比较细致的监控这部分可以通过监控打点等方式综合解决而对于业务异常日志的监控则可以通过Sentry等日志监控工具进行监控。