浏览代码

fix(virtual-pay): query_order返回268490002数据不存在视为未支付关单

微信query_order返回errcode=268490002(请求参数字段错误)且errmsg=数据不存在,
表示该order_id在微信订单库中从未创建成功(并非临时查询失败)。
原逻辑将所有errcode!=0一律return null, 导致超时任务永远跳过不关单,
死单永不清理且每单每分钟刷2条WARN(今日已5200+条)。

- queryOrder: 单独识别268490002返回status=-1(本地扩展状态) + INFO日志
- VirtualPayTimeoutTask三个handler: status=-1归入明确未支付置cancelled分支
- 已用真实paid单(ORD1785990942621001DCC2)实测接口正常, 无误杀风险
Xiaogang Liao 6 天之前
父节点
当前提交
9be1cd6183

+ 12 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/VirtualPayService.java

@@ -35,6 +35,11 @@ public class VirtualPayService {
     /** 虚拟支付下单场景标识(wx.requestVirtualPayment 固定 URI 前缀) */
     private static final String URI_REQUEST_VIRTUAL_PAYMENT = "requestVirtualPayment";
 
+    /** 微信 query_order 错误码:数据不存在(微信侧无该订单号,确定性未支付结论,可安全关单) */
+    private static final int ERRCODE_ORDER_NOT_FOUND = 268490002;
+    /** query_order 自定义结果状态:微信侧订单不存在(官方 status 仅 0-10,-1 为本地扩展) */
+    public static final int STATUS_ORDER_NOT_FOUND = -1;
+
     /** 签名算法标识 */
     private static final String SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256";
 
@@ -525,6 +530,12 @@ public class VirtualPayService {
             JSONObject resp = JSON.parseObject(respBody);
             int errcode = resp.getIntValue("errcode");
             if (errcode != 0) {
+                // 268490002=数据不存在:微信订单库无该 order_id(下单从未在微信侧创建成功)。
+                // 这是确定性的"未支付"结论(区别于网络/签名等临时失败),返回 status=-1 供超时任务安全关单
+                if (errcode == ERRCODE_ORDER_NOT_FOUND) {
+                    log.info("query_order微信侧无此订单, 视为未支付可关单: outTradeNo={}", outTradeNo);
+                    return new QueryOrderResult(false, STATUS_ORDER_NOT_FOUND, null);
+                }
                 log.warn("query_order返回错误: outTradeNo={}, errcode={}, errmsg={}",
                         outTradeNo, errcode, resp.getString("errmsg"));
                 return null;   // 查询失败:调用方不得关单
@@ -558,7 +569,7 @@ public class VirtualPayService {
     public static class QueryOrderResult {
         /** 是否已支付(status∈{2,3,4}) */
         public final boolean paid;
-        /** 微信侧订单状态枚举 */
+        /** 微信侧订单状态枚举(-1=微信无此订单,本地扩展,仅查询接口返回) */
         public final int status;
         /** 微信交易单号(order.wxpay_order_id) */
         public final String wxpayOrderId;

+ 6 - 3
cfc-backend/src/main/java/com/etotem/cfc/task/VirtualPayTimeoutTask.java

@@ -146,7 +146,8 @@ public class VirtualPayTimeoutTask {
             log.info("虚拟支付超时兜底: 测评订单已支付, 触发解锁: orderNo={}, ok={}", order.getOrderNo(), ok);
             return true;
         }
-        if (result.status == 0 || result.status == 1 || result.status == 6) {
+        if (result.status == VirtualPayService.STATUS_ORDER_NOT_FOUND
+                || result.status == 0 || result.status == 1 || result.status == 6) {
             // 明确未支付/已关闭 → 置 cancelled
             order.setStatus("cancelled");
             order.setUpdatedAt(new Date());
@@ -186,7 +187,8 @@ public class VirtualPayTimeoutTask {
             log.info("虚拟支付超时兜底: 会员订单已支付, 触发开通: orderNo={}, ok={}", order.getOrderNo(), ok);
             return true;
         }
-        if (result.status == 0 || result.status == 1 || result.status == 6) {
+        if (result.status == VirtualPayService.STATUS_ORDER_NOT_FOUND
+                || result.status == 0 || result.status == 1 || result.status == 6) {
             order.setStatus("cancelled");
             order.setUpdatedAt(new Date());
             paymentOrderMapper.updateById(order);
@@ -219,7 +221,8 @@ public class VirtualPayTimeoutTask {
                     order.getOrderNo(), activated != null);
             return true;
         }
-        if (result.status == 0 || result.status == 1 || result.status == 6) {
+        if (result.status == VirtualPayService.STATUS_ORDER_NOT_FOUND
+                || result.status == 0 || result.status == 1 || result.status == 6) {
             order.setStatus("cancelled");
             memberSubscriptionOrderMapper.updateById(order);
             log.info("虚拟支付超时兜底: 订阅订单未支付, 置cancelled: orderNo={}, status={}",