团购网站设计:退款申请不等于资金已退回

用户提交退款后,系统若立即显示已退款,可能把审核、通道处理与到账几个阶段混为一谈。

edyang · 2014-10-15

用户提交退款后,系统若立即显示已退款,可能把审核、通道处理与到账几个阶段混为一谈。

本文使用团购业务的虚构订单与关系数据模型,伪代码不连接真实支付。团购把用户购买、平台收款、商家履约与结算连接起来。下单、支付、成团、消费和退款必须保留各自状态,不能用一个布尔字段表达整个生命周期。

1. 从现象推到机制

退款有自己的业务身份和状态,关联原支付流水与金额上限。多次部分退款的累计值不能超过已确认可退金额。通道超时进入待查询,不能在未知结果下生成另一笔新退款。

团购网站设计:退款申请不等于资金已退回的处理、反馈与观察关系

图中的四个节点分别表示退款申请、额度预占、通道处理、最终确认。顺着实线检查输入如何变成输出,再沿虚线核对反馈条件。图示表达的是本文讨论的关系,不是实测曲线或现场截图。

2. 最小实现与输入条件

先冻结本次退款额度,再按稳定退款号请求通道。数据库事务只保护本地额度和状态,外部结果通过通知与查询收敛。

1
2
3
REQUESTED -> APPROVED -> PROCESSING
PROCESSING -> SUCCEEDED | FAILED | UNKNOWN
refund_total <= verified_refundable_amount

先列业务事件与允许的状态迁移,再用少量匿名订单演算。涉及金额的地方保存成交快照,涉及并发的地方使用数据库约束,外部支付行为按支付协议另行实现。

3. 用对照区分原因

下表列出需要主动改变的条件。预期行为来自机制分析,复现时还需记录运行环境和原始输出,不能将预期当作已经测得的结果。

改变的条件 需要验证的行为
用户重复提交同一申请 应关联已有退款单
部分退款并发发生 累计退款不能超过上限
通道响应超时 保留未知状态并查询

对照 1:用户重复提交同一申请

应关联已有退款单。观察时记录申请身份与退款号。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 2:部分退款并发发生

累计退款不能超过上限。观察时记录额度预占与数据库约束。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 3:通道响应超时

保留未知状态并查询。观察时记录稳定退款号对应的通道结果。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

4. 适用条件与取舍

界面要让用户知道当前阶段和后续预期,但不应承诺无法由系统控制的具体到账时间。财务确认与用户提示要用相同状态依据。

一次正常运行只覆盖了这组输入。继续扩展前,应把上面的异常对照保留下来,尤其关注“通道响应超时”这一分支。后续方案若改变了图中任意一处依赖,原来的判断就要重新验证,不能仅凭界面相似或返回值相同沿用结论。

同月延伸

参考资料

  1. Spring 3.2.6 事务管理
  2. Hibernate 4.3 事务与并发