首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

微信支付 Java 版訂單查詢接口實現與異常處理

     編輯:Java 相關知識

接口定位

適用於:前端輪詢支付結果、後端定時任務對賬、退款前確認訂單狀態、支付超時關單前查詢。

請求參數

字段必填說明
transaction_id二選一微信支付訂單號
out_trade_no二選一商戶系統訂單號
nonce_str是隨機字符串

核心實現代碼

@Controller
@RequestMapping("/shop/weiXinPayOrderSearch")
public class WeiXinPayOrderSearchController {

    @Autowired WeiXinPayService weiXinPayService;

    @RequestMapping(value = "/getWeiXinPayOrder", method = RequestMethod.POST)
    @ResponseBody
    public Object queryOrder(HttpServletRequest request,
                             @RequestParam(required = false) String transactionId,
                             @RequestParam(required = false) String outTradeNo) {

        // 1. 參數校驗
        if (StringUtils.isBlank(transactionId) && StringUtils.isBlank(outTradeNo)) {
            return error("transaction_id 與 out_trade_no 至少填一個");
        }

        // 2. 構造請求 XML
        String nonceStr = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String xml = ClientCustomSSL.SearchNativePackage(
                StringUtils.trimToEmpty(transactionId),
                StringUtils.trimToEmpty(outTradeNo),
                nonceStr
        );

        // 3. 雙向 HTTPS 發送
        try (CloseableHttpClient client = buildHttpClient()) {
            HttpPost post = new HttpPost("https://api.mch.weixin.qq.com/pay/orderquery");
            post.setEntity(new StringEntity(xml, "UTF-8"));

            try (CloseableHttpResponse resp = client.execute(post)) {
                Document doc = new SAXReader().read(resp.getEntity().getContent());
                Element root = doc.getRootElement();

                String returnCode = root.elementText("return_code");
                String resultCode = root.elementText("result_code");

                // 4. 通信層失敗
                if (!"SUCCESS".equals(returnCode)) {
                    return error("通信失敗: " + root.elementText("return_msg"));
                }

                // 5. 業務層失敗
                if (!"SUCCESS".equals(resultCode)) {
                    return error("業務失敗: " + root.elementText("err_code_des"),
                            "errCode", root.elementText("err_code"));
                }

                // 6. 成功解析關鍵字段
                JSONObject result = new JSONObject();
                result.put("tradeState", root.elementText("trade_state")); // SUCCESS/REFUND/NOTPAY/CLOSED/REVOKED/USERPAYING/PAYERROR
                result.put("transactionId", root.elementText("transaction_id"));
                result.put("outTradeNo", root.elementText("out_trade_no"));
                result.put("totalFee", root.elementText("total_fee")); // 分
                result.put("status", "success");
                return result;
            }
        } catch (Exception e) {
            log.error("查詢訂單異常", e);
            return error("系統異常: " + e.getMessage());
        }
    }

    private CloseableHttpClient buildHttpClient() throws Exception {
        KeyStore ks = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream(certPath)) {
            ks.load(fis, mchId.toCharArray());
        }
        SSLContext ssl = SSLContexts.custom()
                .loadKeyMaterial(ks, mchId.toCharArray())
                .build();
        return HttpClients.custom()
                .setSSLSocketFactory(new SSLConnectionSocketFactory(ssl,
                        new String[]{"TLSv1"}, null,
                        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER))
                .build();
    }

    private JSONObject error(String msg, String... extra) {
        JSONObject r = new JSONObject();
        r.put("status", "error");
        r.put("msg", msg);
        for (int i = 0; i < extra.length; i += 2) r.put(extra[i], extra[i+1]);
        return r;
    }
}

trade_state 狀態機說明

狀態值含義建議動作
SUCCESS支付成功更新本地訂單為已支付,發貨/發碼
REFUND已退款標記退款完成
NOTPAY未支付繼續輪詢或引導用戶支付
CLOSED已關單標記關閉,可重新下單
REVOKED已撤銷同 CLOSED
USERPAYING用戶支付中繼續輪詢
PAYERROR支付失敗記錄失敗原因,引導重試

最佳實踐建議

  1. 優先用 out_trade_no 查詢:自主可控,不依賴微信單號。
  2. 輪詢策略:前端 3s/次,最長 2 分鐘;後端定時任務每 10 分鐘掃描 24 小時內未完成訂單。
  3. 幂等處理:查詢到 SUCCESS 後更新訂單狀態需加樂觀鎖或狀態機判斷,防重複發貨。
  4. 異常重試:網絡超時/5xx 錯誤指數退避重試 3 次,記錄失敗告警。
  5. 日誌留存:完整請求/響應 XML 入庫(脫敏),便於對賬溯源。

常見錯誤碼速查

SYSTEMERROR      系統錯誤,重試
APPID_NOT_EXIST  AppId 不存在,檢查配置
MCHID_NOT_EXIST  MchId 不存在,檢查配置
OUT_TRADE_NO_USED 商戶訂單號重複,換單號
SIGNERROR        簽名錯誤,核對 Key/參數排序
XML_FORMAT_ERROR XML 格式錯誤,檢查特殊字符轉義
REQUIRE_POST_METHOD 請求方法非 POST
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved