1. 交易流程工程导论

在Android移动交易系统的开发中,很多团队把注意力集中在界面交互、接口联调或支付SDK接入上,却忽略了一个更根本的问题:一笔交易从用户点击“确认支付”到最终资金入账,中间究竟经过了哪些系统、哪些角色、哪些状态变化?这些环节之间的边界在哪里?哪些属于客户端职责,哪些属于服务端职责,哪些又属于第三方支付渠道或银行?如果这些问题没有在工程层面被清晰定义,后续的联调、排障、对账和资金安全都会变得极其被动。

交易流程工程,正是为了解决这一问题而存在。它不是单纯地“写支付代码”,而是把一笔交易视为一个跨越客户端、业务服务、支付网关、渠道和清算系统的完整链路,并对这条链路上的信息流、资金流、状态流进行建模、实现、验证和治理。本节作为导论,先建立三个基础认知:业务边界、核心角色、以及资金流与信息流的分离关系,最后落到工程化视角下的关键质量属性。

一、业务边界:交易系统不等于支付系统

在Android移动交易场景中,最容易混淆的是“交易”与“支付”的边界。交易系统负责的是订单的创建、金额计算、优惠抵扣、库存锁定、交易状态维护;支付系统负责的是收银台展示、支付渠道选择、支付指令下发、支付结果回调。两者通过明确的接口契约协作,但职责不能互相渗透。

例如,一笔电商订单的金额为199.00元,用户使用“满150减20”优惠券,实际应付179.00元。这个计算过程属于交易系统;而用户选择微信支付、调起微信收银台、等待微信返回支付结果,属于支付系统。客户端Android端在这条链路中承担的是“发起交易请求、展示支付结果、处理异常重试”的职责,而不是自行计算最终金额或判断支付是否成功。

常见误区:客户端根据本地缓存的价格计算应付金额并直接发起支付。正确做法是客户端只提交商品ID、数量、优惠券ID等原始参数,由服务端计算并返回最终应付金额,客户端仅做展示和确认。

二、核心角色与职责划分

一笔典型的Android移动交易,至少涉及以下角色:

  • 用户:发起交易的主体,在Android客户端上完成商品选择、订单确认和支付授权。
  • Android客户端:采集用户意图,构造交易请求,展示订单与支付结果,处理网络异常和用户取消。
  • 交易服务:创建订单、计算金额、管理交易状态机、保证幂等。
  • 支付服务:生成支付单、路由支付渠道、处理支付回调、维护支付状态。
  • 第三方支付渠道:如微信支付、支付宝、银联,负责实际资金扣款和结果通知。
  • 银行/清算机构:完成资金从用户账户到商户账户的最终划转与清算。

这些角色之间通过同步接口和异步回调协作。同步接口用于发起请求和获取即时结果,异步回调用于最终确认资金状态。工程上必须明确:同步返回的“支付成功”只是渠道受理成功,不等于资金已到账;真正的到账依据是异步回调或主动查询结果。

三、资金流与信息流的分离

交易流程中最值得在工程上单独建模的,是资金流与信息流的分离。信息流描述的是订单号、支付单号、渠道流水号、状态码、时间戳等数据在系统中的传递;资金流描述的是用户账户余额减少、渠道备付金变化、商户账户增加的 actual 资金移动。两者方向一致,但节奏和确认点不同。

下面这张图展示了一笔Android移动交易中,信息流与资金流在客户端、交易服务、支付服务、支付渠道和银行之间的传递关系。图中实线表示信息流,虚线表示资金流,可以直观看到:客户端只参与信息流的前半段,资金流完全发生在服务端与渠道、银行之间。

