校园上网计费系统对接一卡通时踩过的坑
去年年底接了一个高职院校的项目,学校要求把新上的校园上网计费系统和现有的一卡通平台打通,学生刷校园卡就能开通网络,余额不够了自动停机。听起来是个常规需求,做起来前后折腾了快两个月。把过程中遇到的几个问题记下来。
接口文档和实际返回的数据经常对不上
这个项目的一卡通是本地一家小厂商做的,接口文档三年前写的,中间系统升级过两次,有些字段含义已经变了。最典型的是余额查询接口:文档写的是以"分"为单位返回整数,实际升级后改成了以"元"为单位的字符串。我们按文档做了转换,结果测试时一个学生充值100元,计费系统这边显示1块钱。排查的时候先怀疑自己代码写错了,反复核对计算逻辑,最后才发现源头数据格式就不对。
后来学乖了:不管对方文档怎么写,第一件事是把所有接口实际调一遍,拿真实返回值和文档逐字段比对。字段名一样不代表含义一样,数据类型更不能靠猜。尤其是一卡通这种系统,很多学校用的是定制版本,官方标准接口可能被改得面目全非。
扣费时机和一卡通的账期对不上
校园上网计费系统的扣费一般是实时的——用户上线扣一点,用多少扣多少,用完就断。但一卡通系统通常按天甚至按月结算,内部有账期概念,每天的明细要等晚上批量处理后才算正式入账。这两个节奏天然冲突。
我们遇到的情况是这样的:学生A早上8点刷校园卡充了50块钱到网费账户,充完马上开电脑上网没问题。但中午11点一卡通那边做了一批账务调整(之前有一笔充值记录需要冲正),导致这个学生在计费系统里的临时余额被回滚了,网络没断(因为我们的扣费逻辑基于本地缓存)。等到下午3点学生又充了一次,两笔账一对,系统判定欠费直接把他踢线了。学生投诉过来,查了半天日志才搞清楚来龙去脉。
解决方案是加了一个"余额同步窗口"机制:每次扣费前去一卡通实时查余额,不依赖本地缓存做最终判断。代价是每笔扣费多一次接口调用,响应时间增加了大概80毫秒。高并发场景下这个开销可以接受。如果对延迟特别敏感,可以做折中:正常情况用缓存,每隔N分钟强制刷新一次,或者余额低于某个阈值时触发实时查询。
退费流程比想象中复杂
学生退宿、转专业、休学复学都会涉及网费退费。纯内部的计费系统里退费就是数据库操作减个余额的事。一旦跟一卡通对接,退费意味着要把钱从计费账户原路退回到一卡通账户,再由一卡通退到学生的银行卡或现金账户。这涉及两个系统的账务一致性。
上线第二周就出了问题:一个学生办了退宿,管理员在计费系统点了"退费",钱从计费账户扣掉了,但一卡通那边的充值接口超时了,钱没有回去。两边账不平,财务对不上,最后只能人工补录。事后分析原因:当时一卡通在做日结备份,接口响应时间从平时的200毫秒飙升到了30秒以上,我们超时设置只有5秒。
现在的做法是:所有跨系统的资金操作必须幂等且有补偿机制。退费请求发出去后,对方返回成功就以对方为准;超时或失败则进入重试队列,每5分钟试一次最多6次;6次都失败标记异常人工介入。同时每天凌晨跑对账脚本,比对两边当天所有流水,差异数据自动报警。
认证信息同步延迟导致重复扣费
一卡通和计费系统之间除了资金往来还有用户状态同步。比如学生挂失校园卡,一卡通冻结卡号,这个状态需要同步到计费系统让对应账号下线。但同步不是即时的,一般有几分钟到几十分钟的延迟。
有一次更极端:学生丢了卡去挂失,一卡通已经冻结了,消息队列积压导致计费系统过了两个小时才收到通知。这段时间他室友借了他的账号在用网(密码没改),产生了大约4块钱的费用。学生不服气,说卡挂失了为什么还收费。技术上我们没错——挂失生效前的使用确实应该计费,但从用户体验角度很难解释通。
最后的妥协方案:挂失后产生的费用如果金额在10块钱以下,允许管理员一键豁免并写入备注;超过的走审批流程。同时把轮询间隔从30分钟改成5分钟,再加Webhook让一卡通在状态变更时主动推送,双保险。
联调测试环境和生产环境是两回事
上面这些问题有一半其实能在测试阶段发现。但现实是学校给的一卡通测试环境数据量很小并发也很低,很多边界case根本测不出来。日结备份导致接口超时的问题在测试环境里根本不存在——测试环境不做日结。
所以现在我的习惯是:测试环境验证功能正确性没问题,但上线前一定要安排生产环境的灰度测试。挑非高峰时段(周日晚上),找十几个真实用户参与,让他们正常充值、上网、模拟挂失和退费。观察至少2小时确认无异常后再全量开放。多花这一个晚上能省掉后面好几周的运维麻烦。


