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

Java 實作微信支付 V3 服務端完整流程與最佳實踐

     編輯:Java 相關知識

前言

微信支付是中國市場不可或缺的支付方式。本文基於微信支付 V3 版 API (JSAPI/APP/NATIVE/H5),梳理服務端核心流程:統一下單 → 簽名驗證 → 支付通知回調 → 退款/查單,並提供 Spring Boot 整合範例、關鍵工具類與安全性建議。註:原始碼片段為 V2 風格,本文升級為 V3 規範。

一、核心概念與準備工作

  • 商戶號、AppID、APIv3 金鑰、商戶私鑰/公鑰、微信支付平台公鑰 (下載至 cert/ 目錄)。
  • 簽名算法:V3 採用 SHA-256 with RSA (RSA 2048),不再使用 MD5/HMAC-SHA256。
  • 請求驗證:所有 API 請求需在 Header 帶上 Authorization: WECHATPAY2-SHA256-RSA2048 ...。
  • 回調驗證:解密 resource.ciphertext (AEAD_AES_256_GCM) 並驗證簽名。

二、統一下單流程

2.1 構建請求參數

// 以 JSAPI (小程序/公眾號) 為例
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setAppid(appId);
request.setMchid(mchId);
request.setDescription("商品描述 - 測試訂單");
request.setOutTradeNo(orderNo); // 商戶訂單號,唯一
request.setNotifyUrl("https://api.yourdomain.com/pay/notify"); // 回調地址 (HTTPS, 443)
request.setAmount(new Amount().setTotal(1).setCurrency("CNY")); // 單位:分
request.setPayer(new Payer().setOpenid(userOpenId)); // JSAPI 必填
// 可選:scene_info, detail, goods_tag 等

2.2 呼叫微信支付 SDK

使用官方 wechatpay-java SDK (含自動簽名、驗證、解密):

@Autowired
private WxPayService wxPayService; // 封裝了 WxPayClient

public String createJsapiOrder(WxPayUnifiedOrderRequest request) {
    try {
        WxPayUnifiedOrderResponse response = wxPayService.createOrderV3(request);
        String prepayId = response.getPrepayId();
        // 2.3 為前端生成二次簽名
        return generateJsapiPaySign(prepayId);
    } catch (Exception e) {
        log.error("下單失敗", e);
        throw new BizException("下單失敗: " + e.getMessage());
    }
}

2.3 前端二次簽名

private String generateJsapiPaySign(String prepayId) {
    long timeStamp = System.currentTimeMillis() / 1000;
    String nonceStr = WxPayUtil.generateNonceStr();
    String message = appId + "\n" + timeStamp + "\n" + nonceStr + "\n" + "prepay_id=" + prepayId + "\n";
    String paySign = WxPayUtil.signWithPrivateKey(message, merchantPrivateKey); // RSA2048
    // 返回給前端: appId, timeStamp, nonceStr, package="prepay_id=...", signType="RSA", paySign
    return buildJsonResponse(appId, timeStamp, nonceStr, "prepay_id=" + prepayId, paySign);
}

三、支付結果通知回調

3.1 Controller 接收

@PostMapping("/pay/notify")
public ResponseEntity handleNotify(@RequestHeader Map headers,
                                           @RequestBody String body) {
    try {
        // 1. 驗證簽名 (SDK 內建)
        // 2. 解密資料
        WxPayNotifyResponse notify = wxPayService.parseNotify(body, headers);
        String outTradeNo = notify.getOutTradeNo();
        String transactionId = notify.getTransactionId();
        String tradeState = notify.getTradeState(); // SUCCESS

        if ("SUCCESS".equals(tradeState)) {
            // 3. 業務邏輯:更新訂單狀態、發送通知、發貨等 (冪等性處理!)
            orderService.handlePaySuccess(outTradeNo, transactionId);
        }
        // 4. 回應微信 (必須在 5 秒內返回 SUCCESS,否則會重試)
        return ResponseEntity.ok().build();
    } catch (Exception e) {
        log.error("回調處理異常", e);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    }
}

3.2 關鍵點:冪等性與安全性

  • 使用 out_trade_no 或 transaction_id 做唯一索引,防止重複處理。
  • 回調通知可能重複發送,業務邏輯必須冪等。
  • 驗證 Wechatpay-Serial 對應的平台公鑰,防止偽造通知。

四、常用工具類 (簡化版)

public class WxPayUtil {
    // 生成隨機字串
    public static String generateNonceStr() { return UUID.randomUUID().toString().replace("-", ""); }

    // 簽名 (RSA2048 with SHA256)
    public static String signWithPrivateKey(String data, PrivateKey privateKey) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(privateKey);
        sign.update(data.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    // 驗證簽名
    public static boolean verifySign(String data, String signBase64, PublicKey publicKey) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initVerify(publicKey);
        sign.update(data.getBytes(StandardCharsets.UTF_8));
        return sign.verify(Base64.getDecoder().decode(signBase64));
    }

    // 解密回調資料 (AEAD_AES_256_GCM)
    public static String decryptCallback(String associatedData, String nonce, String ciphertext, String apiV3Key) {
        // 使用 javax.crypto.Cipher "AES/GCM/NoPadding" 實作,參考官方文件
    }
}

五、退款與查單

  • 退款:POST /v3/refund/domestic/refunds,需 out_refund_no, out_trade_no, amount, notify_url。同樣有回調通知。
  • 查詢訂單:GET /v3/pay/transactions/id/{transaction_id} 或 /v3/pay/transactions/out-trade-no/{out_trade_no}。
  • 撤銷訂單:POST /v3/pay/transactions/out-trade-no/{out_trade_no}/close (僅未支付訂單)。

六、安全性與合規清單

  1. ✅ 使用 HTTPS (TLS 1.2+) 所有 API 端點。
  2. ✅ 私鑰存放於 HSM 或加密配置中心,嚴禁 硬編碼或提交 Git。
  3. ✅ 定期輪換 APIv3 金鑰、商戶證書。
  4. ✅ 回調介面做 IP 白名單 (微信回調 IP 段) + 簽名驗證雙重防護。
  5. ✅ 敏感資訊 (OpenID、手機號) 脫敏日誌。
  6. ✅ 遵守《支付機構條碼支付業務規範》等監管要求。

七、結語

微信支付 V3 雖然簽名/加密流程較 V2 複雜,但安全性大幅提升。善用官方 wechatpay-java SDK 可屏蔽大部分底層細節,讓開發者專注業務邏輯。完整 Spring Boot 整合專案已開源至 GitHub (連結略),歡迎參考貢獻。

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