支付模块交易链路梳理与时序图
本文基于某支付模块的代码实现(项目名已替换为 XXX
),详细梳理交易链路,分析核心逻辑,并通过时序图直观展示支付流程。以下内容将帮助读者理解从用户发起支付到支付成功的完整过程,并适用于面试或技术分享场景。
一、核心组件概览
1. 核心实体 (PayInfo
)
- 作用 :记录支付信息,对应数据库表
pay_info
。 - 关键字段 :
payId
:支付流水号(分布式 ID)。userId
:用户 ID。orderIds
:关联订单 ID 列表(逗号分隔)。bizPayNo
:第三方支付交易号。payStatus
:支付状态(0-未支付,1-已支付)。payAmount
:支付金额(单位:分)。version
:乐观锁版本号。
2. 核心控制器
PayController
:处理前端支付请求和状态查询。PayNoticeController
:处理第三方支付回调。
3. 核心服务 (PayInfoServiceImpl
)
- 功能:支付记录创建、状态更新、订单通知。
二、交易链路梳理
以下是基于代码的支付交易完整流程:
1. 用户发起支付请求
- 触发点 :用户在前端点击"支付",调用
POST /pay/order
。 - 输入 :
PayInfoDTO
(包含orderIds
和returnUrl
)。 - 后端逻辑 (
PayController.pay()
):- 获取用户 ID(通过
AuthUserContext
)。 - 调用
PayInfoService.pay()
生成支付记录并返回PayInfoBO
。 - 设置回调地址(
apiNoticeUrl
)和前端跳转地址(returnUrl
)。 - 模拟回调(调用
PayNoticeController.submit()
,仅用于测试)。 - 返回
payId
给前端。
- 获取用户 ID(通过
- 输出 :
ServerResponseEntity
封装的payId
。
注意 :代码中直接调用 payNoticeController.submit()
是测试逻辑,实际生产环境中应由第三方支付平台发起回调。
2. 支付记录创建 (PayInfoService.pay()
)
- 输入 :
userId
和PayInfoDTO
。 - 逻辑 :
- 生成支付 ID :通过
SegmentFeignClient
获取分布式 ID(payId
)。 - 验证订单 :调用
OrderFeignClient.getOrdersAmountAndIfNoCancel()
,获取订单金额并检查订单是否有效。 - 构建支付记录 :
- 设置
payId
、userId
、payAmount
、orderIds
等。 - 初始状态为
PayStatus.UNPAY
(未支付)。
- 设置
- 保存记录 :插入
PayInfo
到数据库。 - 返回支付参数 :构造
PayInfoBO
(包含payId
、payAmount
、body
)。
- 生成支付 ID :通过
- 输出 :
PayInfoBO
。
3. 前端唤起支付
- 前端逻辑 (未在代码中体现,推测实现):
- 接收后端返回的
payId
。 - 使用
payId
和其他参数(如金额)调用第三方支付 SDK(如微信wx.requestPayment()
或支付宝表单提交)。
- 接收后端返回的
- 用户操作:输入密码完成支付。
4. 第三方支付平台回调
- 触发点 :用户支付成功后,第三方支付平台(如微信、支付宝)向后端回调地址(
/notice/pay/order
)发送通知。 - 输入 :
payId
(实际应包含第三方交易号和签名,代码中简化)。 - 后端逻辑 (
PayNoticeController.submit()
):- 根据
payId
查询PayInfo
。 - 解析
orderIds
为List<Long>
。 - 构造
PayInfoResultBO
(包含payId
、bizPayNo
等)。 - 调用
PayInfoService.paySuccess()
处理支付成功逻辑。 - 返回空字符串(实际应返回第三方要求的响应,如
SUCCESS
)。
- 根据
- 输出 :
ResponseEntity<String>
。
5. 支付成功处理 (PayInfoService.paySuccess()
)
- 输入 :
PayInfoResultBO
和orderIds
。 - 逻辑 :
- 更新支付状态 :
- 设置
payStatus
为PayStatus.PAYED
。 - 更新
bizPayNo
、callbackContent
、callbackTime
。 - 执行数据库更新。
- 设置
- 通知订单服务 :
- 通过
RocketMQTemplate
发送消息到XXX_ORDER_NOTIFY_TOPIC
,携带PayNotifyBO
(包含orderIds
)。 - 若发送失败,抛出异常(依赖 RocketMQ 重试)。
- 通过
- 更新支付状态 :
- 特性:事务性操作,确保状态更新和消息发送一致。
6. 前端查询支付状态(可选)
- 触发点 :前端调用
GET /pay/isPay/{orderIds}
检查支付结果。 - 逻辑 (
PayController.isPay()
):- 调用
PayInfoService.isPay()
查询状态。 - 返回布尔值(
true
表示已支付)。
- 调用
- 输出 :
ResponseEntity<Boolean>
。
三、时序图
以下是支付交易的时序图,使用 Mermaid 语法生成:
sequenceDiagram
participant U as 用户
participant F as 前端
participant P as PayController
participant S as PayInfoService
participant O as OrderFeignClient
participant L as SegmentFeignClient
participant T as 第三方支付平台
participant N as PayNoticeController
participant R as RocketMQ
U->>F: 点击支付
F->>P: POST /pay/order (orderIds)
P->>S: pay(userId, payParam)
S->>L: getSegmentId()
L-->>S: payId
S->>O: getOrdersAmountAndIfNoCancel(orderIds)
O-->>S: payAmount
S->>S: 保存 PayInfo
S-->>P: PayInfoBO (payId)
P-->>F: payId
F->>T: 唤起支付 (payId, payAmount)
U->>T: 输入密码支付
T-->>N: POST /notice/pay/order (payId)
N->>S: getByPayId(payId)
S-->>N: PayInfo
N->>S: paySuccess(payInfoResult, orderIds)
S->>S: 更新 PayInfo (payStatus=1)
S->>R: 发送消息 (ORDER_NOTIFY_TOPIC)
R-->>S: 发送成功
N-->>T: SUCCESS
F->>P: GET /pay/isPay/{orderIds}
P->>S: isPay(orderIds, userId)
S-->>P: isPay (1/0)
P-->>F: true/false
说明:
- 图中展示了从用户支付到状态更新的完整链路。
PayNoticeController.submit()
被视为第三方回调的入口。- RocketMQ 确保支付成功后订单状态同步。
四、链路特点
- 分布式 ID :通过
SegmentFeignClient
生成payId
,确保唯一性。 - 状态机 :
payStatus
驱动支付流程(未支付 -> 已支付)。 - 最终一致性:RocketMQ 保证支付状态与订单状态同步。
- 安全性:事务控制和消息发送的幂等性设计。
- 可追溯性 :
payId
贯穿整个链路。
五、注意事项与优化建议
- 回调安全性 :
- 当前代码未验证第三方签名,生产环境需添加签名校验(如微信的 MD5 或支付宝的 RSA2)。
- 测试逻辑 :
PayController
中直接调用submit()
是测试代码,实际应移除,由第三方触发。
- 异常处理 :
- RocketMQ 发送失败时抛异常合理,但可增加重试机制或异步补偿。
- 前端交互 :
- 返回的
payId
需配合第三方支付参数(如prepay_id
),否则前端无法唤起支付。
- 返回的
六、总结
该支付模块设计清晰,链路完整,从前端发起支付到第三方回调更新状态,再到通知下游服务,体现了微服务架构的典型特点。通过时序图可以直观理解各组件的交互逻辑,适合用于技术分享或面试准备。优化方向包括增强安全性、完善异常处理和规范化第三方支付集成。