支付接口的开发涉及多个步骤和业务系统之间的对接,通常包括和第三方支付平台(例如支付宝、微信支付等)的下单请求、支付状态查询、支付通知的处理等。
1. 支付接口的整体架构
- 支付服务:负责与第三方支付平台的对接,封装第三方支付接口。
- 业务系统:例如订单系统,负责处理与支付相关的业务逻辑,例如生成订单、更新支付状态等。
- MQ(消息队列):用于异步接收支付结果通知,确保系统在高并发和分布式环境下能可靠处理支付结果。
2. 支付流程的实现
支付流程通常分为以下几个步骤:
2.1 支付下单
当用户发起支付时,业务系统首先调用支付服务发起支付下单请求,支付服务再调用第三方支付平台的下单接口。
-
前端请求:用户通过点击支付按钮,发起支付请求(例如扫码支付、小程序支付等)。
-
业务系统请求支付服务: 业务系统将订单信息传递给支付服务,请求支付下单。支付服务会根据支付渠道(微信、支付宝等)封装请求参数并发送到第三方支付平台。
public PaymentResponse initiatePayment(Order order) {
// 1. 验证订单信息
if (order == null || order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new IllegalArgumentException("订单无效或订单状态不支持支付");
}
// 2. 构建支付请求
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.setOrderId(order.getId());
paymentRequest.setAmount(order.getTotalAmount());
paymentRequest.setPaymentChannel(PaymentChannel.WECHAT);
// 3. 调用支付服务的接口请求支付
PaymentResponse response = paymentService.createPayment(paymentRequest);
// 4. 返回支付结果
return response;
}
2.2 支付平台返回结果
支付下单成功后,第三方支付平台会返回支付相关的结果。例如:
- 扫码支付:返回二维码 URL,前端通过该 URL 生成二维码供用户扫码。
- 小程序支付:返回会话标识,前端使用该标识调起微信支付窗口。
2.3 系统获取支付结果
当用户完成支付后,系统需要获取支付结果。获取支付结果通常有两种方式:
-
主动查询支付结果:业务系统可以主动调用支付服务查询接口,获取支付结果。
-
被动接收支付通知:支付服务通过消息队列(MQ)异步接收第三方支付平台的支付结果通知,并将结果分发给业务系统。
public PaymentResult queryPaymentResult(String orderId) {
// 1. 调用支付服务的接口,查询支付结果
PaymentResult result = paymentService.queryPayment(orderId);
// 2. 根据支付结果更新订单状态
if (result.isSuccessful()) {
updateOrderStatus(orderId, OrderStatus.PAID);
} else {
updateOrderStatus(orderId, OrderStatus.FAILED);
}
return result;
}
3. 支付通知的处理流程
当用户完成支付后,第三方支付平台通常会通过支付通知将支付结果发送给支付服务。支付服务再将支付结果发送到消息队列(MQ),订单服务监听 MQ,接收到支付通知后更新订单状态。
具体的流程是:
支付服务将支付结果发送给专门传输支付通知的交换机,交换机使用的是topic类型,该交换机绑定了多个队列,因为考虑会有多个微服务对接支付通知,每个微服务监听一个队列。
当支付服务向交换机发送一条支付通知消息,所有绑定此交换机的队列会收到支付通知,业务系统收到支付结果后解析出业务系统应用标识,判断是否属于自己的支付结果通知,如果是再进行处理。
3.1 MQ 消息队列的使用
在微服务架构下,通过 MQ 实现异步通信可以降低系统耦合度,提升系统的可扩展性和可靠性。
- 交换机和队列的绑定: 支付服务将支付结果发送到绑定多个队列的
topic
类型的交换机。多个微服务会监听各自的队列,每个队列都会接收到支付通知。
// 支付服务发送支付通知
public void sendPaymentNotification(PaymentResult result) {
// 1. 构建支付通知消息
PaymentNotification notification = new PaymentNotification();
notification.setOrderId(result.getOrderId());
notification.setStatus(result.getStatus());
// 2. 发送到支付通知的 MQ 交换机
mqService.sendMessage("payment.notification.exchange", notification);
}
// 订单服务监听支付通知
@RabbitListener(queues = "order.payment.queue")
public void handlePaymentNotification(PaymentNotification notification) {
// 1. 根据通知信息更新订单支付状态
Order order = orderService.getOrder(notification.getOrderId());
if (order != null) {
order.setStatus(notification.getStatus() == PaymentStatus.SUCCESS ? OrderStatus.PAID : OrderStatus.FAILED);
orderService.updateOrder(order);
}
}
3.2 消息队列中的交换机和队列设计
-
交换机:支付服务会将支付结果发送到一个
topic
类型的交换机。例如payment.notification.exchange
。 -
队列:订单服务、库存服务等不同的业务微服务会绑定到该交换机的不同队列。例如订单服务监听
order.payment.queue
,库存服务监听inventory.payment.queue
,这样每个服务都可以接收到支付通知。
3.3 支付服务将结果通知给业务系统
当支付服务通过 MQ 发送支付结果后,系统会根据不同的微服务应用标识,判断是否需要处理该支付通知。例如,订单系统只处理与自己相关的支付通知。
if (notification.getAppId().equals("order.service")) {
// 处理订单服务的支付结果
orderService.updateOrderStatus(notification);
} else {
// 忽略其他应用的支付结果
log.info("忽略非订单服务的支付结果通知");
}
4. 实现思路总结
4.1 业务系统调用支付服务发起支付
用户发起支付时,业务系统调用支付服务接口,支付服务根据支付渠道和订单信息调用第三方支付平台的下单接口。
4.2 返回二维码或会话标识
支付服务将下单结果返回给前端,前端根据返回的结果生成支付二维码或调起支付窗口。
4.3 查询支付结果
- 可以主动查询支付结果,调用支付服务的查询接口;
- 也可以通过 MQ 异步接收支付结果通知,更新订单状态。
4.4 MQ 消息队列处理支付通知
支付服务将支付结果发送到消息队列中,业务系统通过监听 MQ 队列接收到支付通知,解析结果并处理订单状态更新。