闽公网安备 35020302035485号


支付成功/失败/取消/关闭:电商系统最终确定了用户在第三方钱包的支付最终结果

客户端通常是轮询获取状态,可能会在轮询时间内没有获取到订单状态,结果用户看到未支付


毫无疑问,最简单的肯定就是定时任务了,支付服务,定时查询一段时间内支付中的支付订单,向第三方渠道查询支付结果,查询到终态之后,就去更新支付订单状态、通知订单服务:
@XxlJob("syncPaymentResult")
public ReturnT<String> syncPaymentResult(int hour) {
//堆代码 duidaima.com
//查询一段之间支付中的流水
List<PayDO> pendingList = payMapper.getPending(now.minusHours(hour));
for (PayDO payDO : pendingList) {
//……
// 主动去第三方查
PaymentStatusResult paymentStatusResult = paymentService.getPaymentStatus(paymentId);
// 第三方支付中
if (PaymentStatusEnum.PENDING.equals(paymentStatusResult.getPayStatus())) {
continue;
}
//支付完成,获取到终态
//……
// 1.更新流水
payMapper.updatePayDO(payDO);
// 2.通知订单服务
orderService.notifyOrder(notifyLocalRequestVO);
}
return ReturnT.SUCCESS;
}
定时任务的最大好处肯定是简单了,但是它也有一些问题:
//……
//控制查询频率的队列,时间单位为s
Deque<Integer> queue = new LinkedList<>();
queue.offer(10);
queue.offer(30);
queue.offer(60);
//……
//支付订单号
PaymentConsultDTO paymentConsultDTO = new PaymentConsultDTO();
paymentConsultDTO.setPaymentId(paymentId);
paymentConsultDTO.setIntervalQueue(queue);
//发送延时消息
Message message = new Message();
message.setTopic("PAYMENT");
message.setKey(paymentId);
message.setTag("CONSULT");
message.setBody(toJSONString(paymentConsultDTO).getBytes(StandardCharsets.UTF_8));
try {
//第一个延时消息,延时10s
long delayTime = System.currentTimeMillis() + 10 * 1000;
// 设置消息需要被投递的时间。
message.setStartDeliverTime(delayTime);
SendResult sendResult = producer.send(message);
//……
} catch (Throwable th) {
log.error("[sendMessage] error:", th);
}
PS:这里用的是RocketMQ云服务器版,支持任意级别的延时消息,开源版的RocketMQ只支持固定级别的延时消息,不得不感慨充钱才能变强。有实力的开发团队,可以在开源基础上,进行二次开发。@Component
@Slf4j
public class ConsultListener implements MessageListener {
//消费者注册,监听器注册
//……
@Override
public Action consume(Message message, ConsumeContext context) {
// UTF-8解析
String body = new String(message.getBody(), StandardCharsets.UTF_8);
PaymentConsultDTO paymentConsultDTO= JsonUtil.parseObject(body, new TypeReference<PaymentConsultDTO>() {
});
if (paymentConsultDTO == null) {
return Action.ReconsumeLater;
}
//获取支付流水
PayDO payDO=payMapper.selectById(paymentConsultDTO.getPaymentId());
//……
//查询支付状态
PaymentStatusResult paymentStatusResult=payService.getPaymentStatus(paymentStatusContext);
//还在支付中,继续投递一个延时消息
if (PaymentStatusEnum.PENDING.equals(paymentStatusResult.getPayStatus())){
//发送延时消息
Message msg = new Message();
message.setTopic("PAYMENT");
message.setKey(paymentConsultDTO.getPaymentId());
message.setTag("CONSULT");
//下一个延时消息的频率
Long delaySeconds=paymentConsultDTO.getIntervalQueue().poll(); message.setBody(toJSONString(paymentConsultDTO).getBytes(StandardCharsets.UTF_8));
try {
Long delayTime = System.currentTimeMillis() + delaySeconds * 1000;
// 设置消息需要被投递的时间。
message.setStartDeliverTime(delayTime);
SendResult sendResult = producer.send(message);
//……
} catch (Throwable th) {
log.error("[sendMessage] error:", th);
}
return Action.CommitMessage;
}
//获取到最终态
//更新支付订单状态
//……
//通知订单服务
//……
return Action.CommitMessage;
}
}
延时消息的方案相对于定时轮询方案来讲:不过大家也看到,我这里的实现是利用的是充钱版的RocketMQ,所以看起来不太复杂,但是如果用开源方案,那就没那么简单。