SpringBoot+MyBatis集成微信支付实现示例

 更新时间:2025年06月06日 09:30:23   作者: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/RSARSA/RSA2
通知格式XMLForm表单/JSON
证书要求需要API证书不需要证书
OpenID需要获取用户openid不需要用户标识
支付流程需要前端调起支付自动跳转支付页面

常见问题解决方案

  • 支付金额单位错误

    • 微信支付单位为分,支付宝单位为元
    • 转换公式:微信金额 = 支付宝金额 × 100
  • 签名验证失败

    • 检查商户密钥是否正确
    • 确认签名算法一致(微信默认HMAC-SHA256)
    • 验证参数是否完整且顺序正确
  • 异步通知处理

    • 必须返回XML格式的响应
    • 处理速度要快(微信会在30秒内重试)
    • 保证幂等性(防止重复处理)
  • 跨域问题

    • 前端调起支付需要在微信内置浏览器
    • 确保公众号JS接口安全域名配置正确
  • 证书管理

    • 定期更新API证书
    • 证书文件妥善保管(不要提交到代码仓库)

实际开发中需要根据具体业务需求进行调整,并注意支付安全相关事项。

到此这篇关于SpringBoot+MyBatis集成微信支付实现示例的文章就介绍到这了,更多相关SpringBoot MyBatis微信支付内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 教你用JDK编译Java文件的方法

    教你用JDK编译Java文件的方法

    这篇文章主要介绍了教你用JDK编译Java文件的方法,分步骤给大家介绍了设置环境变量的方法,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友可以参考下
    2022-01-01
  • mybatis-plus数据权限实现代码

    mybatis-plus数据权限实现代码

    这篇文章主要介绍了mybatis-plus数据权限实现,结合了mybatis-plus的插件方式,做出了自己的注解方式的数据权限,虽然可能存在一部分的局限性,但很好的解决了我们自己去解析SQL的功能,需要的朋友可以参考下
    2023-06-06
  • IDEA 2020 无法启动的解决办法(启动崩盘)附IDEA 2020 新功能

    IDEA 2020 无法启动的解决办法(启动崩盘)附IDEA 2020 新功能

    这篇文章主要介绍了IDEA 2020 无法启动的解决办法(启动崩盘)附IDEA 2020 新功能,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • WeakHashMap的使用方法详解

    WeakHashMap的使用方法详解

    这篇文章主要介绍了WeakHashMap的使用方法详解的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • 比较常用UML类图几种关系的小结

    比较常用UML类图几种关系的小结

    本文给大家总结了UML类图中几种比较常用的关系小总结,需要的朋友可以参考下
    2015-10-10
  • 深入浅出分析Java 类和对象

    深入浅出分析Java 类和对象

    面向对象乃是Java语言的核心,是程序设计的思想。Java语言的面向对象技术包括了面向对象和面向过程的基本概念,面向对象的特征,Java语言的类,对象,修饰符,抽象类等一系列的知识点
    2022-03-03
  • Java自带消息队列Queue的使用教程详细讲解

    Java自带消息队列Queue的使用教程详细讲解

    这篇文章主要介绍了Java自带消息队列Queue的使用教程,Java中的queue类是队列数据结构管理类,在它里边的元素可以按照添加它们的相同顺序被移除,队列通常以FIFO的方式排序各个元素,感兴趣想要详细了解可以参考下文
    2023-05-05
  • Jmeter连接Mysql数据库实现过程详解

    Jmeter连接Mysql数据库实现过程详解

    这篇文章主要介绍了Jmeter连接Mysql数据库实现过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Idea如何使用System.getenv()获取环境变量的值

    Idea如何使用System.getenv()获取环境变量的值

    文章介绍了如何在Java中使用`System.getenv()`方法读取环境变量的值,并提供了两种配置环境变量的方法:启动项配置和系统环境变量配置,对于系统环境变量,文章特别指出需要重启电脑或程序才能使其生效
    2024-11-11
  • IntelliJ IDEA 常用设置(配置)吐血整理(首次安装必需)

    IntelliJ IDEA 常用设置(配置)吐血整理(首次安装必需)

    这篇文章主要介绍了IntelliJ IDEA 常用设置(配置)吐血整理(首次安装必需),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06

最新评论