SpringBoot+MyBatis集成微信支付實現(xiàn)示例
下面我將詳細介紹使用 Spring Boot + MyBatis 實現(xiàn)微信支付(JSAPI支付)的完整流程和代碼示例。
微信支付核心流程(JSAPI支付)
- 商戶系統(tǒng)生成訂單
- 獲取用戶OpenID(微信公眾號支付)
- 調(diào)用微信統(tǒng)一下單API
- 生成JSAPI調(diào)起支付參數(shù)
- 前端調(diào)起微信支付
- 微信異步通知支付結(jié)果
- 商戶處理支付結(jié)果更新訂單狀態(tài)
- 返回處理結(jié)果給微信
代碼實現(xiàn)示例
1. 添加依賴
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis & MySQL -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 微信支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
<!-- XML處理 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
2. 微信支付配置類
@Configuration
public class WxPayConfig {
@Value("${wxpay.app_id}")
private String appId;
@Value("${wxpay.mch_id}")
private String mchId;
@Value("${wxpay.mch_key}")
private String mchKey;
@Value("${wxpay.notify_url}")
private String notifyUrl;
@Value("${wxpay.cert_path}")
private String certPath; // 證書路徑(apiclient_cert.p12)
@Value("${wxpay.api_v3_key}")
private String apiV3Key;
// 微信支付HttpClient
@Bean
public CloseableHttpClient wxPayHttpClient() throws Exception {
// 加載商戶私鑰
PrivateKey merchantPrivateKey = getPrivateKey();
// 使用自動更新平臺證書的驗證器
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
// 初始化httpClient
return WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
}
// 獲取商戶私鑰
private PrivateKey getPrivateKey() throws Exception {
InputStream inputStream = new ClassPathResource(certPath).getInputStream();
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(inputStream, mchId.toCharArray());
return (PrivateKey) keyStore.getKey(mchId, mchId.toCharArray());
}
// 微信支付服務
@Bean
public WxPayService wxPayService(CloseableHttpClient wxPayHttpClient) {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(appId);
payConfig.setMchId(mchId);
payConfig.setKey(mchKey);
payConfig.setNotifyUrl(notifyUrl);
payConfig.setApiV3Key(apiV3Key);
return new WxPayServiceImpl(payConfig, wxPayHttpClient);
}
}
3. 實體類和Mapper
// 訂單實體
@Data
public class Order {
private Long id;
private String orderNo; // 商戶訂單號
private BigDecimal amount;// 支付金額
private Integer status; // 0-待支付, 1-已支付
private LocalDateTime createTime;
private String openid; // 微信用戶openid
}
// MyBatis Mapper
@Mapper
public interface OrderMapper {
@Insert("INSERT INTO orders(order_no, amount, status, create_time, openid) " +
"VALUES(#{orderNo}, #{amount}, 0, NOW(), #{openid})")
@Options(useGeneratedKeys = true, keyProperty = "id")
void insert(Order order);
@Update("UPDATE orders SET status = #{status} WHERE order_no = #{orderNo}")
void updateStatus(@Param("orderNo") String orderNo, @Param("status") int status);
@Select("SELECT * FROM orders WHERE order_no = #{orderNo}")
Order findByOrderNo(String orderNo);
}
4. 支付服務類
@Service
public class WxPayService {
@Autowired private WxPayService wxPayService;
@Autowired private OrderMapper orderMapper;
// 創(chuàng)建支付訂單
public Map<String, String> createPayOrder(Order order) throws Exception {
orderMapper.insert(order); // 保存訂單到數(shù)據(jù)庫
// 構(gòu)建統(tǒng)一下單請求
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
request.setBody("商品支付");
request.setOutTradeNo(order.getOrderNo());
request.setTotalFee(order.getAmount().multiply(new BigDecimal(100)).intValue()); // 微信支付單位為分
request.setSpbillCreateIp("用戶IP"); // 實際獲取用戶IP
request.setNotifyUrl(wxPayConfig.getNotifyUrl());
request.setTradeType("JSAPI");
request.setOpenid(order.getOpenid());
// 調(diào)用統(tǒng)一下單API
WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request);
// 生成JSAPI調(diào)起支付參數(shù)
Map<String, String> payParams = new HashMap<>();
payParams.put("appId", appId);
payParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payParams.put("nonceStr", WxPayUtil.generateNonceStr());
payParams.put("package", "prepay_id=" + result.getPrepayId());
payParams.put("signType", "RSA");
// 生成簽名
String sign = WxPayUtil.generateSignature(payParams, mchKey);
payParams.put("paySign", sign);
return payParams;
}
// 處理支付結(jié)果通知
public String handleNotify(HttpServletRequest request) {
try {
// 解析通知內(nèi)容
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlResult);
// 驗證簽名和業(yè)務結(jié)果
if ("SUCCESS".equals(notifyResult.getResultCode())) {
// 更新訂單狀態(tài)
String orderNo = notifyResult.getOutTradeNo();
orderMapper.updateStatus(orderNo, 1); // 更新為已支付
// 返回成功響應
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}
} catch (Exception e) {
// 記錄日志
}
return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[處理失敗]]></return_msg></xml>";
}
}
5. 控制器
@RestController
@RequestMapping("/wxpay")
public class WxPayController {
@Autowired private WxPayService wxPayService;
// 創(chuàng)建支付訂單(返回調(diào)起支付所需參數(shù))
@PostMapping("/create")
public Map<String, String> createOrder(@RequestParam BigDecimal amount,
@RequestParam String openid) throws Exception {
Order order = new Order();
order.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
order.setAmount(amount);
order.setOpenid(openid);
return wxPayService.createPayOrder(order);
}
// 微信支付結(jié)果通知(需要公網(wǎng)可訪問)
@PostMapping(value = "/notify", produces = "application/xml; charset=UTF-8")
public String wxpayNotify(HttpServletRequest request) {
return wxPayService.handleNotify(request);
}
// 查詢訂單狀態(tài)
@GetMapping("/status")
public String getOrderStatus(@RequestParam String orderNo) {
Order order = orderMapper.findByOrderNo(orderNo);
if (order == null) {
return "訂單不存在";
}
return order.getStatus() == 1 ? "已支付" : "未支付";
}
}
6. 前端調(diào)用示例(Vue.js)
<template>
<div>
<button @click="createOrder">微信支付</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
methods: {
async createOrder() {
try {
// 1. 獲取用戶openid(實際項目中需要通過OAuth2授權(quán)獲?。?
const openid = '用戶openid';
// 2. 創(chuàng)建訂單
const response = await axios.post('/wxpay/create', {
amount: 100, // 支付金額(元)
openid: openid
});
// 3. 調(diào)起微信支付
const payParams = response.data;
wx.chooseWXPay({
...payParams,
success: (res) => {
console.log('支付成功', res);
// 可跳轉(zhuǎn)到支付成功頁面
},
fail: (err) => {
console.error('支付失敗', err);
}
});
} catch (error) {
console.error('創(chuàng)建訂單失敗', error);
}
}
}
}
</script>
7. 配置文件
# application.properties # 微信支付配置 wxpay.app_id=wx1234567890abcdef wxpay.mch_id=1234567890 wxpay.mch_key=your_mch_key wxpay.api_v3_key=your_api_v3_key wxpay.notify_url=http://your-domain.com/wxpay/notify wxpay.cert_path=classpath:cert/apiclient_cert.p12 # MySQL配置 spring.datasource.url=jdbc:mysql://localhost:3306/wxpay_demo spring.datasource.username=root spring.datasource.password=123456
關(guān)鍵流程說明
獲取用戶OpenID
- 微信公眾號支付需要獲取用戶的openid
- 通過微信OAuth2授權(quán)流程獲?。ㄐ柙谖⑿殴娞柡笈_配置授權(quán)域名)
調(diào)用統(tǒng)一下單API
- 使用
WxPayUnifiedOrderRequest構(gòu)建請求 - 關(guān)鍵參數(shù):訂單號、金額(分)、openid、回調(diào)地址
- 返回 prepay_id(預支付交易會話標識)
- 使用
生成JSAPI調(diào)起支付參數(shù)
- 包含 appId、timeStamp、nonceStr、package、signType
- 使用商戶密鑰生成簽名(paySign)
前端調(diào)起支付
- 使用微信JSAPI的 chooseWXPay 方法
- 傳入支付參數(shù)調(diào)起支付界面
接收異步通知
- 必須驗證簽名(防止偽造請求)
- 檢查 result_code 是否為 SUCCESS
- 更新訂單狀態(tài)(注意處理冪等性)
安全注意事項
- 支付金額需與訂單金額比對(防止金額篡改)
- 敏感操作記錄日志
- 異步通知處理需要保證冪等性
微信支付與支付寶支付的區(qū)別
| 特性 | 微信支付 | 支付寶支付 |
|---|---|---|
| 支付方式 | JSAPI、Native、App、H5等 | 電腦網(wǎng)站、手機網(wǎng)站、App等 |
| 金額單位 | 分 | 元 |
| 簽名算法 | HMAC-SHA256/RSA | RSA/RSA2 |
| 通知格式 | XML | Form表單/JSON |
| 證書要求 | 需要API證書 | 不需要證書 |
| OpenID | 需要獲取用戶openid | 不需要用戶標識 |
| 支付流程 | 需要前端調(diào)起支付 | 自動跳轉(zhuǎn)支付頁面 |
常見問題解決方案
支付金額單位錯誤
- 微信支付單位為分,支付寶單位為元
- 轉(zhuǎn)換公式:微信金額 = 支付寶金額 × 100
簽名驗證失敗
- 檢查商戶密鑰是否正確
- 確認簽名算法一致(微信默認HMAC-SHA256)
- 驗證參數(shù)是否完整且順序正確
異步通知處理
- 必須返回XML格式的響應
- 處理速度要快(微信會在30秒內(nèi)重試)
- 保證冪等性(防止重復處理)
跨域問題
- 前端調(diào)起支付需要在微信內(nèi)置瀏覽器
- 確保公眾號JS接口安全域名配置正確
證書管理
- 定期更新API證書
- 證書文件妥善保管(不要提交到代碼倉庫)
實際開發(fā)中需要根據(jù)具體業(yè)務需求進行調(diào)整,并注意支付安全相關(guān)事項。
到此這篇關(guān)于SpringBoot+MyBatis集成微信支付實現(xiàn)示例的文章就介紹到這了,更多相關(guān)SpringBoot MyBatis微信支付內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot實現(xiàn)微信支付接口調(diào)用及回調(diào)函數(shù)(商戶參數(shù)獲取)
- java?Springboot對接開發(fā)微信支付詳細流程
- SpringBoot對接小程序微信支付的實現(xiàn)
- Springboot整合微信支付(訂單過期取消及商戶主動查單)
- UniApp?+?SpringBoot?實現(xiàn)微信支付和退款功能
- SpringBoot實現(xiàn)整合微信支付方法詳解
- springboot對接微信支付的完整流程(附前后端代碼)
- 一篇文章帶你入門Springboot整合微信登錄與微信支付(附源碼)
- springboot整合微信支付sdk過程解析
相關(guān)文章
mybatis-plus數(shù)據(jù)權(quán)限實現(xiàn)代碼
這篇文章主要介紹了mybatis-plus數(shù)據(jù)權(quán)限實現(xiàn),結(jié)合了mybatis-plus的插件方式,做出了自己的注解方式的數(shù)據(jù)權(quán)限,雖然可能存在一部分的局限性,但很好的解決了我們自己去解析SQL的功能,需要的朋友可以參考下2023-06-06
IDEA 2020 無法啟動的解決辦法(啟動崩盤)附IDEA 2020 新功能
這篇文章主要介紹了IDEA 2020 無法啟動的解決辦法(啟動崩盤)附IDEA 2020 新功能,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Jmeter連接Mysql數(shù)據(jù)庫實現(xiàn)過程詳解
這篇文章主要介紹了Jmeter連接Mysql數(shù)據(jù)庫實現(xiàn)過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
Idea如何使用System.getenv()獲取環(huán)境變量的值
文章介紹了如何在Java中使用`System.getenv()`方法讀取環(huán)境變量的值,并提供了兩種配置環(huán)境變量的方法:啟動項配置和系統(tǒng)環(huán)境變量配置,對于系統(tǒng)環(huán)境變量,文章特別指出需要重啟電腦或程序才能使其生效2024-11-11
IntelliJ IDEA 常用設(shè)置(配置)吐血整理(首次安裝必需)
這篇文章主要介紹了IntelliJ IDEA 常用設(shè)置(配置)吐血整理(首次安裝必需),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06

