欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java接入微信支付詳細(xì)實(shí)現(xiàn)步驟

 更新時(shí)間:2025年09月30日 08:54:17   作者:阿海打醬油  
在Java中接入微信支付,通常需要通過(guò)微信提供的微信支付開(kāi)放平臺(tái)來(lái)進(jìn)行集成,這篇文章主要介紹了Java接入微信支付的詳細(xì)實(shí)現(xiàn)步驟,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

一:主要流程

(1)前期準(zhǔn)備工作

①注冊(cè)認(rèn)證微信公眾號(hào)/小程序

②申請(qǐng)微信商戶號(hào)

③配置API安全

(2)環(huán)境搭建和主要流程理解

①引入依賴

②確定支付方式,分析其流程

(3)核心代碼開(kāi)發(fā)

①配置參數(shù)

②向微信發(fā)送請(qǐng)求

二:注意事項(xiàng)

(1)個(gè)人類型賬號(hào)無(wú)法開(kāi)通微信支付

(2)切勿泄露APIV3秘鑰

三:步驟實(shí)現(xiàn)

官方文檔(以Native為例):產(chǎn)品介紹_Native支付|微信支付商戶文檔中心

先查看具體流程

(1)先注冊(cè)對(duì)應(yīng)的公眾號(hào)/小程序

官網(wǎng)鏈接:https://mp.weixin.qq.com/

(2)認(rèn)證對(duì)應(yīng)的公眾號(hào)/小程序

①方法一:

②方法二:

(3)注冊(cè)商戶號(hào),開(kāi)通對(duì)應(yīng)的Native支付權(quán)限

官網(wǎng)鏈接:https://pay.weixin.qq.com/

開(kāi)通步驟指引鏈接:申請(qǐng)Native支付權(quán)限指引_Native支付|微信支付商戶文檔中心

(4)綁定商戶號(hào)與APPID

對(duì)應(yīng)指引鏈接:管理商戶號(hào)綁定的APPID賬號(hào)_Native支付|微信支付商戶文檔中心

(5)獲取對(duì)應(yīng)參數(shù)開(kāi)發(fā)

對(duì)應(yīng)指引鏈接:開(kāi)發(fā)必要參數(shù)說(shuō)明_通用規(guī)則|微信支付商戶文檔中心

(6)核心代碼開(kāi)發(fā)

①推薦使用官方SDK:https://github.com/wechatpay-apiv3/wechatpay-java

②引入依賴

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.4.13</version> <!-- 注意檢查最新版本 -->
</dependency>

③代碼實(shí)現(xiàn)

配置類:

@Configuration
public class WeChatPayConfig {

    @Value("${wechat.pay.mchid}")
    private String mchId;

    @Value("${wechat.pay.mchSerialNo}") // 你的商戶API證書序列號(hào)
    private String mchSerialNo;

    @Value("${wechat.pay.privateKeyPath}") // 你的.p12證書中的私鑰文件路徑 (.pem格式更佳)
    private String privateKeyPath;

    @Value("${wechat.pay.apiV3Key}")
    private String apiV3Key;

    @Value("${wechat.pay.certPath}") // 下載的微信平臺(tái)證書路徑
    private String certPath;

    /**
     * 創(chuàng)建私鑰簽名對(duì)象
     */
    @Bean
    public PrivateKey getPrivateKey() throws Exception {
        return PemUtil.loadPrivateKey(
            new FileInputStream(privateKeyPath));
    }

    /**
     * 創(chuàng)建自動(dòng)更新的Verifier (驗(yàn)證微信響應(yīng)簽名)
     */
    @Bean
    public Verifier getVerifier() throws Exception {
        // 方式1: 使用本地已下載的平臺(tái)證書 (簡(jiǎn)單,需手動(dòng)更新)
        // X509Certificate certificate = PemUtil.loadCertificate(new FileInputStream(certPath));
        // return new StaticVerifier(mchId, mchSerialNo, certificate);

        // 方式2: 使用自動(dòng)更新機(jī)制 (推薦)
        ScheduledUpdateCertificatesVerifier verifier = 
            new ScheduledUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, getPrivateKey())),
                apiV3Key.getBytes(StandardCharsets.UTF_8));
        return verifier;
    }

    /**
     * 創(chuàng)建HttpClient (用于調(diào)用微信API)
     */
    @Bean
    public CloseableHttpClient getWxPayClient() throws Exception {
        return WechatPayHttpClientBuilder.create()
            .withMerchant(mchId, mchSerialNo, getPrivateKey())
            .withValidator(new WechatPay2Validator(getVerifier()))
            .build();
    }
}

服務(wù)層(統(tǒng)一下單)示例與代碼(需調(diào)整)

