下面我将详细介绍使用 Spring Boot + MyBatis 实现微信支付(JSAPI支付)的完整流程和代码示例。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.github.wechatpay-apiv3groupId>
<artifactId>wechatpay-apache-httpclientartifactId>
<version>0.4.7version>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformatgroupId>
<artifactId>jackson-dataformat-xmlartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
@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);
}
}
// 订单实体
@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);
}
@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 " ";
}
} catch (Exception e) {
// 记录日志
}
return " ";
}
}
@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 ? "已支付" : "未支付";
}
}
<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>
# 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
调用统一下单API
WxPayUnifiedOrderRequest
构建请求生成JSAPI调起支付参数
前端调起支付
接收异步通知
安全注意事项
特性 | 微信支付 | 支付宝支付 |
---|---|---|
支付方式 | JSAPI、Native、App、H5等 | 电脑网站、手机网站、App等 |
金额单位 | 分 | 元 |
签名算法 | HMAC-SHA256/RSA | RSA/RSA2 |
通知格式 | XML | Form表单/JSON |
证书要求 | 需要API证书 | 不需要证书 |
OpenID | 需要获取用户openid | 不需要用户标识 |
支付流程 | 需要前端调起支付 | 自动跳转支付页面 |
支付金额单位错误
签名验证失败
异步通知处理
跨域问题
证书管理
以上是使用 Spring Boot + MyBatis 实现微信支付的完整流程和代码示例。实际开发中需要根据具体业务需求进行调整,并注意支付安全相关事项。