用户说”我被扣了两次钱”,这背后是一个支付产品设计的漏洞

0 评论 95 浏览 0 收藏 7 分钟

支付系统最致命的漏洞往往藏在最平静的反馈里——当用户被重复扣款却毫无察觉时,产品经理的噩梦才刚刚开始。本文深度拆解食堂支付场景中的消费冲正难题,从网络抖动引发的重复扣款,到设备端缓存+自动冲正的精妙解法,揭示了支付产品设计中那些比功能更重要的异常路径兜底逻辑。

做支付产品,你最怕接到什么反馈?

我最怕的不是系统崩了,不是接口报错,而是用户平静地发来一条消息:”我刚才被扣了两次钱。”

平静比愤怒更让人后背发凉——因为你知道,还有更多没发现的人。

这个场景叫消费冲正

问题是怎么发生的

我们的系统部署在园区食堂,支持刷卡和扫码两种方式付款。

正常情况下,用户刷卡或扫码,设备把请求发到后台,后台扣款成功,返回结果给设备,设备提示”支付成功”。整个流程一两秒完成,用户走人。

但有一种情况会打破这个流程——网络抖动。

网络不稳定的时候,设备把请求发出去了,后台其实已经扣款成功,但返回结果的数据包在网络层丢失了,设备迟迟收不到响应。

设备端显示的是什么?超时,无结果。

用户看到没有反应,自然的行为是——再刷一次。

第二次请求发出去,后台再次扣款成功。

结果就是,用户的账户被扣了两次钱。从后台日志来看,两笔交易都是正常的,没有任何异常。但用户那边,实实在在少了一笔钱。

这就是弱网络下的重复扣款问题。

听起来是小概率事件,但在人流密集的食堂高峰期,网络本身就更不稳定,发生概率会显著上升。而且这类问题不会主动暴露——只有用户自己发现余额不对,才会来投诉。更多的用户,可能根本没注意到。

我们的解决思路

发现这个问题之后,我们讨论过几种方案。

最直接的想法是在后台做幂等控制——同一张卡在极短时间内的重复请求,后台自动拦截第二笔。这个方案能解决大部分情况,但有一个问题:时间窗口怎么定?窗口太短拦不住,窗口太长会误伤正常的连续消费场景。

最终我们选择的方案是设备端本地缓存+自动冲正

逻辑是这样的:

  1. 设备每次发起支付请求,都会在本地缓存这条记录,包含交易时间、金额、卡号或扫码信息。
  2. 如果设备在超时时间内没有收到后台响应,本地标记这笔交易为”结果未知”。
  3. 当用户再次发起支付时,设备会先检查本地缓存——如果发现有一条”结果未知”的记录,在发起新支付之前,先把这条缓存记录上传到后台。
  4. 后台收到之后,查询这笔交易的真实状态。如果确认已经扣款成功,则对这笔交易发起冲正,也就是自动退款。冲正完成之后,再处理用户新发起的这笔支付。

整个过程对用户完全透明,他感知到的只是”支付慢了一点点”,不会知道后台发生了什么。

这个方案解决了什么,没解决什么

解决了最核心的问题:用户不会因为网络抖动被重复扣款,资金安全有了基本保障。

但有几个边界条件需要想清楚:

  • 设备离线时间过长怎么办? 如果设备断网超过一定时间,本地缓存的记录可能已经过期,后台对账逻辑需要有对应的处理机制。
  • 冲正失败怎么处理? 自动冲正也可能失败,需要有人工介入的兜底流程,以及对账报表里的异常标记。
  • 用户投诉了怎么办? 即使有了冲正机制,还是要有客服处理流程——用户发现被扣两次款来投诉时,客服能快速查到冲正记录,给出明确答复。

这三个问题,是冲正方案上线之后要配套做的。光有技术方案,没有运营兜底,方案只做了一半。

一个更底层的认知

做支付产品,稳定性和安全性是最基本的要求,大家都知道。

但这件事真正难的地方在于——你要提前想到用户在异常路径下的行为。

正常路径很好设计,用户按照你预期的方式操作,一切顺畅。但支付场景里,异常路径才是真正考验产品设计的地方:网络断了、设备超时、用户操作了一半离开了、后台返回了错误码用户却以为成功了……

每一个异常路径背后,都可能是一个用户的资金问题。

冲正这个场景给我最大的启发是:做支付产品,不只要设计成功路径,还要为每一个可能的失败路径,设计好兜底方案。

这个习惯,我后来在做园区其他系统的时候,一直在用。

支付产品最贵的教训,往往不是系统崩了,而是用户默默少了一笔钱,你却浑然不知。

本文由 @园区产品人 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自作者提供

该文观点仅代表作者本人,人人都是产品经理平台仅提供信息存储空间服务

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 目前还没评论,等你发挥!