日终对账系统设计:CSV 字段对照、两轮比对算法与数据库设计

对账系统的三张表、两轮比对和七种差异 对账系统是支付服务的最后一道防线——回调可能丢、消息可能漏、金额可能错,这些不会自己暴露。每天拿渠道的官方记录和自己数据库里的记录对一遍,丢钱多钱才能发现。 本文从零梳理一个日终对账系统的设计:数据表怎么建、两渠道 CSV 字段怎么映射、算法怎么做才能既快又不依赖 JOIN。 一、对账系统的心智模型 对账就一句话:渠道说每天收了多少钱,我们说每天收了多少钱,两边对一下,不一致就逐笔查。 flowchart TD cron["@Scheduled 次日 10:30"] --> download["下载渠道对账单 CSV"] download --> parse["解析 CSV → 批量写入 recon_temp"] parse --> total_check{"总额校验:渠道总额 = 我方总额?"} total_check -->|"相等"| done["对平 ✅ 关闭批次"] total_check -->|"不等"| detail["逐笔对比(HashMap 撮合)"] detail --> result["差异写入 recon_result"] classDef startEnd fill:#701a4c,stroke:#e11d48,stroke-width:2.5px,color:#fce7f3,font-weight:bold classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#e5e7eb classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:2px,color:#ede9fe,font-weight:bold classDef data fill:#052e16,stroke:#16a34a,stroke-width:2px,color:#bbf7d0,font-weight:bold class cron startEnd class download,parse,detail process class total_check condition class done data class result process 选择次日 10:30 触发是因为两家渠道的对账单都在次日 10 点前生成完毕——去早了拿不到文件。 ...

十一月 2, 2023 · 4 分钟 · 759 字 · yaomingye

微信支付 vs 支付宝:聚合支付渠道集成中的十一处暗坑

渠道集成的痛:微信和支付宝处处不一样 聚合支付系统要同时对接支付宝和微信支付。初看两边的官方文档,觉得差不多——都是「下单→拿凭证→前端拉起→回调通知」这个流程。真到写代码的时候才发现,每一步都不一样,甚至同是微信生态,App 和小程序之间还有差异。 这篇文章把两渠道在 SDK 选型、下单参数、签名机制、回调处理、退款流程、对账单格式六个环节的具体差异整理出来,顺便也记录微信 App 和小程序之间那几处让人想砸键盘的细节。 一、SDK 选型和核心对象 先看 SDK 本身的差异,这决定了后面所有代码怎么组织。 支付宝 微信支付 Maven artifact alipay-sdk-java:4.40.308.ALL wechatpay-java:0.2.17 核心对象 DefaultAlipayClient (就是 HTTP 客户端) RSAAutoCertificateConfig (配置 + 签名 + 证书管理) HTTP 层 自带老版 HttpClient 自带 OkHttp,可注入自定义实例 证书管理 无(公钥手动配) AutoCertificateService 自动下载平台证书 + 后台线程轮换 Service 封装 无,裸调 client.execute(request) AppService / JsapiService / RefundService 封装请求-响应对映 ⚠️ 新手提示:支付宝的 DefaultAlipayClient 就是个 HTTP 客户端,一行 new 就行。微信的 RSAAutoCertificateConfig 是重量级对象——创建时会初始化证书下载、后台轮询线程,要通过工厂 + Caffeine 缓存复用,别每次请求都 new。 两者最大的思维差异:支付宝把 SDK 当 HTTP 工具用,微信把 SDK 当基础设施用。 ...

十月 31, 2023 · 4 分钟 · 641 字 · yaomingye

支付系统设计评审:一次增量讨论补足的五个缺陷

一份支付设计方案,是怎么被审出五个洞的 某开发者在设计一套统一支付服务。初版方案自认为考虑周全:支付订单表、退款表、渠道配置、对账批次,表格一张比一张漂亮。然后跟组里人过了一遍设计,被一个问题接一个问题地问到改稿——每一问都补出一个之前没想透的缺陷。 这篇就把这次增量讨论里补上的五个点记下来。它们不是"支付系统特有的冷知识",而是任何一个会分库分表、会对账、要复用的系统都可能踩的坑。 场景:一个要复用的统一支付服务 先交代背景。这套支付服务的目标是: 从"模拟支付"改造成真实支付——聚合支付宝、微信支付、微信小程序支付,未来还可能接更多国内渠道 拥有自己的数据库(此前完全没有,支付数据存在别处) 带一套每日对账系统——拉取渠道对账单、解析、批量入库、逐笔对比、算差异 日后作为独立支付服务接入其他项目(商城只是第一个业务方) 正是"要复用"和"要对账"这两点,引出了下面五个洞。 洞一:对账的逐笔对比写了 JOIN 初版对账设计里,逐笔对比是这么写的: -- 渠道账单临时表 LEFT JOIN 本地支付订单表 SELECT t.trade_no, t.amount, o.pay_amount, ... FROM recon_temp t LEFT JOIN pay_order o ON t.trade_no = o.merchant_order_no; 乍看没毛病——两张表同库、有公共键。但评审的人问了一句:"pay_order 分库分表之后,这个 JOIN 还能跑吗?" 不能。跨表 JOIN 需要分片键能路由到同一分片,而 recon_temp 和 pay_order 的分片键不同(一个按批次、一个按用户),JOIN 会退化成全分片广播扫描——每一个分片都扫一遍再合并,数据量一大就是灾难,更别说分片规则一变直接报错。 📌 前置知识:分库分表后,跨表 JOIN 要保证两张表的行落在同一个分片(同分片键)才能路由;分片键不一致时只能广播到所有分片再内存合并。 改法:双批查询 + 内存撮合。 flowchart TD A["渠道对账单文件"] -->|"解析"| B["recon_temp 当日明细"] C["pay_order 支付订单表"] -->|"按渠道×时间窗查询"| D["当日支付单子集"] B -->|"批查加载"| E["渠道侧内存 Map"] D -->|"批查加载"| F["平台侧内存 Map"] E -->|"按 merchant_order_no 匹配"| G["逐笔撮合"] F -->|"按 merchant_order_no 匹配"| G G --> H["命中 / 仅渠道 / 仅平台"] H --> I["差异写 recon_result"] classDef process fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#e5e7eb; classDef data fill:#052e16,stroke:#16a34a,stroke-width:2px,color:#bbf7d0,font-weight:bold; classDef condition fill:#2a1147,stroke:#a855f7,stroke-width:2px,color:#ede9fe,font-weight:bold; class A,D process class B,C,F data class G,E condition class H,I process 两个单表查询各自按自己的分片键路由( recon_temp 按 batch_no 、 pay_order 按 user_id ),各自只取撮合需要的列,在内存里建 HashMap 按 merchant_order_no 精确匹配。全程无跨表 JOIN——这就是"无联表"原则,也是后续分库分表的前提条件。 ...

十月 27, 2023 · 3 分钟 · 621 字 · yaomingye
Cat Radio