小型支付商城下单支付

内容概述

下单功能以及支付功能的完整实现

流程图

流程分析

graph TB
    subgraph "前端层"
        A[用户界面] --> B[HTTP请求]
    end
    
    subgraph "Web层 (s-pay-mall-web)"
        B --> C[AliPayController]
        C --> D[参数验证]
    end
    
    subgraph "业务层 (s-pay-mall-service)"
        D --> E[OrderServiceImpl]
        E --> F[ProductRPC]
        E --> G[支付宝SDK]
    end
    
    subgraph "数据层 (s-pay-mall-dao)"
        E --> H[IOrderDao]
        H --> I[(MySQL数据库)]
    end
    
    subgraph "外部系统"
        G --> J[支付宝网关]
        J --> K[支付回调]
        K --> C
    end
    
    subgraph "异步处理"
        E --> L[EventBus事件总线]
        L --> M[OrderPaySuccessListener]
        N[定时任务] --> E
    end

第一阶段:用户下单请求

输入数据:

1
2
3
4
5
POST /api/v1/alipay/create_pay_order
{
"userId": "10001",
"productId": "100001"
}

处理流程

  1. 前端发送 HTTP POST 请求到AliPayController.createPayOrder()
  2. Controller 接收 ShopCartReq 对象,包含用户ID和商品ID

数据流向:前端请求 -> AliPayController -> 参数验证

第二阶段:重复订单检查

输入数据:ShopCartReq对象:

1
2
3
4
public class ShopCartReq {
private String userId;
private String productId;
}

处理流程

  1. OrderServiceImpl.createOrder() 被调用
  2. 构造查询条件:创建 PayOrder 对象设置 userIdproductId
  3. 调用 IOrderDao.queryUnPayOrder() 查询数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PayOrder {
private Long id;
private String userId;
private String productId;
private String productName;
private String orderId;
private Date orderTime;
private BigDecimal totalAmount;
private String status;
private String payUrl;
private Date payTime;
private Date createTime;
private Date updateTime;
}

可能的情况:

  1. 返回状态为 PAY_WAIT 的未支付订单
    • 直接返回现有的 PayOrderRes{orderId, payUrl}
    • 流程结束,跳转到返回响应阶段
  2. 返回状态为 CREATE 的订单
    • 需要重新创建支付单
    • 调用 doPrepayOrder() 方法创建支付单
    • 跳转到支付单创建阶段
  3. 返回 null(无相关订单)
    • 继续执行后续流程

数据流向:OrderServiceImpl -> IOrderDao -> MySQL数据库 -> 返回查询结果

第三阶段:商品信息查询

输入数据:productId(String 类型)

处理流程

  1. 调用 ProductRPC.queryProductByProductId()
    • 这是一个模拟的RPC调用,实际返回固定的商品信息

输出数据:

1
2
3
4
5
6
7
8
9
10
public class ProductVO {
/** 商品ID */
private String productId;
/** 商品名称 */
private String productName;
/** 商品描述 */
private String productDesc;
/** 商品价格 */
private BigDecimal price;
}

数据流向:OrderServiceImpl -> ProductRPC -> 返回ProductVO对象

第四阶段:订单数据持久化

输入数据:

  • userId:用户ID
  • productId:商品ID
  • productName:从ProductVO获取的商品名称
  • price:从ProductVO获取的商品价格

处理流程

  1. 生成16位随机订单号:RandomStringUtils.randomNumeric(16)
  2. 构造 PayOrder 对象:
1
2
3
4
5
6
7
8
9
PayOrder.builder()
.userId("10001")
.productId("100001")
.productName("new product")
.orderId("1234567890123456") // 16位随机数
.totalAmount(new BigDecimal("1.68"))
.orderTime(new Date())
.status("CREATE") // 使用Constants.OrderStatusEnum.CREATE.getCode()
.build()
  1. 调用 IOrderDao.insert() 插入数据库
  2. 执行SQL:INSERT INTO pay_order(...) VALUES(...)

输出数据

  • 数据库中新增一条订单记录,状态为 CREATE

数据流向:OrderServiceImpl -> 构造PayOrder对象 -> IOrderDao -> MySQL数据库

第五阶段:支付单创建

输入数据:

  • productId:商品ID
  • productName:商品名称
  • orderId:订单号
  • totalAmount:订单金额

处理流程

  1. 调用 doPrepayOrder() 方法
  2. 创建支付宝请求对象:
1
2
3
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(notifyUrl); // 异步回调地址
request.setReturnUrl(returnUrl); // 同步跳转地址
  1. 构造业务参数:
1
2
3
4
5
6
{
"out_trade_no": "1234567890123456", // 商户订单号
"total_amount": "1.68", // 订单金额
"subject": "new product", // 订单标题
"product_code": "FAST_INSTANT_TRADE_PAY"
}
  1. 调用支付宝SDK:alipayClient.pageExecute(request).getBody()

输出数据

  • 支付宝返回HTML表单字符串,包含支付页面的完整HTML代码

数据流向:OrderServiceImpl -> 构造支付宝请求 -> 支付宝SDK -> 支付宝服务器 -> 返回支付表单

第六阶段:更新订单支付信息

输入数据:

  • orderId:订单号
  • payUrl:支付宝返回的HTML表单
  • status:新状态 PAY_WAIT

处理流程

  1. 构造更新对象:
1
2
3
4
PayOrder payOrder = new PayOrder();
payOrder.setOrderId("1234567890123456");
payOrder.setPayUrl("<html>支付表单内容</html>");
payOrder.setStatus("PAY_WAIT");
  1. 调用 IOrderDao.updateOrderPayInfo()
  2. 执行SQL:UPDATE pay_order SET pay_url = ?, status = ?, update_time = now() WHERE order_id = ?

