支付网关应该在哪里止步?界定支付、计费与订单之间的边界
要点速览
- •该架构定义了四个逻辑职责边界——支付网关、支付服务、计费和订单——它们可以作为单个应用内的模块起步,而不必是四个独立的微服务。
- •网关应只处理面向提供商的任务,如格式转换、认证和通知验证,不应包含会员等级或订阅宽限期等产品知识。
- •支付服务必须维护一个能容纳未决结果的状态模型,因为超时、延迟通知和乱序更新是常态,且超时绝不应自动触发新的扣款。
- •计费拥有财务义务,发起的每一笔收款请求都必须带有明确的金额、币种和义务引用;订单领域则决定履约条件和取消策略。
- •团队应通过演练业务变更(如增加支付提供商或调整履约策略)以及构建退款工作流来测试边界,确保审批、计算、执行和记录各归其主。

设想这样一个结账场景:支付成功了,但订单更新失败了。客户看到错误提示并再次尝试。与此同时,客服部门持有一笔交易记录,仓库没有任何已确认的订单,而财务部门需要弄清客户是否还有欠款。
该由哪个系统来解决这种局面?这类事故正是架构决策显形之处:当职责归属未定义时,每一次事故都会得到一个手工打造的修复方案,而这些修复往往堆积在最容易编写的地方。
架构应当在第一笔交易进入生产环境之前回答这个问题。否则,支付代码会逐渐吸收订单恢复逻辑、订阅规则、发票调整和履约决策。
下面列出的职责归属模型提供了一个实用的起点:让网关专注于与服务提供商的通信,为支付操作安排一个独立的归属,而将商业决策留给计费和订单。
从四项职责开始,而不是三项
在这套设计中,应将支付网关与更广义的支付服务区分开来。该模型使用四个逻辑边界,覆盖网关、支付服务、计费和订单。
把这些视为职责归属边界,而不是要求部署四个微服务的指令。如果适合团队,完全可以从单个应用内的模块开始。无论哪种方式,职责都应当被明确界定。
在评审支付网关架构时,应将组件图与决策地图配套使用:由谁决定金额?由谁发起收款?由谁记录结果?由谁授权下一个业务动作?
让网关贴近服务提供商
给网关一个窄接口契约。它应接受一个受支持的支付操作,将其转换为提供商的格式,并返回一个支付服务能够解读的结果。
这种隔离有其实际动机:各家支付提供商在 API、认证方式、字段格式和通知机制上各不相同,把这种差异收敛到一个层中,可以让系统的其余部分免受提供商具体细节的影响。
可以为其分配的职责包括:
- 校验面向提供商的请求
- 对与提供商的通信进行认证
- 将内部字段转换为提供商专有字段
- 验证来自提供商的通知
- 映射响应,同时保留有用的提供商细节
产品知识不进入该契约。网关不应需要理解会员等级、配送资格、订阅宽限期或促销套餐。传递给它的应是已批准的金额、币种、支付参考号和所需的支付方式信息,而不是产生这些数据的规则。
一个有用的评审问题:修改退货政策是否需要修改网关代码?如果是,就应重新审视这条边界。
为支付操作安排独立的归属
支付尝试及其结果应归属支付服务。对于每次尝试,应记录内部标识符、相关的提供商参考号、请求金额、币种、操作类型和状态。保留足够的历史记录,以便调查当初请求了什么以及实际确认什么。
避免把模型简化为单一的“已支付”标志。而应定义工作流所需的各类状态区分,包括未决结果。与提供商的通信有时确实存在不确定性——超时、延迟通知和乱序更新都是常态——因此状态模型需要为模糊性留出空间,而不是把所有结果都坍缩为成功或失败。
要为以下假设的序列做显式设计:支付服务发起一笔授权请求,请求到达了提供商,但响应丢失了。此时应用必须决定下一步怎么做。超时绝不应自动触发一笔新的扣款。应要求支付服务先解决或安全管理原始尝试,才允许进行下一个操作。
还应区分两类重试:
- 技术性重试:在既定安全规则下,对同一预期操作重复通信。
- 收款重试:为收回未结余额而发起的新尝试。
技术性重试的处理应归入支付集成设计。收款时机与资格由计费决定,支付服务执行已批准的尝试。
让计费决定欠款金额
计费拥有财务义务:费用计算、发票、抵扣额度和剩余余额。对于订阅产品,方案变更、按比例分摊、账单周期和收款计划等规则都应放在这里。
要求计费发起的每一笔收款请求都带有明确的金额、币种以及对被收款项义务的引用。支付服务上报结果,然后由计费判定该结果如何影响余额。
设想一张 100 美元的假设发票,附带 30 美元的抵扣额度。计费应请求剩余的 70 美元。绝不应要求网关从订阅元数据中重构这一计算。
定价越复杂,利害关系越大:每一处落在错误组件中的计算,都会变成日后必须在各系统间查找、迁移和对账的逻辑。
退款之后也应遵循同样的纪律。支付记录确定通过提供商退还了什么;计费则判定哪张发票或哪种余额调整与该笔退款相对应。
让订单决定购买的后续处理
履约和购买生命周期决策应留在订单领域。支付服务应发布支付结果,而不是下达仓库指令;订单工作流应结合自身的其他要求来解读该结果。对结果的解读既是业务判断,也是技术判断,因为同一笔已确认的支付在不同的履约模式下可能有不同分量。
一个显式履约规则的示例:只有当所需的支付条件得到满足、库存已分配且任何必要的审核已完成时,才放行订单。支付条件应根据业务模式来选择,绝不应埋在提供商响应处理器中。
取消操作同样适用这种分离。由订单决定是否允许取消以及购买应如何处理,然后通过支付服务请求相应的支付操作。避免使用含义过载的“取消”命令——它可能意味着取消订单、释放授权、退款或终止订阅。请为每个动作精确命名。
协调退款,但不让一个系统包揽所有工作
退款流程是检验边界是否成立的好方法。假设客户退回三件商品订单中的一件。应这样构建工作流:
- 退货或订单组件批准退货。
- 被指定的商业计算归属方确定可退款金额。
- 支付服务核查支付历史和适用的操作限制。
- 网关向提供商提交请求。
- 支付服务记录已确认或未决的结果。
- 计费订单各自更新自己的记录。
为每项计算指定一个归属方。不应让计费和订单各自独立计算出不同的退款金额,再让支付去二选一。
将“退款已请求”与“退款已确认”区分开来。如果提供商的结果处于未决状态,应保留这种不确定性并提供一条调查路径,而不是将整个工作流标记为已完成。
把恢复机制纳入契约
对每一个跨边界的操作,规范的内容应超出成功响应之外。应记录在案:
- 如何识别重复请求
- 哪个组件拥有权威状态
- 如何处理迟到或重复的通知
- 当下一个组件不可用时会发生什么
- 员工如何调查未决结果
- 哪些操作可以安全重试
尤其对于重复请求,提供商通常提供正是为此目的设计的幂等机制,允许同一操作被重新提交而不会被执行两次。
为每个领域分配各自的标识符,并显式地将它们关联起来:订单 ID、发票 ID、支付 ID、尝试 ID 和提供商参考号。不要强迫一个标识符代表所有关系。
可以为客服人员提供一个合并的时间线,但更正操作应始终处于归属组件的控制之下。一个方便的仪表盘不应成为覆盖支付历史或悄悄更改发票余额的许可。
用业务变更来测试边界
在批准设计之前,演练若干业务变更:
- 在不修改定价规则的前提下增加一个支付提供商。
- 在不修改网关适配器的前提下更改订阅收款时机。
- 在不重写提供商通知处理的前提下引入部分退货。
- 在不更改支付状态定义的前提下调整履约策略。
把意料之外的跨组件改动视为评审信号。某些协调是正当的;无法解释的耦合则值得警惕。
职责漂移很少会主动宣告自己;它通过一次又一次的权宜性改动悄然累积。每当引入新的提供商、支付方式或定价模型时重新执行这些演练,可以在初始设计评审之后的很长时间里让边界保持清晰。
网关应止步于面向提供商的支付通信。支付服务应拥有支付执行与凭证。计费应拥有义务与余额。订单应拥有购买及其履约。
把这些职责写进接口、恢复程序和团队归属中。仅靠一张架构图并不能让它们保持分离。