Android移动交易:信息流与资金流分离示意 Android客户端 用户意图 / 结果展示 交易服务 订单 / 金额 / 状态机 支付服务 支付单 / 渠道路由 支付渠道 微信 / 支付宝 / 银联 银行 / 清算 资金划转 / 对账 下单请求 创建支付单 支付指令 异步回调 支付结果 订单状态 资金扣款 清算入账 商户账户 资金最终归属 信息流(订单/支付单/状态码) 资金流(扣款/清算/入账) 关键判断:同步返回“成功”≠资金到账 到账依据:异步回调 + 主动查询 + 对账

从图中可以读出三个工程结论。第一,Android客户端只参与信息流的前半段,它不应该、也无法直接感知资金流。第二,支付服务是信息流与资金流的交汇点,它既接收渠道的异步回调,又向交易服务同步支付结果。第三,资金流的最终确认发生在银行清算环节,因此交易系统必须保留“待清算”“已清算”等状态,而不能在支付回调成功时就把订单标记为“已完成结算”。

四、工程化视角下的关键质量属性

把上述链路放到工程视角下,交易流程的质量属性可以归纳为五类,每一类都对应具体的实现手段和验证方式。

质量属性含义典型实现手段验证方式
一致性订单状态、支付状态、资金状态三者最终一致状态机 + 对账补偿 + 定时查询构造回调丢失场景,观察是否自动修复
幂等性同一笔交易重复请求不产生重复扣款唯一业务单号 + 数据库唯一索引 + 去重表并发重复提交同一订单号,检查支付单数量
可追溯任意一笔交易可还原完整链路统一traceId贯穿客户端到渠道按traceId检索日志,检查节点是否完整
安全性金额、签名、回调来源不可篡改服务端计价 + 签名验签 + 回调IP白名单篡改金额和伪造回调,验证是否被拒绝
可恢复异常中断后能继续或回滚超时重试 + 冲正 + 人工介入入口断网、杀进程、渠道超时后检查状态

以幂等性为例,实际工程中常见的做法是:客户端在发起支付前生成一个全局唯一的业务请求号(如requestNo),服务端以该字段建立唯一索引。当同一requestNo再次到达时,服务端直接返回首次处理结果,而不是重新创建支付单。这样即使客户端因网络超时重试,也不会产生两笔扣款。

// 示例:客户端生成幂等请求号(示例参数,非实测)
String requestNo = UUID.randomUUID().toString().replace("-", "");
// 请求体
{
  "requestNo": "a3f8c2e1b94d47f0",
  "orderId": "ORD20250115001",
  "channel": "WECHAT",
  "amount": 17900   // 单位:分,由服务端最终确认
}

再以一致性为例,支付回调可能因为网络抖动、渠道重试策略或服务重启而丢失。工程上不能假设回调一定到达,而应设计“回调 + 主动查询 + 日终对账”三层保障。回调到达时立即更新支付状态;若在约定时间内未收到回调,则由定时任务主动向渠道查询;日终再以渠道对账单为准进行批量核对,发现差异进入人工处理队列。

判断一笔交易是否真正闭环,不要只看客户端是否弹出“支付成功”。应同时检查:支付单状态是否为SUCCESS、交易订单状态是否已推进、渠道流水号是否已回填、对账文件中该笔是否匹配。四者一致,才算工程意义上的完成。

五、本节小结与工程检查清单

交易流程工程的起点,是把一笔交易从“用户点击按钮”还原为一条跨越多个系统的链路,并明确每个角色的边界、信息流与资金流的分离点、以及质量属性的落地方式。以下检查清单可用于新项目启动或既有系统评审:

  • 客户端是否只提交原始参数,最终金额由服务端计算并返回?
  • 订单号、支付单号、渠道流水号是否分别独立且可关联?
  • 是否定义了明确的交易状态机,且状态跃迁有唯一入口?
  • 支付回调是否验签、是否校验来源、是否做幂等?
  • 回调丢失时是否有主动查询和日终对账兜底?
  • 是否用统一traceId串联客户端日志、服务端日志和渠道请求?
  • 异常场景(超时、取消、重复提交、渠道维护)是否有明确处理路径?