输出数据

  • 数据库订单记录更新完成,状态变为 PAY_WAIT,包含支付链接

数据流向:OrderServiceImpl -> 构造更新对象 -> IOrderDao -> MySQL数据库

第七阶段:返回支付响应

输入数据:

  • orderId:订单号
  • payUrl:支付链接

处理流程

  1. 构造响应对象:
1
2
3
4
PayOrderRes.builder()
.orderId("1234567890123456")
.payUrl("<html>支付表单</html>")
.build()
  1. 在Controller层包装成统一响应格式:
1
2
3
4
5
Response.<String>builder()
.code("0000") // Constants.ResponseCode.SUCCESS.getCode()
.info("调用成功") // Constants.ResponseCode.SUCCESS.getInfo()
.data(payOrderRes.getPayUrl())
.build()
  1. 记录成功日志:”商品下单,根据商品ID创建支付单完成”

输出数据(返回给前端)

1
2
3
4
5
{
"code": "0000",
"info": "调用成功",
"data": "<html>支付宝支付表单HTML代码</html>"
}

数据流向:OrderServiceImpl -> PayOrderRes -> AliPayController -> Response对象 -> 前端页面

第八阶段:用户支付操作

处理流程

  1. 前端接收到支付表单HTML
  2. 用户浏览器渲染支付页面
  3. 用户在支付宝页面完成付款操作
  4. 支付宝处理支付请求并扣款

数据流向:前端页面 -> 用户操作 -> 支付宝支付页面 -> 支付宝服务器处理

第九阶段:支付回调处理

输入数据(支付宝异步回调):

1
POST /api/v1/alipay/pay_notify

参数包括:

  • trade_status: “TRADE_SUCCESS”
  • out_trade_no: “1234567890123456” (商户订单号)
  • trade_no: “2021081622001234567890123456” (支付宝交易号)
  • total_amount: “1.68”
  • sign: “支付宝RSA256签名”
  • 其他支付宝回调参数…

处理流程

  1. AliPayController.payNotify() 接收回调
  2. 交易状态验证:检查 trade_status 是否为 “TRADE_SUCCESS”
  3. 签名验证:
1
2
3
String sign = params.get("sign");
String content = AlipaySignature.getSignCheckContentV1(params);
boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, alipayPublicKey, "UTF-8");
  1. 记录回调日志:记录交易名称、状态、支付宝交易号、金额等信息

验证失败的输出

  • 返回字符串 “false” 给支付宝(拒绝回调)

验证成功继续处理…

数据流向:支付宝服务器 -> AliPayController.payNotify() -> 参数解析和验证

第十阶段:订单状态更新

输入数据:

  • orderId:从回调参数 out_trade_no 获取

处理流程

  1. 调用 OrderServiceImpl.changeOrderPaySuccess()
  2. 构造更新对象:
1
2
3
PayOrder payOrderReq = new PayOrder();
payOrderReq.setOrderId("1234567890123456");
payOrderReq.setStatus("PAY_SUCCESS"); // Constants.OrderStatusEnum.PAY_SUCCESS.getCode()
  1. 调用 IOrderDao.changeOrderPaySuccess()
  2. 执行SQL:UPDATE pay_order SET status = ?, pay_time = now(), update_time = now() WHERE order_id = ?

输出数据

  • 数据库订单状态更新为 PAY_SUCCESS
  • 设置支付时间为当前时间

数据流向:OrderServiceImpl -> 构造更新对象 -> IOrderDao -> MySQL数据库

第十一阶段:异步事件发布

输入数据:

  • 更新后的 PayOrder 对象

处理流程

  1. 将订单对象序列化为JSON:JSON.toJSONString(payOrderReq)
  2. 通过EventBus发布事件:eventBus.post(jsonString)
  3. OrderPaySuccessListener.handleEvent() 监听并处理事件

输出数据

1
2
3
4
5
6
{
"orderId": "1234567890123456",
"status": "PAY_SUCCESS",
"payTime": "2025-09-16 10:30:00",
"updateTime": "2025-09-16 10:30:00"
}

后续处理

  • 商品发货逻辑
  • 积分奖励计算
  • 用户通知推送
  • 返利处理等

数据流向:OrderServiceImpl -> EventBus -> OrderPaySuccessListener -> 后续业务处理

第十二阶段:回调响应返回

输出数据

  • 向支付宝返回字符串 “success”(表示回调处理成功)
  • 记录日志:”支付回调,支付回调,更新订单 {}”

数据流向:AliPayController -> 支付宝服务器(确认回调处理完成)

异步处理机制

定时任务处理

超时关单

  • TimeoutCloseOrderJob 每10分钟执行一次
  • 查询超过30分钟未支付的订单:queryTimeoutCloseOrderList()
  • 将订单状态更新为 CLOSE

支付状态主动查询

  • NoPayNotifyOrderJob 每3秒执行一次
  • 查询超过1分钟未支付且未收到回调的订单:queryNoPayNotifyOrder()
  • 主动调用支付宝接口查询订单实际支付状态
  • 若支付成功则更新订单状态为 PAY_SUCCESS

事件监听器

支付成功事件监听

  • OrderPaySuccessListener 基于Guava EventBus实现
  • 通过@Subscribe注解订阅支付成功事件
  • 接收JSON格式的订单信息
  • 负责处理支付成功后的异步业务逻辑(如商品发货、积分奖励等)