Java接入微信支付詳細(xì)實(shí)現(xiàn)步驟
一:主要流程
(1)前期準(zhǔn)備工作
①注冊(cè)認(rèn)證微信公眾號(hào)/小程序
②申請(qǐng)微信商戶號(hào)
③配置API安全
(2)環(huán)境搭建和主要流程理解
①引入依賴(lài)
②確定支付方式,分析其流程
(3)核心代碼開(kāi)發(fā)
①配置參數(shù)
②向微信發(fā)送請(qǐng)求
二:注意事項(xiàng)
(1)個(gè)人類(lèi)型賬號(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
②引入依賴(lài)
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.4.13</version> <!-- 注意檢查最新版本 -->
</dependency>③代碼實(shí)現(xiàn)
配置類(lèi):
@Configuration
public class WeChatPayConfig {
@Value("${wechat.pay.mchid}")
private String mchId;
@Value("${wechat.pay.mchSerialNo}") // 你的商戶API證書(shū)序列號(hào)
private String mchSerialNo;
@Value("${wechat.pay.privateKeyPath}") // 你的.p12證書(shū)中的私鑰文件路徑 (.pem格式更佳)
private String privateKeyPath;
@Value("${wechat.pay.apiV3Key}")
private String apiV3Key;
@Value("${wechat.pay.certPath}") // 下載的微信平臺(tái)證書(shū)路徑
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)證書(shū) (簡(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ù) (類(lèi)似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提供了工具類(lèi))
// 這里需要解析請(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("");
}
}
}工具類(lèi)
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)、證書(shū)、配置,需仔細(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)接口重試的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
Java Mail與Apache Mail發(fā)送郵件示例
這篇文章主要介紹了Java Mail與Apache Mail發(fā)送郵件示例的相關(guān)資料,需要的朋友可以參考下2014-10-10
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

