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

微信支付 Java 實戰:Native 掃碼支付完整流程與代碼實作

     編輯:Java 相關知識

前置準備

  1. 申請微信公眾號/商戶號,獲取 AppID、MchID、API Key (v2/v3)、商戶證書 (apiclient_cert.p12)。
  2. 設定支付授權目錄與回調通知 URL(需外網可訪問,建議用內網穿透測試)。

核心工具類

1. MD5 簽名工具

public class WxPayMD5 {
    public static String sign(String data, String key) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] bytes = (data + "&key=" + key).getBytes(StandardCharsets.UTF_8);
            byte[] digest = md.digest(bytes);
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) sb.append(String.format("%02X", b));
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5 簽名失敗", e);
        }
    }
}

2. 參數排序與 XML 組裝

public class WxPayUtil {
    public static String toXml(Map<String, String> params) {
        // 1. 參數按 ASCII 排序
        StringBuilder sb = new StringBuilder("<xml>");
        params.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(e -> {
                String v = e.getValue();
                if (v.matches("\\d+")) sb.append("<").append(e.getKey()).append(">").append(v).append("</").append(e.getKey()).append(">");
                else sb.append("<").append(e.getKey()).append("><![CDATA[").append(v).append("]]></").append(e.getKey()).append(">");
            });
        sb.append("</xml>");
        return sb.toString();
    }

    public static Map<String, String> xmlToMap(String xml) {
        // 簡易解析,生產建議用 dom4j 或 JAXB
        Map<String, String> map = new HashMap<>();
        // ... 略
        return map;
    }
}

統一下單 - Native Pay

@RestController
@RequestMapping("/pay/wx")
public class WxNativePayController {

    @Value("${wx.appid}") String appId;
    @Value("${wx.mchid}") String mchId;
    @Value("${wx.key}") String apiKey;
    @Value("${wx.notify-url}") String notifyUrl;
    @Value("${wx.cert-path}") String certPath;

    @PostMapping("/native")
    public Result unifiedOrder(@RequestBody OrderReq req) {
        try {
            // 1. 組裝業務參數
            Map<String, String> params = new HashMap<>();
            params.put("appid", appId);
            params.put("mch_id", mchId);
            params.put("nonce_str", RandomStringUtils.randomAlphanumeric(32));
            params.put("body", req.getSubject());
            params.put("out_trade_no", req.getOrderNo());
            params.put("total_fee", String.valueOf(req.getAmountCents())); // 單位:分
            params.put("spbill_create_ip", "127.0.0.1"); // 實際應取客戶端 IP
            params.put("notify_url", notifyUrl);
            params.put("trade_type", "NATIVE");

            // 2. 簽名
            String sign = WxPayMD5.sign(WxPayUtil.buildSignString(params), apiKey);
            params.put("sign", sign);

            // 3. 發送 HTTPS 請求(雙向憑證)
            String xmlReq = WxPayUtil.toXml(params);
            String xmlResp = HttpClientUtil.postWithCert(
                "https://api.mch.weixin.qq.com/pay/unifiedorder",
                xmlReq, certPath, mchId
            );

            // 4. 解析回應
            Map<String, String> resp = WxPayUtil.xmlToMap(xmlResp);
            if ("SUCCESS".equals(resp.get("return_code")) && "SUCCESS".equals(resp.get("result_code"))) {
                String codeUrl = resp.get("code_url"); // 此 URL 可直接生成二維碼
                return Result.ok(codeUrl);
            } else {
                return Result.fail(resp.get("err_code_des"));
            }
        } catch (Exception e) {
            log.error("統一下單失敗", e);
            return Result.fail("系統異常");
        }
    }
}

關鍵點總結

  • 金額單位為分:傳入前 元 * 100 取整。
  • 雙向憑證:Native/退款/撤銷等涉及資金的介面需載入 .p12 憑證建立 SSLContext。
  • 簽名算法:v2 版為 MD5,v3 版為 RSA (SHA256 with RSA),本文示範 v2。
  • 回調驗簽:收到通知後,必須用相同算法驗證 sign,防偽造。
  • 冪等性:回調處理邏輯需冪等(以 out_trade_no 或 transaction_id 判斷)。

結語

微信 Native 支付核心在於「統一下單 → 獲取 code_url → 前端生成二維碼 → 用戶掃碼 → 後台接收回調」。掌握參數組裝、簽名、雙向憑證請求三大塊,即可穩健接入。建議封裝成通用 SDK,業務層僅需呼叫 payService.nativePay(order)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved