我们的系统里,用户账上有一个余额。这个余额的定义,我们花了不少力气才定死:
一律是美元「分」为单位的净额。
一句话,但它挡掉了后面所有的对账争吵。
净额是什么
我们自己设定的商品价格。
不含买家所在地的税、不含收款平台的手续费差额、不含换汇损益。
一笔交易实际上有几个数
一笔跨境交易,能数出至少五个不同的金额:
- 买家看到的标价
- 买家实际扣款的金额(加了他当地的税)
- 他用本币付的话,他银行按什么汇率换的
- 收款平台抽完之后打给我们的
- 我们的商品定价
只有第 5 个进用户余额。 其余四个各有各的去处,但都不影响这个数。
为什么必须这样
因为其余四个数,我们控制不了,而且会变。
- 买家属地的税率,各国不同,还会调
- 汇率每天在变
- 平台费率会调整
如果余额跟着这些数走,那同一件商品,两个用户扣掉的额度会不一样——而他们买的是同一样东西。
更糟的是,同一个用户在不同时间买同一样东西,扣掉的也不一样。 这件事解释不清楚。
退款的时候最能看出好处
退款只退发起时锁定的那个净额。
平台回传的实际金额只用来核对,对不上就报警,但不用它来算账。
这一条听起来有点固执,但它的效果是:退款金额永远和当初扣的一样。 不会出现「你买的时候扣了 10,退的时候只退 9.7,因为汇率变了」这种解释起来极其痛苦的情况。
换汇的差额和税,由收款方自己消化。 这是成本,不是用户的问题。
「先定不变量,再写功能」
这是这件事真正的教训。
不变量是一句在任何情况下都成立的话。定下它之后,每写一个新功能,只需要问一句:「这个功能会不会破坏它?」
而如果没有不变量,每个功能都要单独想一遍「这里的钱该怎么算」——而十个人想十遍,会得到十一种答案。
怎么检验一条不变量
拿三个真实会发生的场景去撞它:
- 退款
- 汇率大幅变动
- 平台费率调整
三个都成立,才算数。
如果某个场景下不成立,改定义,别加特例。 一条需要三个特例才成立的不变量,不是不变量,是一句愿望。
别处的对应
咨询和研究里也有对应的东西:同一个指标的口径。
「活跃用户」这个数,是按登录算、按有操作算、还是按停留时长算?这三个定义会给出三个差很远的数。
而最贵的不是选错了哪一个,是不同的报告里用了不同的那一个,而且都叫「活跃用户」。
先定口径,再出数。 顺序反了,那份报告里的每一个数都要重新问一遍。