套餐与计费:价格、额度、订单快照三者的关系
计费系统里最容易被忽略、后果又最严重的,是「订单快照」。运营调整了套餐,结果已经创建甚至已经支付的订单跟着变了——这时候该以哪一份为准?
下单那一刻把套餐内容快照进去
价格、每日额度、并发数、有效期,全部在创建订单时快照。之后套餐怎么改,都不影响这张已存在的订单。
正确做法是下单那一刻把套餐内容(价格、每日额度、并发数、有效期)快照进订单。
之后套餐怎么改,都不影响这张已经存在的订单。
金额用整数分,额度用整数
浮点数在累加时会出现精度误差,账目对不上是这类系统最致命的问题。所有的金额和额度计算都要有溢出检查。
价格用整数分存储,额度用整数(并且检查溢出),不能用浮点数。
所有金额和额度的计算都要有溢出检查。
续费要保留的三样东西
原 Key 续费的最大价值是「不用换 Key」。所以今日已用量、已兑换的额外额度、原到期时间的叠加规则都得保留,否则用户会觉得被坑了。
原 Key 续费的最大价值是「不用换 Key」,因为换 Key 意味着所有依赖它的配置都要改。
所以续费时要保留:今日已用量、已兑换的额外额度、以及原到期时间的叠加规则。
支付成功的判定只有一个来源
只有验签通过、且应用、商户、订单、金额、交易状态全部核对无误的支付宝通知或查询结果才能确认付款。浏览器返回地址和前端轮询都不算。
兑换失败通常是「码不存在 / 已使用 / 过期」;游客购卡失败可能是「支付未确认 / 凭证过期 / 目标 Key 已停用」。
这两种失败要给不同的提示,否则用户会反复重试一个根本不会成功的操作。
实操:支付回调的验签与幂等处理骨架。
套餐的实际效果最终体现在「今天还剩多少」上,把它做成用户最常打开的页面。
import hashlib, hmac, json
def verify_notification(raw_body: bytes, headers: dict) -> bool:
# 1) 验签:用平台公钥对原始报文做 RSA/SM2 验签,
# 任何字段被改动都会导致验签失败
# verify_signature(raw_body, headers["signature"], PLATFORM_PUBLIC_KEY)
# 2) 核对业务字段:应用、商户、订单号、金额、交易状态
data = json.loads(raw_body)
if data["app_id"] != APP_ID or data["seller_id"] != SELLER_ID:
return False
if data["total_amount"] != str(expected_amount(data["out_trade_no"])):
return False
return data["trade_status"] in ("TRADE_SUCCESS", "TRADE_FINISHED")
def handle(data):
# 3) 幂等:同一笔订单重复通知时直接返回成功,
# 绝不能重复加额度或重复发卡
order = load_order(data["out_trade_no"])
if order.paid_at:
return "SUCCESS"
with transaction():
settle_order(order) # 结算 + 加额度 + 发卡,原子完成
mark_paid(order, data)
return "SUCCESS"
# 浏览器返回地址和前端轮询到的状态,都不能作为支付成功的依据。
几个容易踩的坑
- 价格用浮点数存储,累加后账目对不上。
- 套餐改了之后已创建的订单也跟着变,用户看到的和付的不一样。
- 续费时没保留今日已用量,等于凭空多给了一份额度。
- 奖励金被撤销但已经被抵扣,产生了资损。
小结
技术上的事大多不复杂,难的是把每个异常分支都想清楚。把这份清单存下来,下次排障会快不少。
对具体能力有疑问,可以直接看接入文档 https://aiminis.info/docs/api-integration,里面把 Base URL、鉴权方式和各协议的可选范围都列清楚了。
- 点赞
- 收藏
- 关注作者
评论(0)