@Override
public String createNativeOrder(String outTradeNo, int totalFee, String body) {
    // ... 構(gòu)建請(qǐng)求參數(shù) (類似JSAPI,但去掉payer,增加scene_info等)
    json.put("appid", appid); // Native也需要appid
    json.put("mchid", mchId);
    json.put("description", body);
    json.put("out_trade_no", outTradeNo);
    json.put("notify_url", "https://yourdomain.com/api/wxpay/notify");

    JSONObject amount = new JSONObject();
    amount.put("total", totalFee);
    amount.put("currency", "CNY");
    json.put("amount", amount);

    // 場(chǎng)景信息 (可選)
    JSONObject sceneInfo = new JSONObject();
    sceneInfo.put("payer_client_ip", "用戶IP"); // 必須傳真實(shí)IP
    json.put("scene_info", sceneInfo);

    // 發(fā)送請(qǐng)求到 Native 接口
    try {
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
        // ... 同上設(shè)置請(qǐng)求頭和實(shí)體
        // 解析響應(yīng),獲取 code_url
        JSONObject result = JSON.parseObject(responseString);
        return result.getString("code_url"); // 返回二維碼鏈接
    } catch (Exception e) {
        throw new RuntimeException("創(chuàng)建Native訂單失敗", e);
    }
}

④回調(diào)處理

注意:此路徑必須是公網(wǎng)能訪問(wèn)的路徑

@RestController
@RequestMapping("/api/wxpay")
public class WeChatPayController {

    @Autowired
    private WeChatPayService weChatPayService;

    @PostMapping("/create-jsapi-order")
    public ResponseEntity<Map<String, String>> createJsapiOrder(@RequestBody PayRequest request) {
        // 獲取用戶openid (通常由前端通過(guò)登錄態(tài)傳遞或后端通過(guò)code換取)
        String openid = getUserOpenidFromSessionOrToken(request.getToken());
        Map<String, String> result = weChatPayService.createJsapiOrder(
            openid, request.getOutTradeNo(), request.getTotalFee(), request.getBody());
        return ResponseEntity.ok(result);
    }

    @PostMapping("/notify")
    public ResponseEntity<String> handleNotify(@RequestBody String notifyData, 
                                              HttpServletRequest request) {
        try {
            // 1. 驗(yàn)證通知的簽名和有效性 (SDK提供了工具類)
            // 這里需要解析請(qǐng)求頭中的 Wechatpay-Signature, Wechatpay-Nonce, Wechatpay-Timestamp
            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String body = notifyData; // 請(qǐng)求體

            // 使用之前配置的Verifier來(lái)驗(yàn)證
            // boolean isValid = verifier.verify(timestamp, nonce, body, signature);
            // if (!isValid) {
            //     return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("簽名驗(yàn)證失敗");
            // }

            // 2. 解密通知數(shù)據(jù) (APIv3通知體是加密的)
            AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
            String plainText = aesUtil.decryptToString(
                nonce.getBytes(StandardCharsets.UTF_8),
                associatedData.getBytes(StandardCharsets.UTF_8), // 關(guān)聯(lián)數(shù)據(jù)
                ciphertext.getBytes(StandardCharsets.UTF_8)); // 密文

            // 3. 解析解密后的JSON數(shù)據(jù)
            JSONObject notifyDataObj = JSON.parseObject(plainText);
            String eventType = notifyDataObj.getString("event_type");
            JSONObject resource = notifyDataObj.getJSONObject("resource");
            String ciphertext = resource.getString("ciphertext"); // 加密的數(shù)據(jù)包
            String associatedData = resource.getString("associated_data");
            String nonce = resource.getString("nonce");

            // 再次解密ciphertext得到最終的訂單信息
            String orderInfo = aesUtil.decryptToString(nonce.getBytes(), 
                associatedData.getBytes(), ciphertext.getBytes());
            JSONObject orderData = JSON.parseObject(orderInfo);

            String outTradeNo = orderData.getString("out_trade_no");
            String transactionId = orderData.getString("transaction_id");
            String tradeState = orderData.getString("trade_state"); // SUCCESS為支付成功

            // 4. 業(yè)務(wù)處理
            if ("SUCCESS".equals(tradeState)) {
                // TODO: 更新你的數(shù)據(jù)庫(kù)訂單狀態(tài)為"已支付"
                // TODO: 執(zhí)行發(fā)貨、積分發(fā)放等后續(xù)業(yè)務(wù)邏輯
                // TODO: 記錄日志
            } else if ("CLOSED".equals(tradeState) || "REVOKED".equals(tradeState)) {
                // 支付失敗或關(guān)閉
            }

            // 5. 返回成功響應(yīng) (必須是200且內(nèi)容為"success")
            return ResponseEntity.ok().body("{\"code\":\"SUCCESS\",\"message\":\"成功\"}");
        } catch (Exception e) {
            // 記錄詳細(xì)的錯(cuò)誤日志
            log.error("處理微信支付通知失敗", e);
            // 返回失敗,微信會(huì)在一段時(shí)間后重試
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
        }
    }
}

