支付回调是票务系统里最容易被忽略的一条链路:它不通,游客已经扣款,系统里却还是一条待支付订单。掉单本身不多,集中在网络抖动的几分钟里,一次可能几十单。真正决定现场混乱程度的,是发现它的速度和补单的规矩。
一、三种成因,三种现象。第一种是超时,回调地址响应慢或被安全策略拦掉,支付侧重推几次都拿不到成功应答,之后就不再发,表现是订单永远停在待支付。第二种是重复通知,同一条流水被推了多次,没做幂等就出成多张票,报表对不上,闸机侧能看到同一订单两组码。第三种是签名不符,多半是改了参数顺序、加了字段或者字符集不一致,整批回调全被判无效,这种最危险,短时间内一单都进不来。

二、掉单之后库存和票是什么状态。按下单扣减的模型,未支付订单到时限就被释放,占用量退回可用量;等补单回来时,原时段可能已经满员。票号是在支付确认那一刻才生成的,所以掉单状态下不存在半张票。补单不是把订单标成已支付,而是要重新走一次占位、出票、通知,每一步都可能失败,每一步都要能重入。
三、主动查单要跑多久。不能只等回调。常规做法是对待支付订单做轮询查单:下单后前两分钟每三十秒查一次,之后每两分钟一次,十五分钟后停止查询,三十分钟关闭订单。批量查单一次不要超过两百条,避开支付侧限流。查单结果与回调结果共用同一个处理函数,两条路指向同一次出票动作,靠流水号做唯一约束,谁先到谁生效。
四、补单的顺序。先判订单是否已经出过票,出过的标注重复通知并结束;未出票的先扣库存,扣不动就挂起,不要把订单直接置成失败;扣成功再出票,出票成功后才发通知。通知放最后,是为了避免游客收到票码而票务系统里其实没有这张票。

五、人工核账要留的凭证。查单和补单都失败的那部分只能人工处理,要留四样:支付侧流水号、本地商户订单号、回调原文、处理过程记录。凭证存在票务系统的独立表里,不写进订单备注,备注会被后续修改覆盖。权限给值班经理以上,操作要第二人复核,处理完在记录上注明结果码与经办人。
六、现场要给一个当场结论。掉单最集中的后果是窗口排队:手机提示扣款成功,页面查无此单。景区在检票通道旁要有一个能确认的岗,凭流水号查本地有没有单,有单未出票就补,无单就登记后先放行入园、事后回补,别让游客在闸机前等一个结论。
七、回调处理本身要写轻。很多掉单是自家造成的:回调函数里同步做扣库存、出票、发短信三件事,遇到库存行锁等待就超时,支付侧拿不到成功应答,于是判定这次通知失败,后面不再推。可用的写法是回调里只做三件事,校验签名、按流水号落一条待处理记录、立即应答,处理时限压在两秒以内;出票与通知放异步队列,队列积压时先看时长,超过十分钟就该告警,而不是等到游客到闸机才发现。
