在线支付系列【16】微信支付实战篇之集成关闭订单API

文章介绍了如何集成微信关闭订单API,包括根据商户订单号调用接口以及处理不同状态码的情况。同时,讨论了用户关闭订单和支付超时后的处理策略,如使用定时任务监控未支付订单并执行关闭操作。此外,提到了延时队列作为优化方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有道无术,术尚可求,有术无道,止于术。

关闭订单

以下情况需要调用关单接口

  1. 商户订单支付失败需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
  2. 系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。

注意:关单没有时间限制,建议在订单生成后间隔几分钟(最短5分钟)再调用关单接口,避免出现订单状态同步不及时导致关单失败。

1. 集成微信关闭订单API

官方关闭订单API文档
在这里插入图片描述
枚举类WechatPayNativeApiEnum添加关闭订单API地址:

CLOSE_MERCHANT_ORDER_NO("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", "商户订单号关闭订单"),

声明服务接口根据商户订单号关闭订单方法。

    /**
     * 根据商户订单号查询订单 关闭订单
     *
     * @param outTradeNo 商户订单号
     * @param merchantId 商户ID
     */
    void closePayOrder(String outTradeNo, String merchantId) throws Exception;

集成微信关闭订单API,实现上述方法。

    @Override
    public void closePayOrder(String outTradeNo, String merchantId) throws Exception {
        log.info("关闭订单开始,订单号:{}", outTradeNo);
        // 1. 创建POST请求
        String url = String.format(WechatPayNativeApiEnum.CLOSE_MERCHANT_ORDER_NO.getAddress(), outTradeNo);
        HttpPost httpPost = new HttpPost(url);
        // 2. 使用JSON库,构建请求参数对象
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid", merchantId);// 直连商户号
        objectMapper.writeValue(bos, rootNode);
        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        // 3. 执行请求,是没有数据返回的,只有(Http状态码为204)
        CloseableHttpResponse response = httpClient.execute(httpPost);
        // 4. 响应解析
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200 || statusCode == 204) {
            log.info("成功关闭订单");
        } else {
            // 403	OUT_TRADE_NO_USED	商户订单号重复	请核实商户订单号是否重复提交
            // 404	ORDERNOTEXIST	订单不存在	请检查订单是否发起过交易
            // 400	ORDER_CLOSED	订单已关闭	当前订单已关闭,请重新下单
            throw new RuntimeException("关闭订单失败,状态码:%s ,需要自己根据状态码获取到错误信息。。。。" + statusCode);
        }
    }

2. 用户关闭订单

用户操作时,有以下情况,需要调用关闭订单接口:

  • 不想要该商品了,用户点击取消订单按钮
  • 后台问题,导致支付失败,再次支付时,需要生成新单号重新发起支付,要对原订单号调用关单,避免重复支付

订单服务类添加关闭订单功能。

    public void closeOrder(String orderId) throws Exception {
        // 1. 查询订单
        OrderEntity order = orderService.getById(orderId);
        if (ObjectUtil.isNull(order)){
            throw new IllegalStateException("未查询到当前订单");
        }
        // 2. 关闭订单
        closePayOrder(order.getOutTradeNo(), wechatPayProperties.getMerchantId());
    }

添加访问接口。

    @Operation(summary = "用户关闭订单")
    @PostMapping("/closeOrder")
    public R<?> closeOrder(String orderId) throws Exception {
        wechatPayService.closeOrder(orderId);
        return R.success();
    }

订单失效或支付失败,重新支付逻辑也不复杂,这里就不具体实现了。

    @Operation(summary = "订单失效或支付失败,重新支付")
    @PostMapping("/repayOrder")
    public R<?> repayOrder(String orderId) throws Exception {
        // 1. 查询订单
        // 2. 检查订单状态支付失败 或超时
        // 3. 重新调用微信下单
        // 4. 将原支付交易订单关闭
        // 5. 支付成功,修改订单信息
        wechatPayService.repayOrder(orderId);
        return R.success();
    }

3. 支付超时关闭订单

下单时,微信返回的二维码链接都是有有效期的,一般订单都会设置有效期,比如30分钟还未支付,订单状态变为支付超时

一般有一下几种实现方案:

1、定时任务

每隔30秒启动一次,找出最近30分钟内创建并且未支付的订单,调用微信查单接口核实订单状态。未支付成功调用关单接口关闭订单。

开发起来比较简单,缺点

  • 对数据库的压力很大,定时任务造成人为的波峰,执行的时刻数据库的压力会陡增
  • 计时不准,定时任务做不到非常精确的时间控制,比如半小时订单过期,但是定时任务很难卡准这个点

2、被动取消

  1. 用户留在收银台的时候,客户端倒计时+主动查询订单状态,服务端每次都去检查一下订单是否超时、剩余时间
  2. 用户每次进入订单相关的页面,查询订单的时候,服务端也检查一下订单是否超时

这种方式实现起来也比较简单,缺点

  • 依赖客户端,如果客户端不发起请求,订单可能永远没法过期,一直占用库存
  • 可以被动取消+定时任务,通过定时任务去做兜底的操作。

3、延时队列

可以使用RocketMQRabbitMQKafka的延时消息,下单消息发送后,发送延迟消息,延迟一到,消费者接收到订单消息,去微信查询订单支付情况,查询若还是未返回支付成功状态,调用微信关闭订单。

这里使用简单的定时任务演示:

@Component
public class CloseOrderTask {

    @Resource
    WechatPayService wechatPayService;

    @Resource
    OrderService orderService;

    // 半小时:0 0/30 * * * ?
    @Scheduled(cron = "*/30 * * * * ?")   // 30秒钟执行一次
    public void test() throws Exception {
        // 1. 查询未支付订单
        List<OrderEntity> orderEntities = orderService.selectListNoPayOrder();
        for (OrderEntity order : orderEntities) {
            // 2. 微信API查询该订单
            Boolean orderStatus = wechatPayService.selectPayOrderStatus(order.getGoodId());
            if (orderStatus) {
                // 已支付,更新订单状态
                order.setStatus(OrderStatusEnum.SUCCESS.getCode());
                orderService.updateById(order);
            } else {
                // 未支付,关闭订单
                order.setStatus(OrderStatusEnum.CLOSED.getCode());
                wechatPayService.closeOrder(order.getGoodId());
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨 禹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值