工具類

public class WeChatPayUtil {
    // 生成JSAPI調(diào)用所需參數(shù)的簽名 (注意:這是RSA-SHA256,不是APIv3的HMAC-SHA256)
    public static String sign(Map<String, String> params, String apiKey) {
        // 1. 參數(shù)按key ASCII排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);

        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (value != null && !value.trim().isEmpty() && !"sign".equals(key)) {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        sb.append("key=").append(apiKey);

        // 2. 計(jì)算MD5 (舊版) 或 HMAC-SHA256 (新版推薦,但JSAPI仍多用MD5?實(shí)際看文檔)
        // 注意:微信JSAPI目前主流仍是MD5,但請(qǐng)查閱最新官方文檔確認(rèn)!
        String stringA = sb.toString();
        String sign = DigestUtils.md5Hex(stringA).toUpperCase();

        return sign;
    }
}

四:總結(jié)

對(duì)接微信支付主要繁瑣的是前期商戶號(hào)、證書、配置,需仔細(xì)閱讀文檔,后續(xù)對(duì)接SDK非常簡(jiǎn)單,注意處理冪等性

到此這篇關(guān)于Java接入微信支付的文章就介紹到這了,更多相關(guān)Java接入微信支付內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Guava?Retryer實(shí)現(xiàn)接口重試的示例

    Guava?Retryer實(shí)現(xiàn)接口重試的示例

    本文主要介紹了Guava?Retryer實(shí)現(xiàn)接口重試的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Java讀取郵件的方法

    Java讀取郵件的方法

    這篇文章主要介紹了Java讀取郵件的方法,以163郵件服務(wù)器為例說(shuō)明了Java讀取郵件的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-01-01
  • JDK8中新增的Optional工具類基本使用

    JDK8中新增的Optional工具類基本使用

    Optional不是對(duì)null關(guān)鍵字的一種替代,而是對(duì)于null判定提供了一種更加優(yōu)雅的實(shí)現(xiàn),接下來(lái)通過(guò)本文給大家分享JDK8中新增的Optional工具類基本使用,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • 帶你快速搞定java數(shù)組

    帶你快速搞定java數(shù)組

    數(shù)組是相同類型數(shù)據(jù)的有序集合數(shù)組描述的是相同類型的若干個(gè)數(shù)據(jù),按照一定的先后次序排列組合而成。其中,每一個(gè)數(shù)據(jù)稱作一個(gè)數(shù)組元素,每個(gè)數(shù)組元素可以通過(guò)一個(gè)下標(biāo)來(lái)訪問(wèn)它們數(shù)組的聲明創(chuàng)建
    2021-07-07
  • Java Mail與Apache Mail發(fā)送郵件示例

    Java Mail與Apache Mail發(fā)送郵件示例

    這篇文章主要介紹了Java Mail與Apache Mail發(fā)送郵件示例的相關(guān)資料,需要的朋友可以參考下
    2014-10-10
  • java獲取整點(diǎn)與凌晨的時(shí)間戳

    java獲取整點(diǎn)與凌晨的時(shí)間戳

    這篇文章主要給大家介紹了關(guān)于java獲取凌晨與整點(diǎn)時(shí)間戳的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • mybatis plus怎么忽略映射字段

    mybatis plus怎么忽略映射字段

    這篇文章主要介紹了mybatis plus怎么忽略映射字段,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • Spring?Boot?如何正確讀取配置文件屬性

    Spring?Boot?如何正確讀取配置文件屬性

    這篇文章主要介紹了Spring?Boot?如何正確讀取配置文件屬性,項(xiàng)目中經(jīng)常會(huì)經(jīng)常讀取配置文件中的屬性的值,Spring?Boot提供了很多注解讀取配置文件屬性,那么如何正確使用呢,下文一起來(lái)參考下面文章內(nèi)容吧
    2022-04-04
  • SpringMVC基于注解方式實(shí)現(xiàn)上傳下載

    SpringMVC基于注解方式實(shí)現(xiàn)上傳下載

    本文主要介紹了SpringMVC基于注解方式實(shí)現(xiàn)上傳下載,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • java讀取txt文件并輸出結(jié)果

    java讀取txt文件并輸出結(jié)果

    這篇文章主要介紹了java讀取txt文件并輸出結(jié)果,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論