Spring Boot中对接微信支付的详细步骤

在Spring Boot中对接微信支付的详细步骤如下:


一、前期准备

  1. 注册微信支付商户账号

    • 前往微信支付商户平台注册并完成实名认证。

    • 获取以下关键信息:

      • appid:公众号或小程序的AppID。
      • mch_id:微信支付商户号。
      • api_key:商户平台设置的API密钥(用于签名)。
      • notify_url:支付结果回调地址(需公网可访问)。
  2. 配置开发环境

    • 确保项目使用Java 8+和Spring Boot 2.x。
    • 配置域名和HTTPS(微信支付要求回调地址必须为HTTPS)。

二、添加依赖

pom.xml中添加必要依赖:

xml 复制代码
<!-- HTTP客户端 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

<!-- XML处理 -->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

<!-- 其他Spring Boot基础依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

三、配置参数

application.yml中配置微信支付参数:

vbnet 复制代码
wxpay:
  appid: your_appid
  mch-id: your_mch_id
  api-key: your_api_key
  notify-url: https://your-domain.com/pay/notify

通过@ConfigurationProperties读取配置:

less 复制代码
@Configuration
@ConfigurationProperties(prefix = "wxpay")
@Data
public class WxPayConfig {
    private String appid;
    private String mchId;
    private String apiKey;
    private String notifyUrl;
}

四、实现工具类

  1. 签名工具
vbnet 复制代码
public class WxPayUtil {
    public static String generateSign(Map<String, String> data, String apiKey) {
        // 按参数名ASCII字典序排序
        List<String> keyList = new ArrayList<>(data.keySet());
        Collections.sort(keyList);

        StringBuilder sb = new StringBuilder();
        for (String key : keyList) {
            if (!key.equals("sign") && data.get(key) != null && !data.get(key).isEmpty()) {
                sb.append(key).append("=").append(data.get(key)).append("&");
            }
        }
        sb.append("key=").append(apiKey);
        // MD5签名(或使用HMAC-SHA256)
        return DigestUtils.md5Hex(sb.toString()).toUpperCase();
    }
}
  1. HTTP请求工具
java 复制代码
public class WxPayHttpClient {
    public static String post(String url, String xmlData) throws IOException {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(url);
        post.setEntity(new StringEntity(xmlData, "UTF-8"));
        post.setHeader("Content-Type", "application/xml");

        CloseableHttpResponse response = client.execute(post);
        return EntityUtils.toString(response.getEntity(), "UTF-8");
    }
}

五、统一下单接口

  1. Controller入口
less 复制代码
@RestController
@RequestMapping("/pay")
public class WxPayController {
    @Autowired
    private WxPayConfig wxPayConfig;

    @PostMapping("/create")
    public String createOrder(@RequestBody OrderRequest orderRequest) throws Exception {
        Map<String, String> data = new HashMap<>();
        data.put("appid", wxPayConfig.getAppid());
        data.put("mch_id", wxPayConfig.getMchId());
        data.put("nonce_str", UUID.randomUUID().toString().replace("-", ""));
        data.put("body", orderRequest.getBody());
        data.put("out_trade_no", orderRequest.getOrderNo());
        data.put("total_fee", String.valueOf(orderRequest.getTotalFee())); // 单位:分
        data.put("spbill_create_ip", "123.12.12.123");
        data.put("notify_url", wxPayConfig.getNotifyUrl());
        data.put("trade_type", "NATIVE"); // JSAPI、APP等

        // 生成签名
        String sign = WxPayUtil.generateSign(data, wxPayConfig.getApiKey());
        data.put("sign", sign);

        // 转换为XML
        String xmlData = mapToXml(data);

        // 调用微信统一下单接口
        String response = WxPayHttpClient.post("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlData);

        // 解析返回的XML,获取code_url或prepay_id
        Map<String, String> respData = parseXml(response);
        return respData.get("code_url");
    }
}

六、处理支付回调

  1. 回调接口实现
xml 复制代码
@PostMapping("/notify")
public String payNotify(HttpServletRequest request) throws Exception {
    // 读取回调数据
    String xmlData = IOUtils.toString(request.getInputStream(), "UTF-8");
    Map<String, String> notifyData = parseXml(xmlData);

    // 验证签名
    String sign = notifyData.get("sign");
    String localSign = WxPayUtil.generateSign(notifyData, wxPayConfig.getApiKey());
    if (!sign.equals(localSign)) {
        return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
    }

    // 处理业务逻辑(如更新订单状态)
    String orderNo = notifyData.get("out_trade_no");
    orderService.updateOrderPaid(orderNo);

    // 返回成功响应
    return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}

七、其他功能

  1. 订单查询

    • 调用https://api.mch.weixin.qq.com/pay/orderquery接口,传递订单号查询状态。
  2. 退款

    • 调用https://api.mch.weixin.qq.com/secapi/pay/refund接口,需使用商户证书(.p12文件)。

八、注意事项

  1. 金额单位:微信支付以分为单位(如100表示1元)。
  2. 超时处理:设置合理的超时时间和重试机制。
  3. 幂等性:处理回调时确保重复通知不会导致重复业务操作。
  4. 日志记录:记录关键步骤日志,便于排查问题。
  5. 沙箱测试 :使用微信支付沙箱环境进行测试。

通过以上步骤,即可在Spring Boot项目中完成微信支付的对接。具体实现需参考微信支付官方文档调整参数和接口调用方式。

相关推荐
一枚小小程序员哈1 小时前
基于asp.net 的在线餐饮订餐系统的设计与实现/基于c#的网上订餐系统/餐厅管理系统
后端·c#·asp.net
你的人类朋友6 小时前
【Node&Vue】什么是ECMAScript?
前端·javascript·后端
你的人类朋友7 小时前
说说你对go的认识
后端·云原生·go
我崽不熬夜8 小时前
Java中基本的输入输出(I/O)操作:你知道如何处理文件吗?
java·后端·java ee
我崽不熬夜8 小时前
Java的异常处理机制:如何优雅地捕获和抛出异常?
java·后端·java ee
Ice__Cai8 小时前
Flask 之 Cookie & Session 详解:用户状态管理
后端·python·flask·cookie·session
我崽不熬夜9 小时前
掌握Java中的数组与集合:如何灵活处理不同的数据结构?
java·后端·java ee
jiunian_cn9 小时前
【Linux】线程
android·linux·运维·c语言·c++·后端
coding随想9 小时前
前端常见焦点事件(Focus)解析
后端
野生技术架构师10 小时前
Spring Boot 定时任务与 xxl-job 灵活切换方案
java·spring boot·后端