SpringBoot+MyBatis集成微信支付实现示例
作者:yuren_xia
本文主要介绍了SpringBoot+MyBatis集成微信支付实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
下面我将详细介绍使用 Spring Boot + MyBatis 实现微信支付(JSAPI支付)的完整流程和代码示例。
微信支付核心流程(JSAPI支付)
- 商户系统生成订单
- 获取用户OpenID(微信公众号支付)
- 调用微信统一下单API
- 生成JSAPI调起支付参数
- 前端调起微信支付
- 微信异步通知支付结果
- 商户处理支付结果更新订单状态
- 返回处理结果给微信
代码实现示例
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;
// 创建支付订单
public Map<String, String> createPayOrder(Order order) throws Exception {
orderMapper.insert(order); // 保存订单到数据库
// 构建统一下单请求
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());
// 调用统一下单API
WxPayUnifiedOrderResult result = wxPayService.unifiedOrder(request);
// 生成JSAPI调起支付参数
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;
}
// 处理支付结果通知
public String handleNotify(HttpServletRequest request) {
try {
// 解析通知内容
String xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlResult);
// 验证签名和业务结果
if ("SUCCESS".equals(notifyResult.getResultCode())) {
// 更新订单状态
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;
// 创建支付订单(返回调起支付所需参数)
@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);
}
// 微信支付结果通知(需要公网可访问)
@PostMapping(value = "/notify", produces = "application/xml; charset=UTF-8")
public String wxpayNotify(HttpServletRequest request) {
return wxPayService.handleNotify(request);
}
// 查询订单状态
@GetMapping("/status")
public String getOrderStatus(@RequestParam String orderNo) {
Order order = orderMapper.findByOrderNo(orderNo);
if (order == null) {
return "订单不存在";
}
return order.getStatus() == 1 ? "已支付" : "未支付";
}
}
6. 前端调用示例(Vue.js)
<template>
<div>
<button @click="createOrder">微信支付</button>
</div>
</template>
<script>
import axios from 'axios';
export default {
methods: {
async createOrder() {
try {
// 1. 获取用户openid(实际项目中需要通过OAuth2授权获取)
const openid = '用户openid';
// 2. 创建订单
const response = await axios.post('/wxpay/create', {
amount: 100, // 支付金额(元)
openid: openid
});
// 3. 调起微信支付
const payParams = response.data;
wx.chooseWXPay({
...payParams,
success: (res) => {
console.log('支付成功', res);
// 可跳转到支付成功页面
},
fail: (err) => {
console.error('支付失败', err);
}
});
} catch (error) {
console.error('创建订单失败', 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
关键流程说明
获取用户OpenID
- 微信公众号支付需要获取用户的openid
- 通过微信OAuth2授权流程获取(需在微信公众号后台配置授权域名)
调用统一下单API
- 使用
WxPayUnifiedOrderRequest构建请求 - 关键参数:订单号、金额(分)、openid、回调地址
- 返回 prepay_id(预支付交易会话标识)
- 使用
生成JSAPI调起支付参数
- 包含 appId、timeStamp、nonceStr、package、signType
- 使用商户密钥生成签名(paySign)
前端调起支付
- 使用微信JSAPI的 chooseWXPay 方法
- 传入支付参数调起支付界面
接收异步通知
- 必须验证签名(防止伪造请求)
- 检查 result_code 是否为 SUCCESS
- 更新订单状态(注意处理幂等性)
安全注意事项
- 支付金额需与订单金额比对(防止金额篡改)
- 敏感操作记录日志
- 异步通知处理需要保证幂等性
微信支付与支付宝支付的区别
| 特性 | 微信支付 | 支付宝支付 |
|---|---|---|
| 支付方式 | JSAPI、Native、App、H5等 | 电脑网站、手机网站、App等 |
| 金额单位 | 分 | 元 |
| 签名算法 | HMAC-SHA256/RSA | RSA/RSA2 |
| 通知格式 | XML | Form表单/JSON |
| 证书要求 | 需要API证书 | 不需要证书 |
| OpenID | 需要获取用户openid | 不需要用户标识 |
| 支付流程 | 需要前端调起支付 | 自动跳转支付页面 |
常见问题解决方案
支付金额单位错误
- 微信支付单位为分,支付宝单位为元
- 转换公式:微信金额 = 支付宝金额 × 100
签名验证失败
- 检查商户密钥是否正确
- 确认签名算法一致(微信默认HMAC-SHA256)
- 验证参数是否完整且顺序正确
异步通知处理
- 必须返回XML格式的响应
- 处理速度要快(微信会在30秒内重试)
- 保证幂等性(防止重复处理)
跨域问题
- 前端调起支付需要在微信内置浏览器
- 确保公众号JS接口安全域名配置正确
证书管理
- 定期更新API证书
- 证书文件妥善保管(不要提交到代码仓库)
实际开发中需要根据具体业务需求进行调整,并注意支付安全相关事项。
到此这篇关于SpringBoot+MyBatis集成微信支付实现示例的文章就介绍到这了,更多相关SpringBoot MyBatis微信支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
